]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/sql/Pg/1.4.0.5-1.6.0.0-upgrade-db.sql
Qstore: support LIMIT and OFFSET clauses.
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 1.4.0.5-1.6.0.0-upgrade-db.sql
1 /*
2  * Copyright (C) 2009  Equinox Software, Inc.
3  * Mike Rylander <miker@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17
18
19 DROP SCHEMA IF EXISTS acq CASCADE;
20 DROP SCHEMA IF EXISTS serial CASCADE;
21
22 BEGIN;
23
24 INSERT INTO config.upgrade_log (version) VALUES ('1.6.0.0');
25  
26 CREATE TABLE config.standing_penalty (
27         id              SERIAL  PRIMARY KEY,
28         name            TEXT    NOT NULL UNIQUE,
29         label           TEXT    NOT NULL,
30         block_list      TEXT
31 );
32 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (1,'PATRON_EXCEEDS_FINES','Patron exceeds fine threshold','CIRC|HOLD|RENEW');
33 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (2,'PATRON_EXCEEDS_OVERDUE_COUNT','Patron exceeds max overdue item threshold','CIRC|HOLD|RENEW');
34 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (3,'PATRON_EXCEEDS_CHECKOUT_COUNT','Patron exceeds max checked out item threshold','CIRC');
35 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (4,'PATRON_EXCEEDS_COLLECTIONS_WARNING','Patron exceeds pre-collections warning fine threshold','CIRC|HOLD|RENEW');
36
37 INSERT INTO config.standing_penalty (id,name,label) VALUES (20,'ALERT_NOTE','Alerting Note, no blocks');
38 INSERT INTO config.standing_penalty (id,name,label) VALUES (21,'SILENT_NOTE','Note, no blocks');
39 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (22,'STAFF_C','Alerting block on Circ','CIRC');
40 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (23,'STAFF_CH','Alerting block on Circ and Hold','CIRC|HOLD');
41 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (24,'STAFF_CR','Alerting block on Circ and Renew','CIRC|RENEW');
42 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (25,'STAFF_CHR','Alerting block on Circ, Hold and Renew','CIRC|HOLD|RENEW');
43 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (26,'STAFF_HR','Alerting block on Hold and Renew','HOLD|RENEW');
44 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (27,'STAFF_H','Alerting block on Hold','HOLD');
45 INSERT INTO config.standing_penalty (id,name,label,block_list) VALUES (28,'STAFF_R','Alerting block on Renew','RENEW');
46
47 SELECT SETVAL('config.standing_penalty_id_seq', 100);
48
49 CREATE TABLE config.billing_type (
50     id              SERIAL  PRIMARY KEY,
51     name            TEXT    NOT NULL,
52     owner           INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
53     default_price   NUMERIC(6,2),
54     CONSTRAINT billing_type_once_per_lib UNIQUE (name, owner)
55 );
56
57 INSERT INTO config.billing_type (id, name, owner) VALUES ( 1, 'Overdue Materials', 1);
58 INSERT INTO config.billing_type (id, name, owner) VALUES ( 2, 'Long Overdue Collection Fee', 1);
59 INSERT INTO config.billing_type (id, name, owner) VALUES ( 3, 'Lost Materials', 1);
60 INSERT INTO config.billing_type (id, name, owner) VALUES ( 4, 'Lost Materials Processing Fee', 1);
61 INSERT INTO config.billing_type (id, name, owner) VALUES ( 5, 'System: Deposit', 1);
62 INSERT INTO config.billing_type (id, name, owner) VALUES ( 6, 'System: Rental', 1);
63 INSERT INTO config.billing_type (id, name, owner) VALUES ( 7, 'Damaged Item', 1);
64 INSERT INTO config.billing_type (id, name, owner) VALUES ( 8, 'Damaged Item Processing Fee', 1);
65 INSERT INTO config.billing_type (id, name, owner) VALUES ( 9, 'Notification Fee', 1);
66
67 SELECT SETVAL('config.billing_type_id_seq'::TEXT, 100);
68  
69 ALTER TABLE actor.usr ADD COLUMN alias TEXT;
70 ALTER TABLE actor.usr ADD COLUMN juvenile BOOL;
71 ALTER TABLE auditor.actor_usr_history ADD COLUMN alias TEXT;
72 ALTER TABLE auditor.actor_usr_history ADD COLUMN juvenile BOOL;
73
74 ALTER TABLE actor.usr ALTER COLUMN juvenile SET DEFAULT FALSE;
75 UPDATE actor.usr SET juvenile=FALSE;
76
77
78 DROP TABLE actor.usr_standing_penalty;
79 CREATE TABLE actor.usr_standing_penalty (
80         id                      SERIAL  PRIMARY KEY,
81         org_unit                INT     NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
82         usr                     INT     NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
83         standing_penalty        INT     NOT NULL REFERENCES config.standing_penalty (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
84         staff                   INT     REFERENCES actor.usr (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
85         set_date                TIMESTAMP WITH TIME ZONE        DEFAULT NOW(),
86         stop_date               TIMESTAMP WITH TIME ZONE,
87         note                    TEXT
88 );
89 CREATE INDEX actor_usr_standing_penalty_usr_idx ON actor.usr_standing_penalty (usr);
90
91
92 ALTER TABLE actor.usr_address ADD COLUMN pending BOOL;
93 ALTER TABLE actor.usr_address ADD COLUMN replaces INT;
94 ALTER TABLE auditor.actor_usr_address_history ADD COLUMN pending BOOL;
95 ALTER TABLE auditor.actor_usr_address_history ADD COLUMN replaces INT;
96
97 ALTER TABLE actor.usr_address ALTER COLUMN pending SET DEFAULT FALSE;
98 UPDATE actor.usr_address SET pending = FALSE;
99
100
101 CREATE TABLE permission.grp_penalty_threshold (
102     id          SERIAL          PRIMARY KEY,
103     grp         INT             NOT NULL REFERENCES permission.grp_tree (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
104     org_unit    INT             NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
105     penalty     INT             NOT NULL REFERENCES config.standing_penalty (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
106     threshold   NUMERIC(8,2)    NOT NULL,
107     CONSTRAINT penalty_grp_once UNIQUE (grp,penalty)
108 );
109
110 INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold) VALUES (1,1,1,10.0);
111 INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold) VALUES (1,1,2,10.0);
112 INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold) VALUES (1,1,3,10.0);
113 SELECT SETVAL('permission.grp_penalty_threshold_id_seq'::TEXT, (SELECT MAX(id) FROM permission.grp_penalty_threshold));
114
115
116
117 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_nd(
118         user_id    IN INTEGER,
119         perm_code  IN TEXT
120 )
121 RETURNS SETOF INTEGER AS $$
122 --
123 -- Return a set of all the org units for which a given user has a given
124 -- permission, granted directly (not through inheritance from a parent
125 -- org unit).
126 --
127 -- The permissions apply to a minimum depth of the org unit hierarchy,
128 -- for the org unit(s) to which the user is assigned.  (They also apply
129 -- to the subordinates of those org units, but we don't report the
130 -- subordinates here.)
131 --
132 -- For purposes of this function, the permission.usr_work_ou_map table
133 -- defines which users belong to which org units.  I.e. we ignore the
134 -- home_ou column of actor.usr.
135 --
136 -- The result set may contain duplicates, which should be eliminated
137 -- by a DISTINCT clause.
138 --
139 DECLARE
140         b_super       BOOLEAN;
141         n_perm        INTEGER;
142         n_min_depth   INTEGER; 
143         n_work_ou     INTEGER;
144         n_curr_ou     INTEGER;
145         n_depth       INTEGER;
146         n_curr_depth  INTEGER;
147 BEGIN
148         --
149         -- Check for superuser
150         --
151         SELECT INTO b_super
152                 super_user
153         FROM
154                 actor.usr
155         WHERE
156                 id = user_id;
157         --
158         IF NOT FOUND THEN
159                 return;                         -- No user?  No permissions.
160         ELSIF b_super THEN
161                 --
162                 -- Super user has all permissions everywhere
163                 --
164                 FOR n_work_ou IN
165                         SELECT
166                                 id
167                         FROM
168                                 actor.org_unit
169                         WHERE
170                                 parent_ou IS NULL
171                 LOOP
172                         RETURN NEXT n_work_ou; 
173                 END LOOP;
174                 RETURN;
175         END IF;
176         --
177         -- Translate the permission name
178         -- to a numeric permission id
179         --
180         SELECT INTO n_perm
181                 id
182         FROM
183                 permission.perm_list
184         WHERE
185                 code = perm_code;
186         --
187         IF NOT FOUND THEN
188                 RETURN;               -- No such permission
189         END IF;
190         --
191         -- Find the highest-level org unit (i.e. the minimum depth)
192         -- to which the permission is applied for this user
193         --
194         -- This query is modified from the one in permission.usr_perms().
195         --
196         SELECT INTO n_min_depth
197                 min( depth )
198         FROM    (
199                 SELECT depth 
200                   FROM permission.usr_perm_map upm
201                  WHERE upm.usr = user_id 
202                    AND upm.perm = n_perm
203                                 UNION
204                 SELECT  gpm.depth
205                   FROM  permission.grp_perm_map gpm
206                   WHERE gpm.perm = n_perm 
207                 AND gpm.grp IN (
208                            SELECT       (permission.grp_ancestors(
209                                         (SELECT profile FROM actor.usr WHERE id = user_id)
210                                 )).id
211                         )
212                                 UNION
213                 SELECT  p.depth
214                   FROM  permission.grp_perm_map p 
215                   WHERE p.perm = n_perm
216                     AND p.grp IN (
217                                 SELECT (permission.grp_ancestors(m.grp)).id 
218                                 FROM   permission.usr_grp_map m
219                                 WHERE  m.usr = user_id
220                         )
221         ) AS x;
222         --
223         IF NOT FOUND THEN
224                 RETURN;                -- No such permission for this user
225         END IF;
226         --
227         -- Identify the org units to which the user is assigned.  Note that
228         -- we pay no attention to the home_ou column in actor.usr.
229         --
230         FOR n_work_ou IN
231                 SELECT
232                         work_ou
233                 FROM
234                         permission.usr_work_ou_map
235                 WHERE
236                         usr = user_id
237         LOOP            -- For each org unit to which the user is assigned
238                 --
239                 -- Determine the level of the org unit by a lookup in actor.org_unit_type.
240                 -- We take it on faith that this depth agrees with the actual hierarchy
241                 -- defined in actor.org_unit.
242                 --
243                 SELECT INTO n_depth
244                     type.depth
245                 FROM
246                     actor.org_unit_type type
247                         INNER JOIN actor.org_unit ou
248                             ON ( ou.ou_type = type.id )
249                 WHERE
250                     ou.id = n_work_ou;
251                 --
252                 IF NOT FOUND THEN
253                         CONTINUE;        -- Maybe raise exception?
254                 END IF;
255                 --
256                 -- Compare the depth of the work org unit to the
257                 -- minimum depth, and branch accordingly
258                 --
259                 IF n_depth = n_min_depth THEN
260                         --
261                         -- The org unit is at the right depth, so return it.
262                         --
263                         RETURN NEXT n_work_ou;
264                 ELSIF n_depth > n_min_depth THEN
265                         --
266                         -- Traverse the org unit tree toward the root,
267                         -- until you reach the minimum depth determined above
268                         --
269                         n_curr_depth := n_depth;
270                         n_curr_ou := n_work_ou;
271                         WHILE n_curr_depth > n_min_depth LOOP
272                                 SELECT INTO n_curr_ou
273                                         parent_ou
274                                 FROM
275                                         actor.org_unit
276                                 WHERE
277                                         id = n_curr_ou;
278                                 --
279                                 IF FOUND THEN
280                                         n_curr_depth := n_curr_depth - 1;
281                                 ELSE
282                                         --
283                                         -- This can happen only if the hierarchy defined in
284                                         -- actor.org_unit is corrupted, or out of sync with
285                                         -- the depths defined in actor.org_unit_type.
286                                         -- Maybe we should raise an exception here, instead
287                                         -- of silently ignoring the problem.
288                                         --
289                                         n_curr_ou = NULL;
290                                         EXIT;
291                                 END IF;
292                         END LOOP;
293                         --
294                         IF n_curr_ou IS NOT NULL THEN
295                                 RETURN NEXT n_curr_ou;
296                         END IF;
297                 ELSE
298                         --
299                         -- The permission applies only at a depth greater than the work org unit.
300                         -- Use connectby() to find all dependent org units at the specified depth.
301                         --
302                         FOR n_curr_ou IN
303                                 SELECT ou::INTEGER
304                                 FROM connectby( 
305                                                 'actor.org_unit',         -- table name
306                                                 'id',                     -- key column
307                                                 'parent_ou',              -- recursive foreign key
308                                                 n_work_ou::TEXT,          -- id of starting point
309                                                 (n_min_depth - n_depth)   -- max depth to search, relative
310                                         )                             --   to starting point
311                                         AS t(
312                                                 ou text,            -- dependent org unit
313                                                 parent_ou text,     -- (ignore)
314                                                 level int           -- depth relative to starting point
315                                         )
316                                 WHERE
317                                         level = n_min_depth - n_depth
318                         LOOP
319                                 RETURN NEXT n_curr_ou;
320                         END LOOP;
321                 END IF;
322                 --
323         END LOOP;
324         --
325         RETURN;
326         --
327 END;
328 $$ LANGUAGE 'plpgsql';
329
330
331 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_all_nd(
332         user_id    IN INTEGER,
333         perm_code  IN TEXT
334 )
335 RETURNS SETOF INTEGER AS $$
336 --
337 -- Return a set of all the org units for which a given user has a given
338 -- permission, granted either directly or through inheritance from a parent
339 -- org unit.
340 --
341 -- The permissions apply to a minimum depth of the org unit hierarchy, and
342 -- to the subordinates of those org units, for the org unit(s) to which the
343 -- user is assigned.
344 --
345 -- For purposes of this function, the permission.usr_work_ou_map table
346 -- assigns users to org units.  I.e. we ignore the home_ou column of actor.usr.
347 --
348 -- The result set may contain duplicates, which should be eliminated
349 -- by a DISTINCT clause.
350 --
351 DECLARE
352         n_head_ou     INTEGER;
353         n_child_ou    INTEGER;
354 BEGIN
355         FOR n_head_ou IN
356                 SELECT DISTINCT * FROM permission.usr_has_perm_at_nd( user_id, perm_code )
357         LOOP
358                 --
359                 -- The permission applies only at a depth greater than the work org unit.
360                 -- Use connectby() to find all dependent org units at the specified depth.
361                 --
362                 FOR n_child_ou IN
363                         SELECT ou::INTEGER
364                         FROM connectby( 
365                                         'actor.org_unit',   -- table name
366                                         'id',               -- key column
367                                         'parent_ou',        -- recursive foreign key
368                                         n_head_ou::TEXT,    -- id of starting point
369                                         0                   -- no limit on search depth
370                                 )
371                                 AS t(
372                                         ou text,            -- dependent org unit
373                                         parent_ou text,     -- (ignore)
374                                         level int           -- (ignore)
375                                 )
376                 LOOP
377                         RETURN NEXT n_child_ou;
378                 END LOOP;
379         END LOOP;
380         --
381         RETURN;
382         --
383 END;
384 $$ LANGUAGE 'plpgsql';
385
386
387 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at(
388         user_id    IN INTEGER,
389         perm_code  IN TEXT
390 )
391 RETURNS SETOF INTEGER AS $$
392 SELECT DISTINCT * FROM permission.usr_has_perm_at_nd( $1, $2 );
393 $$ LANGUAGE 'sql';
394
395
396 CREATE OR REPLACE FUNCTION permission.usr_has_perm_at_all(
397         user_id    IN INTEGER,
398         perm_code  IN TEXT
399 )
400 RETURNS SETOF INTEGER AS $$
401 SELECT DISTINCT * FROM permission.usr_has_perm_at_all_nd( $1, $2 );
402 $$ LANGUAGE 'sql';
403
404
405
406 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
407 DECLARE
408
409     owning_lib      TEXT;
410     circ_lib        TEXT;
411     call_number     TEXT;
412     copy_number     TEXT;
413     status          TEXT;
414     location        TEXT;
415     circulate       TEXT;
416     deposit         TEXT;
417     deposit_amount  TEXT;
418     ref             TEXT;
419     holdable        TEXT;
420     price           TEXT;
421     barcode         TEXT;
422     circ_modifier   TEXT;
423     circ_as_type    TEXT;
424     alert_message   TEXT;
425     opac_visible    TEXT;
426     pub_note        TEXT;
427     priv_note       TEXT;
428
429     attr_def        RECORD;
430     tmp_attr_set    RECORD;
431     attr_set        vandelay.import_item%ROWTYPE;
432
433     xpath           TEXT;
434
435 BEGIN
436
437     SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
438
439     IF FOUND THEN
440
441         attr_set.definition := attr_def.id;
442
443         -- Build the combined XPath
444
445         owning_lib :=
446             CASE
447                 WHEN attr_def.owning_lib IS NULL THEN 'null()'
448                 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
449                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
450             END;
451
452         circ_lib :=
453             CASE
454                 WHEN attr_def.circ_lib IS NULL THEN 'null()'
455                 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
456                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
457             END;
458
459         call_number :=
460             CASE
461                 WHEN attr_def.call_number IS NULL THEN 'null()'
462                 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
463                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
464             END;
465
466         copy_number :=
467             CASE
468                 WHEN attr_def.copy_number IS NULL THEN 'null()'
469                 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
470                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
471             END;
472
473         status :=
474             CASE
475                 WHEN attr_def.status IS NULL THEN 'null()'
476                 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
477                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
478             END;
479
480         location :=
481             CASE
482                 WHEN attr_def.location IS NULL THEN 'null()'
483                 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
484                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
485             END;
486
487         circulate :=
488             CASE
489                 WHEN attr_def.circulate IS NULL THEN 'null()'
490                 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
491                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
492             END;
493
494         deposit :=
495             CASE
496                 WHEN attr_def.deposit IS NULL THEN 'null()'
497                 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
498                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
499             END;
500
501         deposit_amount :=
502             CASE
503                 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
504                 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
505                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
506             END;
507
508         ref :=
509             CASE
510                 WHEN attr_def.ref IS NULL THEN 'null()'
511                 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
512                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
513             END;
514
515         holdable :=
516             CASE
517                 WHEN attr_def.holdable IS NULL THEN 'null()'
518                 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
519                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
520             END;
521
522         price :=
523             CASE
524                 WHEN attr_def.price IS NULL THEN 'null()'
525                 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
526                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
527             END;
528
529         barcode :=
530             CASE
531                 WHEN attr_def.barcode IS NULL THEN 'null()'
532                 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
533                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
534             END;
535
536         circ_modifier :=
537             CASE
538                 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
539                 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
540                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
541             END;
542
543         circ_as_type :=
544             CASE
545                 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
546                 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
547                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
548             END;
549
550         alert_message :=
551             CASE
552                 WHEN attr_def.alert_message IS NULL THEN 'null()'
553                 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
554                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
555             END;
556
557         opac_visible :=
558             CASE
559                 WHEN attr_def.opac_visible IS NULL THEN 'null()'
560                 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
561                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
562             END;
563
564         pub_note :=
565             CASE
566                 WHEN attr_def.pub_note IS NULL THEN 'null()'
567                 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
568                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
569             END;
570         priv_note :=
571             CASE
572                 WHEN attr_def.priv_note IS NULL THEN 'null()'
573                 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
574                 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
575             END;
576
577
578         xpath :=
579             owning_lib      || '|' ||
580             circ_lib        || '|' ||
581             call_number     || '|' ||
582             copy_number     || '|' ||
583             status          || '|' ||
584             location        || '|' ||
585             circulate       || '|' ||
586             deposit         || '|' ||
587             deposit_amount  || '|' ||
588             ref             || '|' ||
589             holdable        || '|' ||
590             price           || '|' ||
591             barcode         || '|' ||
592             circ_modifier   || '|' ||
593             circ_as_type    || '|' ||
594             alert_message   || '|' ||
595             pub_note        || '|' ||
596             priv_note       || '|' ||
597             opac_visible;
598
599         -- RAISE NOTICE 'XPath: %', xpath;
600
601         FOR tmp_attr_set IN
602                 SELECT  *
603                   FROM  xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
604                             AS t( id BIGINT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
605                                   dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
606                                   circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
607         LOOP
608
609             tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
610             tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
611
612             tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
613             tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
614
615             SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
616             SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
617             SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
618
619             SELECT  id INTO attr_set.location
620               FROM  asset.copy_location
621               WHERE LOWER(name) = LOWER(tmp_attr_set.cl)
622                     AND asset.copy_location.owning_lib = COALESCE(attr_set.owning_lib, attr_set.circ_lib); -- INT
623
624             attr_set.circulate      :=
625                 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
626                 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
627
628             attr_set.deposit        :=
629                 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
630                 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
631
632             attr_set.holdable       :=
633                 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
634                 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
635
636             attr_set.opac_visible   :=
637                 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
638                 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
639
640             attr_set.ref            :=
641                 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
642                 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
643
644             attr_set.copy_number    := tmp_attr_set.cnum::INT; -- INT,
645             attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
646             attr_set.price          := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
647
648             attr_set.call_number    := tmp_attr_set.cn; -- TEXT
649             attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
650             attr_set.circ_modifier  := tmp_attr_set.circ_mod; -- TEXT,
651             attr_set.circ_as_type   := tmp_attr_set.circ_as; -- TEXT,
652             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
653             attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
654             attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
655             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
656
657             RETURN NEXT attr_set;
658
659         END LOOP;
660
661     END IF;
662
663 END;
664 $$ LANGUAGE PLPGSQL;
665
666 CREATE OR REPLACE FUNCTION actor.org_unit_ancestors ( INT ) RETURNS SETOF actor.org_unit AS $$
667         SELECT  a.*
668           FROM  connectby('actor.org_unit'::text,'parent_ou'::text,'id'::text,'name'::text,$1::text,100,'.'::text)
669                         AS t(keyid text, parent_keyid text, level int, branch text,pos int)
670                 JOIN actor.org_unit a ON a.id::text = t.keyid::text
671         JOIN actor.org_unit_type tp ON tp.id = a.ou_type
672         ORDER BY tp.depth, a.name;
673 $$ LANGUAGE SQL STABLE;
674
675 CREATE OR REPLACE FUNCTION actor.org_unit_ancestor_setting( setting_name TEXT, org_id INT ) RETURNS SETOF actor.org_unit_setting AS $$
676 DECLARE
677     setting RECORD;
678     cur_org INT;
679 BEGIN
680     cur_org := org_id;
681     LOOP
682         SELECT INTO setting * FROM actor.org_unit_setting WHERE org_unit = cur_org AND name = setting_name;
683         IF FOUND THEN
684             RETURN NEXT setting;
685         END IF;
686         SELECT INTO cur_org parent_ou FROM actor.org_unit WHERE id = cur_org;
687         EXIT WHEN cur_org IS NULL;
688     END LOOP;
689     RETURN;
690 END;
691 $$ LANGUAGE plpgsql;
692
693 COMMENT ON FUNCTION actor.org_unit_ancestor_setting( TEXT, INT) IS $$
694 /**
695 * Search "up" the org_unit tree until we find the first occurrence of an 
696 * org_unit_setting with the given name.
697 */
698 $$;
699  
700
701 ALTER TABLE asset.copy_tranparency_map RENAME TO copy_transparency_map;
702 ALTER TABLE asset.copy_transparency_map RENAME COLUMN tansparency TO transparency;
703
704 CREATE TABLE asset.uri (
705     id  SERIAL  PRIMARY KEY,
706     href    TEXT    NOT NULL,
707     label   TEXT,
708     use_restriction TEXT,
709     active  BOOL    NOT NULL DEFAULT TRUE
710 );
711
712 CREATE TABLE asset.uri_call_number_map (
713     id          BIGSERIAL   PRIMARY KEY,
714     uri         INT         NOT NULL REFERENCES asset.uri (id),
715     call_number INT         NOT NULL REFERENCES asset.call_number (id),
716     CONSTRAINT uri_cn_once UNIQUE (uri,call_number)
717 );
718 CREATE INDEX asset_uri_call_number_map_cn_idx ON asset.uri_call_number_map (call_number);
719
720 -----------------------------
721
722 CREATE TABLE container.copy_bucket_type (
723         code    TEXT    PRIMARY KEY,
724         label   TEXT    NOT NULL UNIQUE
725 );
726
727
728 CREATE TABLE container.copy_bucket_note (
729     id      SERIAL      PRIMARY KEY,
730     bucket  INT         NOT NULL REFERENCES container.copy_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
731     note    TEXT        NOT NULL
732 );
733
734 ALTER TABLE container.copy_bucket_item ADD COLUMN pos INT;
735 CREATE INDEX copy_bucket_item_bucket_idx ON container.copy_bucket_item (bucket);
736
737 CREATE TABLE container.copy_bucket_item_note (
738     id      SERIAL      PRIMARY KEY,
739     item    INT         NOT NULL REFERENCES container.copy_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
740     note    TEXT        NOT NULL
741 );
742  
743 ----------------------------- 
744  
745 CREATE TABLE container.call_number_bucket_type (
746         code    TEXT    PRIMARY KEY,
747         label   TEXT    NOT NULL UNIQUE
748 );
749  
750 ALTER TABLE container.call_number_bucket_item ADD COLUMN pos INT;
751 CREATE INDEX call_number_bucket_item_bucket_idx ON container.call_number_bucket_item (bucket);
752
753 CREATE TABLE container.call_number_bucket_note (
754     id      SERIAL      PRIMARY KEY,
755     bucket  INT         NOT NULL REFERENCES container.call_number_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
756     note    TEXT        NOT NULL
757 );
758
759
760 CREATE TABLE container.call_number_bucket_item_note (
761     id      SERIAL      PRIMARY KEY,
762     item    INT         NOT NULL REFERENCES container.call_number_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
763     note    TEXT        NOT NULL
764 );
765
766 ---------------------------
767  
768 CREATE TABLE container.biblio_record_entry_bucket_type (
769         code    TEXT    PRIMARY KEY,
770         label   TEXT    NOT NULL UNIQUE
771 );
772
773
774 CREATE TABLE container.biblio_record_entry_bucket_note (
775     id      SERIAL      PRIMARY KEY,
776     bucket  INT         NOT NULL REFERENCES container.biblio_record_entry_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
777     note    TEXT        NOT NULL
778 );
779
780 ALTER TABLE container.biblio_record_entry_bucket_item ADD COLUMN pos INT;
781 CREATE INDEX biblio_record_entry_bucket_item_bucket_idx ON container.biblio_record_entry_bucket_item (bucket);
782
783 CREATE TABLE container.biblio_record_entry_bucket_item_note (
784     id      SERIAL      PRIMARY KEY,
785     item    INT         NOT NULL REFERENCES container.biblio_record_entry_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
786     note    TEXT        NOT NULL
787 );
788
789 ---------------------------
790  
791 CREATE TABLE container.user_bucket_type (
792         code    TEXT    PRIMARY KEY,
793         label   TEXT    NOT NULL UNIQUE
794 );
795  
796 CREATE TABLE container.user_bucket_note (
797     id      SERIAL      PRIMARY KEY,
798     bucket  INT         NOT NULL REFERENCES container.user_bucket (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
799     note    TEXT        NOT NULL
800 );
801
802 ALTER TABLE container.user_bucket_item ADD COLUMN pos INT;
803 CREATE INDEX user_bucket_item_bucket_idx ON container.user_bucket_item (bucket);
804
805 CREATE TABLE container.user_bucket_item_note (
806     id      SERIAL      PRIMARY KEY,
807     item    INT         NOT NULL REFERENCES container.user_bucket_item (id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
808     note    TEXT        NOT NULL
809 );
810
811 -----------------------------
812
813 INSERT INTO config.billing_type (name,owner) SELECT DISTINCT billing_type, 1 FROM money.billing WHERE billing_type NOT IN (SELECT name FROM config.billing_type);
814 ALTER TABLE money.billing ADD COLUMN btype INT;
815
816 UPDATE money.billing SET btype = config.billing_type.id FROM config.billing_type WHERE config.billing_type.name = money.billing.billing_type;
817 ALTER TABLE money.billing ALTER COLUMN btype SET NOT NULL;
818 ALTER TABLE money.billing ADD CONSTRAINT btype_fkey FOREIGN KEY (btype) REFERENCES config.billing_type (id) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED;
819
820
821 CREATE TABLE money.materialized_billable_xact_summary AS
822         SELECT * FROM money.billable_xact_summary WHERE 1=0;
823
824 CREATE INDEX money_mat_summary_id_idx ON money.materialized_billable_xact_summary (id);
825 CREATE INDEX money_mat_summary_usr_idx ON money.materialized_billable_xact_summary (usr);
826 CREATE INDEX money_mat_summary_xact_start_idx ON money.materialized_billable_xact_summary (xact_start);
827
828 /* AFTER trigger only! */
829 CREATE OR REPLACE FUNCTION money.mat_summary_create () RETURNS TRIGGER AS $$
830 BEGIN
831         INSERT INTO money.materialized_billable_xact_summary (id, usr, xact_start, xact_finish, total_paid, total_owed, balance_owed, xact_type)
832                 VALUES ( NEW.id, NEW.usr, NEW.xact_start, NEW.xact_finish, 0.0, 0.0, 0.0, TG_ARGV[0]);
833         RETURN NEW;
834 END;
835 $$ LANGUAGE PLPGSQL;
836
837 /* BEFORE or AFTER trigger only! */
838 CREATE OR REPLACE FUNCTION money.mat_summary_update () RETURNS TRIGGER AS $$
839 BEGIN
840         UPDATE  money.materialized_billable_xact_summary
841           SET   usr = NEW.usr,
842                 xact_start = NEW.xact_start,
843                 xact_finish = NEW.xact_finish
844           WHERE id = NEW.id;
845         RETURN NEW;
846 END;
847 $$ LANGUAGE PLPGSQL;
848
849 /* AFTER trigger only! */
850 CREATE OR REPLACE FUNCTION money.mat_summary_delete () RETURNS TRIGGER AS $$
851 BEGIN
852         DELETE FROM money.materialized_billable_xact_summary WHERE id = OLD.id;
853         RETURN OLD;
854 END;
855 $$ LANGUAGE PLPGSQL;
856
857 CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON money.grocery FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_create ('grocery');
858 CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON money.grocery FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_update ();
859 CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON money.grocery FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_delete ();
860
861
862
863 /* BEFORE or AFTER trigger */
864 CREATE OR REPLACE FUNCTION money.materialized_summary_billing_add () RETURNS TRIGGER AS $$
865 BEGIN
866         IF NOT NEW.voided THEN
867                 UPDATE  money.materialized_billable_xact_summary
868                   SET   total_owed = total_owed + NEW.amount,
869                         last_billing_ts = NEW.billing_ts,
870                         last_billing_note = NEW.note,
871                         last_billing_type = NEW.billing_type,
872                         balance_owed = balance_owed + NEW.amount
873                   WHERE id = NEW.xact;
874         END IF;
875
876         RETURN NEW;
877 END;
878 $$ LANGUAGE PLPGSQL;
879
880 /* AFTER trigger only! */
881 CREATE OR REPLACE FUNCTION money.materialized_summary_billing_update () RETURNS TRIGGER AS $$
882 DECLARE
883         old_billing     money.billing%ROWTYPE;
884         old_voided      money.billing%ROWTYPE;
885 BEGIN
886
887         SELECT * INTO old_billing FROM money.billing WHERE xact = NEW.xact AND NOT voided ORDER BY billing_ts DESC LIMIT 1;
888         SELECT * INTO old_voided FROM money.billing WHERE xact = NEW.xact ORDER BY billing_ts DESC LIMIT 1;
889
890         IF NEW.voided AND NOT OLD.voided THEN
891                 IF OLD.id = old_voided.id THEN
892                         UPDATE  money.materialized_billable_xact_summary
893                           SET   last_billing_ts = old_billing.billing_ts,
894                                 last_billing_note = old_billing.note,
895                                 last_billing_type = old_billing.billing_type
896                           WHERE id = OLD.xact;
897                 END IF;
898
899                 UPDATE  money.materialized_billable_xact_summary
900                   SET   total_owed = total_owed - NEW.amount,
901                         balance_owed = balance_owed - NEW.amount
902                   WHERE id = NEW.xact;
903
904         ELSIF NOT NEW.voided AND OLD.voided THEN
905
906                 IF OLD.id = old_billing.id THEN
907                         UPDATE  money.materialized_billable_xact_summary
908                           SET   last_billing_ts = old_billing.billing_ts,
909                                 last_billing_note = old_billing.note,
910                                 last_billing_type = old_billing.billing_type
911                           WHERE id = OLD.xact;
912                 END IF;
913
914                 UPDATE  money.materialized_billable_xact_summary
915                   SET   total_owed = total_owed + NEW.amount,
916                         balance_owed = balance_owed + NEW.amount
917                   WHERE id = NEW.xact;
918
919         ELSE
920                 UPDATE  money.materialized_billable_xact_summary
921                   SET   total_owed = total_owed - (OLD.amount - NEW.amount),
922                         balance_owed = balance_owed - (OLD.amount - NEW.amount)
923                   WHERE id = NEW.xact;
924         END IF;
925
926         RETURN NEW;
927 END;
928 $$ LANGUAGE PLPGSQL;
929
930 /* BEFORE trigger only! */
931 CREATE OR REPLACE FUNCTION money.materialized_summary_billing_del () RETURNS TRIGGER AS $$
932 DECLARE
933         prev_billing    money.billing%ROWTYPE;
934         old_billing     money.billing%ROWTYPE;
935 BEGIN
936         SELECT * INTO prev_billing FROM money.billing WHERE xact = OLD.xact AND NOT voided ORDER BY billing_ts DESC LIMIT 1 OFFSET 1;
937         SELECT * INTO old_billing FROM money.billing WHERE xact = OLD.xact AND NOT voided ORDER BY billing_ts DESC LIMIT 1;
938
939         IF OLD.id = old_billing.id THEN
940                 UPDATE  money.materialized_billable_xact_summary
941                   SET   last_billing_ts = prev_billing.billing_ts,
942                         last_billing_note = prev_billing.note,
943                         last_billing_type = prev_billing.billing_type
944                   WHERE id = NEW.xact;
945         END IF;
946
947         IF NOT OLD.voided THEN
948                 UPDATE  money.materialized_billable_xact_summary
949                   SET   total_owed = total_owed - OLD.amount,
950                         balance_owed = balance_owed + OLD.amount
951                   WHERE id = OLD.xact;
952         END IF;
953
954         RETURN OLD;
955 END;
956 $$ LANGUAGE PLPGSQL;
957
958 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.billing FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_billing_add ();
959 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.billing FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_billing_update ();
960 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.billing FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_billing_del ();
961
962
963 /* BEFORE or AFTER trigger */
964 CREATE OR REPLACE FUNCTION money.materialized_summary_payment_add () RETURNS TRIGGER AS $$
965 BEGIN
966         IF NOT NEW.voided THEN
967                 UPDATE  money.materialized_billable_xact_summary
968                   SET   total_paid = total_paid + NEW.amount,
969                         last_payment_ts = NEW.payment_ts,
970                         last_payment_note = NEW.note,
971                         last_payment_type = TG_ARGV[0],
972                         balance_owed = balance_owed - NEW.amount
973                   WHERE id = NEW.xact;
974         END IF;
975
976         RETURN NEW;
977 END;
978 $$ LANGUAGE PLPGSQL;
979
980 /* AFTER trigger only! */
981 CREATE OR REPLACE FUNCTION money.materialized_summary_payment_update () RETURNS TRIGGER AS $$
982 DECLARE
983         old_payment     money.payment_view%ROWTYPE;
984         old_voided      money.payment_view%ROWTYPE;
985 BEGIN
986
987         SELECT * INTO old_payment FROM money.payment_view WHERE xact = NEW.xact AND NOT voided ORDER BY payment_ts DESC LIMIT 1;
988         SELECT * INTO old_voided FROM money.payment_view WHERE xact = NEW.xact ORDER BY payment_ts DESC LIMIT 1;
989
990         IF NEW.voided AND NOT OLD.voided THEN
991                 IF OLD.id = old_voided.id THEN
992                         UPDATE  money.materialized_billable_xact_summary
993                           SET   last_payment_ts = old_payment.payment_ts,
994                                 last_payment_note = old_payment.note,
995                                 last_payment_type = old_payment.payment_type
996                           WHERE id = OLD.xact;
997                 END IF;
998
999                 UPDATE  money.materialized_billable_xact_summary
1000                   SET   total_paid = total_paid - NEW.amount,
1001                         balance_owed = balance_owed + NEW.amount
1002                   WHERE id = NEW.xact;
1003
1004         ELSIF NOT NEW.voided AND OLD.voided THEN
1005
1006                 IF OLD.id = old_payment.id THEN
1007                         UPDATE  money.materialized_billable_xact_summary
1008                           SET   last_payment_ts = old_payment.payment_ts,
1009                                 last_payment_note = old_payment.note,
1010                                 last_payment_type = old_payment.payment_type
1011                           WHERE id = OLD.xact;
1012                 END IF;
1013
1014                 UPDATE  money.materialized_billable_xact_summary
1015                   SET   total_paid = total_paid + NEW.amount,
1016                         balance_owed = balance_owed - NEW.amount
1017                   WHERE id = NEW.xact;
1018
1019         ELSE
1020                 UPDATE  money.materialized_billable_xact_summary
1021                   SET   total_paid = total_paid - (OLD.amount - NEW.amount),
1022                         balance_owed = balance_owed + (OLD.amount - NEW.amount)
1023                   WHERE id = NEW.xact;
1024         END IF;
1025
1026         RETURN NEW;
1027 END;
1028 $$ LANGUAGE PLPGSQL;
1029
1030 /* BEFORE trigger only! */
1031 CREATE OR REPLACE FUNCTION money.materialized_summary_payment_del () RETURNS TRIGGER AS $$
1032 DECLARE
1033         prev_payment    money.payment_view%ROWTYPE;
1034         old_payment     money.payment_view%ROWTYPE;
1035 BEGIN
1036         SELECT * INTO prev_payment FROM money.payment_view WHERE xact = OLD.xact AND NOT voided ORDER BY payment_ts DESC LIMIT 1 OFFSET 1;
1037         SELECT * INTO old_payment FROM money.payment_view WHERE xact = OLD.xact AND NOT voided ORDER BY payment_ts DESC LIMIT 1;
1038
1039         IF OLD.id = old_payment.id THEN
1040                 UPDATE  money.materialized_billable_xact_summary
1041                   SET   last_payment_ts = prev_payment.payment_ts,
1042                         last_payment_note = prev_payment.note,
1043                         last_payment_type = prev_payment.payment_type
1044                   WHERE id = OLD.xact;
1045         END IF;
1046
1047         IF NOT OLD.voided THEN
1048                 UPDATE  money.materialized_billable_xact_summary
1049                   SET   total_paid = total_paid - OLD.amount,
1050                         balance_owed = balance_owed + OLD.amount
1051                   WHERE id = OLD.xact;
1052         END IF;
1053
1054         RETURN OLD;
1055 END;
1056 $$ LANGUAGE PLPGSQL;
1057
1058 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('payment');
1059 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('payment');
1060 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('payment');
1061
1062 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.bnm_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('bnm_payment');
1063 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.bnm_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('bnm_payment');
1064 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.bnm_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('bnm_payment');
1065
1066 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.forgive_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('forgive_payment');
1067 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.forgive_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('forgive_payment');
1068 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.forgive_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('forgive_payment');
1069
1070 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.work_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('work_payment');
1071 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.work_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('work_payment');
1072 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.work_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('work_payment');
1073
1074 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.credit_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('credit_payment');
1075 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.credit_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('credit_payment');
1076 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.credit_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('credit_payment');
1077
1078 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.goods_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('goods_payment');
1079 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.goods_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('goods_payment');
1080 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.goods_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('goods_payment');
1081
1082 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.bnm_desk_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('bnm_desk_payment');
1083 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.bnm_desk_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('bnm_desk_payment');
1084 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.bnm_desk_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('bnm_desk_payment');
1085
1086 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.cash_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('bnm_payment');
1087 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.cash_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('bnm_payment');
1088 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.cash_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('bnm_payment');
1089
1090 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.check_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('bnm_payment');
1091 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.check_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('bnm_payment');
1092 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.check_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('bnm_payment');
1093
1094 CREATE TRIGGER mat_summary_add_tgr AFTER INSERT ON money.credit_card_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_add ('credit_card_payment');
1095 CREATE TRIGGER mat_summary_upd_tgr AFTER UPDATE ON money.credit_card_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_update ('credit_card_payment');
1096 CREATE TRIGGER mat_summary_del_tgr BEFORE DELETE ON money.credit_card_payment FOR EACH ROW EXECUTE PROCEDURE money.materialized_summary_payment_del ('credit_card_payment');
1097
1098
1099 CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_create ('circulation');
1100 CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_update ();
1101 CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_delete ();
1102
1103 CREATE OR REPLACE VIEW action.billable_circulations AS
1104         SELECT  *
1105           FROM  action.circulation
1106           WHERE xact_finish IS NULL;
1107
1108 CREATE TABLE action.hold_request_cancel_cause (
1109     id      SERIAL  PRIMARY KEY,
1110     label   TEXT    UNIQUE
1111 );
1112 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (1,'Untargeted expiration');
1113 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (2,'Hold Shelf expiration');
1114 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (3,'Patron via phone');
1115 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (4,'Patron in person');
1116 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (5,'Staff forced');
1117 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (6,'Patron via OPAC');
1118 SELECT SETVAL('action.hold_request_cancel_cause_id_seq', 100);
1119  
1120 ALTER TABLE action.hold_request ADD COLUMN cancel_cause INT;
1121 ALTER TABLE action.hold_request ADD COLUMN cancel_note TEXT;
1122
1123
1124 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN juvenile_flag BOOL;
1125 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN circulate BOOL NOT NULL DEFAULT TRUE;
1126 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN duration_rule INT REFERENCES config.rule_circ_duration (id) DEFERRABLE INITIALLY DEFERRED;
1127 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN recurring_fine_rule INT REFERENCES config.rule_recurring_fine (id) DEFERRABLE INITIALLY DEFERRED;
1128 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN max_fine_rule INT REFERENCES config.rule_max_fine (id) DEFERRABLE INITIALLY DEFERRED;
1129 ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN script_test TEXT;
1130
1131 ALTER TABLE config.circ_matrix_matchpoint DROP CONSTRAINT ep_once_per_grp_loc_mod_marc;
1132 ALTER TABLE config.circ_matrix_matchpoint
1133         ADD CONSTRAINT ep_once_per_grp_loc_mod_marc
1134                 UNIQUE (grp, org_unit, circ_modifier, marc_type, marc_form, marc_vr_format, ref_flag, juvenile_flag, usr_age_lower_bound, usr_age_upper_bound, is_renewal);
1135
1136 UPDATE  config.circ_matrix_matchpoint
1137   SET   duration_rule = config.circ_matrix_ruleset.duration_rule,
1138         recurring_fine_rule = config.circ_matrix_ruleset.recurring_fine_rule,
1139         max_fine_rule = config.circ_matrix_ruleset.max_fine_rule
1140   FROM  config.circ_matrix_ruleset
1141   WHERE config.circ_matrix_ruleset.matchpoint = config.circ_matrix_matchpoint.id;
1142
1143 DROP TABLE config.circ_matrix_ruleset;
1144
1145 ALTER TABLE config.circ_matrix_circ_mod_test DROP COLUMN circ_mod;
1146 CREATE TABLE config.circ_matrix_circ_mod_test_map (
1147     id      SERIAL  PRIMARY KEY,
1148     circ_mod_test   INT NOT NULL REFERENCES config.circ_matrix_circ_mod_test (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1149     circ_mod        TEXT    NOT NULL REFERENCES config.circ_modifier (code) ON DELETE CASCADE ON UPDATE CASCADE  DEFERRABLE INITIALLY DEFERRED,
1150     CONSTRAINT cm_once_per_test UNIQUE (circ_mod_test, circ_mod)
1151 );
1152  
1153 DROP FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL );
1154 CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS config.circ_matrix_matchpoint AS $func$
1155 DECLARE
1156     current_group    permission.grp_tree%ROWTYPE;
1157     user_object    actor.usr%ROWTYPE;
1158     item_object    asset.copy%ROWTYPE;
1159     rec_descriptor    metabib.rec_descriptor%ROWTYPE;
1160     current_mp    config.circ_matrix_matchpoint%ROWTYPE;
1161     matchpoint    config.circ_matrix_matchpoint%ROWTYPE;
1162 BEGIN
1163     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
1164     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
1165     SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r JOIN asset.call_number c USING (record) WHERE c.id = item_object.call_number;
1166     SELECT INTO current_group * FROM permission.grp_tree WHERE id = user_object.profile;
1167
1168     LOOP
1169         -- for each potential matchpoint for this ou and group ...
1170         FOR current_mp IN
1171             SELECT    m.*
1172               FROM    config.circ_matrix_matchpoint m
1173                 JOIN actor.org_unit_ancestors( context_ou ) d ON (m.org_unit = d.id)
1174                 LEFT JOIN actor.org_unit_proximity p ON (p.from_org = context_ou AND p.to_org = d.id)
1175               WHERE    m.grp = current_group.id AND m.active
1176               ORDER BY    CASE WHEN p.prox        IS NULL THEN 999 ELSE p.prox END,
1177                     CASE WHEN m.is_renewal = renewal        THEN 128 ELSE 0 END +
1178                     CASE WHEN m.juvenile_flag    IS NOT NULL THEN 64 ELSE 0 END +
1179                     CASE WHEN m.circ_modifier    IS NOT NULL THEN 32 ELSE 0 END +
1180                     CASE WHEN m.marc_type        IS NOT NULL THEN 16 ELSE 0 END +
1181                     CASE WHEN m.marc_form        IS NOT NULL THEN 8 ELSE 0 END +
1182                     CASE WHEN m.marc_vr_format    IS NOT NULL THEN 4 ELSE 0 END +
1183                     CASE WHEN m.ref_flag        IS NOT NULL THEN 2 ELSE 0 END +
1184                     CASE WHEN m.usr_age_lower_bound    IS NOT NULL THEN 0.5 ELSE 0 END +
1185                     CASE WHEN m.usr_age_upper_bound    IS NOT NULL THEN 0.5 ELSE 0 END DESC LOOP
1186
1187             IF current_mp.circ_modifier IS NOT NULL THEN
1188                 CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier;
1189             END IF;
1190
1191             IF current_mp.marc_type IS NOT NULL THEN
1192                 IF item_object.circ_as_type IS NOT NULL THEN
1193                     CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
1194                 ELSE
1195                     CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
1196                 END IF;
1197             END IF;
1198
1199             IF current_mp.marc_form IS NOT NULL THEN
1200                 CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
1201             END IF;
1202
1203             IF current_mp.marc_vr_format IS NOT NULL THEN
1204                 CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
1205             END IF;
1206
1207             IF current_mp.ref_flag IS NOT NULL THEN
1208                 CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
1209             END IF;
1210
1211             IF current_mp.juvenile_flag IS NOT NULL THEN
1212                 CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
1213             END IF;
1214
1215             IF current_mp.usr_age_lower_bound IS NOT NULL THEN
1216                 CONTINUE WHEN user_object.dob IS NULL OR current_mp.usr_age_lower_bound < age(user_object.dob);
1217             END IF;
1218
1219             IF current_mp.usr_age_upper_bound IS NOT NULL THEN
1220                 CONTINUE WHEN user_object.dob IS NULL OR current_mp.usr_age_upper_bound > age(user_object.dob);
1221             END IF;
1222
1223
1224             -- everything was undefined or matched
1225             matchpoint = current_mp;
1226
1227             EXIT WHEN matchpoint.id IS NOT NULL;
1228         END LOOP;
1229
1230         EXIT WHEN current_group.parent IS NULL OR matchpoint.id IS NOT NULL;
1231
1232         SELECT INTO current_group * FROM permission.grp_tree WHERE id = current_group.parent;
1233     END LOOP;
1234
1235     RETURN matchpoint;
1236 END;
1237 $func$ LANGUAGE plpgsql;
1238
1239 CREATE OR REPLACE FUNCTION action.item_user_circ_test( circ_ou INT, match_item BIGINT, match_user INT, renewal BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
1240 DECLARE
1241     user_object        actor.usr%ROWTYPE;
1242     standing_penalty    config.standing_penalty%ROWTYPE;
1243     item_object        asset.copy%ROWTYPE;
1244     item_status_object    config.copy_status%ROWTYPE;
1245     item_location_object    asset.copy_location%ROWTYPE;
1246     result            action.matrix_test_result;
1247     circ_test        config.circ_matrix_matchpoint%ROWTYPE;
1248     out_by_circ_mod        config.circ_matrix_circ_mod_test%ROWTYPE;
1249     circ_mod_map        config.circ_matrix_circ_mod_test_map%ROWTYPE;
1250     penalty_type         TEXT;
1251     tmp_grp         INT;
1252     items_out        INT;
1253     context_org_list        INT[];
1254     done            BOOL := FALSE;
1255 BEGIN
1256     result.success := TRUE;
1257
1258     -- Fail if the user is BARRED
1259     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
1260
1261     -- Fail if we couldn't find a set of tests
1262     IF user_object.id IS NULL THEN
1263         result.fail_part := 'no_user';
1264         result.success := FALSE;
1265         done := TRUE;
1266         RETURN NEXT result;
1267         RETURN;
1268     END IF;
1269
1270     IF user_object.barred IS TRUE THEN
1271         result.fail_part := 'actor.usr.barred';
1272         result.success := FALSE;
1273         done := TRUE;
1274         RETURN NEXT result;
1275     END IF;
1276
1277     -- Fail if the item can't circulate
1278     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
1279     IF item_object.circulate IS FALSE THEN
1280         result.fail_part := 'asset.copy.circulate';
1281         result.success := FALSE;
1282         done := TRUE;
1283         RETURN NEXT result;
1284     END IF;
1285
1286     -- Fail if the item isn't in a circulateable status on a non-renewal
1287     IF NOT renewal AND item_object.status NOT IN ( 0, 7, 8 ) THEN
1288         result.fail_part := 'asset.copy.status';
1289         result.success := FALSE;
1290         done := TRUE;
1291         RETURN NEXT result;
1292     ELSIF renewal AND item_object.status <> 1 THEN
1293         result.fail_part := 'asset.copy.status';
1294         result.success := FALSE;
1295         done := TRUE;
1296         RETURN NEXT result;
1297     END IF;
1298
1299     -- Fail if the item can't circulate because of the shelving location
1300     SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
1301     IF item_location_object.circulate IS FALSE THEN
1302         result.fail_part := 'asset.copy_location.circulate';
1303         result.success := FALSE;
1304         done := TRUE;
1305         RETURN NEXT result;
1306     END IF;
1307
1308     SELECT INTO circ_test * FROM action.find_circ_matrix_matchpoint(circ_ou, match_item, match_user, renewal);
1309     result.matchpoint := circ_test.id;
1310
1311     SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( circ_test.org_unit );
1312
1313     -- Fail if we couldn't find a set of tests
1314     IF result.matchpoint IS NULL THEN
1315         result.fail_part := 'no_matchpoint';
1316         result.success := FALSE;
1317         done := TRUE;
1318         RETURN NEXT result;
1319     END IF;
1320
1321     -- Fail if the test is set to hard non-circulating
1322     IF circ_test.circulate IS FALSE THEN
1323         result.fail_part := 'config.circ_matrix_test.circulate';
1324         result.success := FALSE;
1325         done := TRUE;
1326         RETURN NEXT result;
1327     END IF;
1328
1329     IF renewal THEN
1330         penalty_type = '%RENEW%';
1331     ELSE
1332         penalty_type = '%CIRC%';
1333     END IF;
1334
1335     FOR standing_penalty IN
1336         SELECT  DISTINCT csp.*
1337           FROM  actor.usr_standing_penalty usp
1338                 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
1339           WHERE usr = match_user
1340                 AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
1341                 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
1342                 AND csp.block_list LIKE penalty_type LOOP
1343
1344         result.fail_part := standing_penalty.name;
1345         result.success := FALSE;
1346         done := TRUE;
1347         RETURN NEXT result;
1348     END LOOP;
1349
1350     -- Fail if the user has too many items with specific circ_modifiers checked out
1351     FOR out_by_circ_mod IN SELECT * FROM config.circ_matrix_circ_mod_test WHERE matchpoint = circ_test.id LOOP
1352         SELECT  INTO items_out COUNT(*)
1353           FROM  action.circulation circ
1354             JOIN asset.copy cp ON (cp.id = circ.target_copy)
1355           WHERE circ.usr = match_user
1356                AND circ_lib IN ( SELECT * FROM explode_array(context_org_list) )
1357             AND circ.checkin_time IS NULL
1358             AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL)
1359             AND cp.circ_modifier IN (SELECT circ_mod FROM config.circ_matrix_circ_mod_test_map WHERE circ_mod_test = out_by_circ_mod.id);
1360         IF items_out >= out_by_circ_mod.items_out THEN
1361             result.fail_part := 'config.circ_matrix_circ_mod_test';
1362             result.success := FALSE;
1363             done := TRUE;
1364             RETURN NEXT result;
1365         END IF;
1366     END LOOP;
1367
1368     -- If we passed everything, return the successful matchpoint id
1369     IF NOT done THEN
1370         RETURN NEXT result;
1371     END IF;
1372
1373     RETURN;
1374 END;
1375 $func$ LANGUAGE plpgsql;
1376
1377 CREATE OR REPLACE FUNCTION actor.calculate_system_penalties( match_user INT, context_org INT ) RETURNS SETOF actor.usr_standing_penalty AS $func$
1378 DECLARE
1379     user_object         actor.usr%ROWTYPE;
1380     new_sp_row          actor.usr_standing_penalty%ROWTYPE;
1381     existing_sp_row     actor.usr_standing_penalty%ROWTYPE;
1382     collections_fines   permission.grp_penalty_threshold%ROWTYPE;
1383     max_fines           permission.grp_penalty_threshold%ROWTYPE;
1384     max_overdue         permission.grp_penalty_threshold%ROWTYPE;
1385     max_items_out       permission.grp_penalty_threshold%ROWTYPE;
1386     tmp_grp             INT;
1387     items_overdue       INT;
1388     items_out           INT;
1389     context_org_list    INT[];
1390     current_fines        NUMERIC(8,2) := 0.0;
1391     tmp_fines            NUMERIC(8,2);
1392     tmp_groc            RECORD;
1393     tmp_circ            RECORD;
1394     tmp_org             actor.org_unit%ROWTYPE;
1395 BEGIN
1396     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
1397
1398     -- Max fines
1399     SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
1400
1401     -- Fail if the user has a high fine balance
1402     LOOP
1403         tmp_grp := user_object.profile;
1404         LOOP
1405             SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id;
1406
1407             IF max_fines.threshold IS NULL THEN
1408                 SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
1409             ELSE
1410                 EXIT;
1411             END IF;
1412
1413             IF tmp_grp IS NULL THEN
1414                 EXIT;
1415             END IF;
1416         END LOOP;
1417
1418         IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
1419             EXIT;
1420         END IF;
1421
1422         SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
1423
1424     END LOOP;
1425
1426     IF max_fines.threshold IS NOT NULL THEN
1427
1428         FOR existing_sp_row IN
1429                 SELECT  *
1430                   FROM  actor.usr_standing_penalty
1431                   WHERE usr = match_user
1432                         AND org_unit = max_fines.org_unit
1433                         AND (stop_date IS NULL or stop_date > NOW())
1434                         AND standing_penalty = 1
1435                 LOOP
1436             RETURN NEXT existing_sp_row;
1437         END LOOP;
1438
1439         SELECT  SUM(f.balance_owed) INTO current_fines
1440           FROM  money.materialized_billable_xact_summary f
1441                 JOIN (
1442                     SELECT  g.id
1443                       FROM  money.grocery g
1444                             JOIN  actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id)
1445                       WHERE usr = match_user
1446                             AND xact_finish IS NULL
1447                                 UNION ALL
1448                     SELECT  circ.id
1449                       FROM  action.circulation circ
1450                             JOIN  actor.org_unit_full_path( max_fines.org_unit ) fp ON (circ.circ_lib = fp.id)
1451                       WHERE usr = match_user
1452                             AND xact_finish IS NULL ) l USING (id);
1453
1454         IF current_fines >= max_fines.threshold THEN
1455             new_sp_row.usr := match_user;
1456             new_sp_row.org_unit := max_fines.org_unit;
1457             new_sp_row.standing_penalty := 1;
1458             RETURN NEXT new_sp_row;
1459         END IF;
1460     END IF;
1461
1462     -- Start over for max overdue
1463     SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
1464
1465     -- Fail if the user has too many overdue items
1466     LOOP
1467         tmp_grp := user_object.profile;
1468         LOOP
1469
1470             SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id;
1471
1472             IF max_overdue.threshold IS NULL THEN
1473                 SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
1474             ELSE
1475                 EXIT;
1476             END IF;
1477
1478             IF tmp_grp IS NULL THEN
1479                 EXIT;
1480             END IF;
1481         END LOOP;
1482
1483         IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
1484             EXIT;
1485         END IF;
1486
1487         SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
1488
1489     END LOOP;
1490
1491     IF max_overdue.threshold IS NOT NULL THEN
1492
1493         FOR existing_sp_row IN
1494                 SELECT  *
1495                   FROM  actor.usr_standing_penalty
1496                   WHERE usr = match_user
1497                         AND org_unit = max_overdue.org_unit
1498                         AND (stop_date IS NULL or stop_date > NOW())
1499                         AND standing_penalty = 2
1500                 LOOP
1501             RETURN NEXT existing_sp_row;
1502         END LOOP;
1503
1504         SELECT  INTO items_overdue COUNT(*)
1505           FROM  action.circulation circ
1506                 JOIN  actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id)
1507           WHERE circ.usr = match_user
1508             AND circ.checkin_time IS NULL
1509             AND circ.due_date < NOW()
1510             AND (circ.stop_fines = 'MAXFINES' OR circ.stop_fines IS NULL);
1511
1512         IF items_overdue >= max_overdue.threshold::INT THEN
1513             new_sp_row.usr := match_user;
1514             new_sp_row.org_unit := max_overdue.org_unit;
1515             new_sp_row.standing_penalty := 2;
1516             RETURN NEXT new_sp_row;
1517         END IF;
1518     END IF;
1519
1520     -- Start over for max out
1521     SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
1522
1523     -- Fail if the user has too many checked out items
1524     LOOP
1525         tmp_grp := user_object.profile;
1526         LOOP
1527             SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id;
1528
1529             IF max_items_out.threshold IS NULL THEN
1530                 SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
1531             ELSE
1532                 EXIT;
1533             END IF;
1534
1535             IF tmp_grp IS NULL THEN
1536                 EXIT;
1537             END IF;
1538         END LOOP;
1539
1540         IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
1541             EXIT;
1542         END IF;
1543
1544         SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou;
1545
1546     END LOOP;
1547
1548
1549     -- Fail if the user has too many items checked out
1550     IF max_items_out.threshold IS NOT NULL THEN
1551
1552         FOR existing_sp_row IN
1553                 SELECT  *
1554                   FROM  actor.usr_standing_penalty
1555                   WHERE usr = match_user
1556                         AND org_unit = max_items_out.org_unit
1557                         AND (stop_date IS NULL or stop_date > NOW())
1558                         AND standing_penalty = 3
1559                 LOOP
1560             RETURN NEXT existing_sp_row;
1561         END LOOP;
1562
1563         SELECT  INTO items_out COUNT(*)
1564           FROM  action.circulation circ
1565                 JOIN  actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id)
1566           WHERE circ.usr = match_user
1567                 AND circ.checkin_time IS NULL
1568                 AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL);
1569
1570            IF items_out >= max_items_out.threshold::INT THEN
1571             new_sp_row.usr := match_user;
1572             new_sp_row.org_unit := max_items_out.org_unit;
1573             new_sp_row.standing_penalty := 3;
1574             RETURN NEXT new_sp_row;
1575            END IF;
1576     END IF;
1577
1578     -- Start over for collections warning
1579     SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
1580
1581     -- Fail if the user has a collections-level fine balance
1582     LOOP
1583         tmp_grp := user_object.profile;
1584         LOOP
1585             SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 4 AND org_unit = tmp_org.id;
1586
1587             IF max_fines.threshold IS NULL THEN
1588                 SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp;
1589             ELSE
1590                 EXIT;
1591             END IF;
1592
1593             IF tmp_grp IS NULL THEN
1594                 EXIT;
1595             END IF;
1596         END LOOP;
1597
1598         IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN
1599             EXIT;
1600         END IF;
1601
1602         SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
1603
1604     END LOOP;
1605
1606     IF max_fines.threshold IS NOT NULL THEN
1607
1608         FOR existing_sp_row IN
1609                 SELECT  *
1610                   FROM  actor.usr_standing_penalty
1611                   WHERE usr = match_user
1612                         AND org_unit = max_fines.org_unit
1613                         AND (stop_date IS NULL or stop_date > NOW())
1614                         AND standing_penalty = 4
1615                 LOOP
1616             RETURN NEXT existing_sp_row;
1617         END LOOP;
1618
1619         SELECT  SUM(f.balance_owed) INTO current_fines
1620           FROM  money.materialized_billable_xact_summary f
1621                 JOIN (
1622                     SELECT  g.id
1623                       FROM  money.grocery g
1624                             JOIN  actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id)
1625                       WHERE usr = match_user
1626                             AND xact_finish IS NULL
1627                                 UNION ALL
1628                     SELECT  circ.id
1629                       FROM  action.circulation circ
1630                             JOIN  actor.org_unit_full_path( max_fines.org_unit ) fp ON (circ.circ_lib = fp.id)
1631                       WHERE usr = match_user
1632                             AND xact_finish IS NULL ) l USING (id);
1633
1634         IF current_fines >= max_fines.threshold THEN
1635             new_sp_row.usr := match_user;
1636             new_sp_row.org_unit := max_fines.org_unit;
1637             new_sp_row.standing_penalty := 4;
1638             RETURN NEXT new_sp_row;
1639         END IF;
1640     END IF;
1641
1642
1643     RETURN;
1644 END;
1645 $func$ LANGUAGE plpgsql;
1646
1647
1648 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN juvenile_flag BOOL;
1649 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN age_hold_protect_rule INT REFERENCES config.rule_age_hold_protect (id) DEFERRABLE INITIALLY DEFERRED;
1650 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN holdable BOOL NOT NULL DEFAULT TRUE;
1651 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN distance_is_from_owner BOOL NOT NULL DEFAULT FALSE;
1652 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN transit_range INT REFERENCES actor.org_unit_type (id) DEFERRABLE INITIALLY DEFERRED;
1653 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN max_holds INT;
1654 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN include_frozen_holds BOOL NOT NULL DEFAULT TRUE;
1655 ALTER TABLE config.hold_matrix_matchpoint ADD COLUMN stop_blocked_user BOOL NOT NULL DEFAULT FALSE;
1656
1657 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS INT AS $func$
1658 DECLARE
1659     current_requestor_group    permission.grp_tree%ROWTYPE;
1660     root_ou            actor.org_unit%ROWTYPE;
1661     requestor_object    actor.usr%ROWTYPE;
1662     user_object        actor.usr%ROWTYPE;
1663     item_object        asset.copy%ROWTYPE;
1664     item_cn_object        asset.call_number%ROWTYPE;
1665     rec_descriptor        metabib.rec_descriptor%ROWTYPE;
1666     current_mp_weight    FLOAT;
1667     matchpoint_weight    FLOAT;
1668     tmp_weight        FLOAT;
1669     current_mp        config.hold_matrix_matchpoint%ROWTYPE;
1670     matchpoint        config.hold_matrix_matchpoint%ROWTYPE;
1671 BEGIN
1672     SELECT INTO root_ou * FROM actor.org_unit WHERE parent_ou IS NULL;
1673     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
1674     SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
1675     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
1676     SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
1677     SELECT INTO rec_descriptor r.* FROM metabib.rec_descriptor r WHERE r.record = item_cn_object.record;
1678     SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = requestor_object.profile;
1679
1680     LOOP
1681         -- for each potential matchpoint for this ou and group ...
1682         FOR current_mp IN
1683             SELECT    m.*
1684               FROM    config.hold_matrix_matchpoint m
1685               WHERE    m.requestor_grp = current_requestor_group.id AND m.active
1686               ORDER BY    CASE WHEN m.circ_modifier    IS NOT NULL THEN 16 ELSE 0 END +
1687                     CASE WHEN m.juvenile_flag    IS NOT NULL THEN 16 ELSE 0 END +
1688                     CASE WHEN m.marc_type        IS NOT NULL THEN 8 ELSE 0 END +
1689                     CASE WHEN m.marc_form        IS NOT NULL THEN 4 ELSE 0 END +
1690                     CASE WHEN m.marc_vr_format    IS NOT NULL THEN 2 ELSE 0 END +
1691                     CASE WHEN m.ref_flag        IS NOT NULL THEN 1 ELSE 0 END DESC LOOP
1692
1693             current_mp_weight := 5.0;
1694
1695             IF current_mp.circ_modifier IS NOT NULL THEN
1696                 CONTINUE WHEN current_mp.circ_modifier <> item_object.circ_modifier;
1697             END IF;
1698
1699             IF current_mp.marc_type IS NOT NULL THEN
1700                 IF item_object.circ_as_type IS NOT NULL THEN
1701                     CONTINUE WHEN current_mp.marc_type <> item_object.circ_as_type;
1702                 ELSE
1703                     CONTINUE WHEN current_mp.marc_type <> rec_descriptor.item_type;
1704                 END IF;
1705             END IF;
1706
1707             IF current_mp.marc_form IS NOT NULL THEN
1708                 CONTINUE WHEN current_mp.marc_form <> rec_descriptor.item_form;
1709             END IF;
1710
1711             IF current_mp.marc_vr_format IS NOT NULL THEN
1712                 CONTINUE WHEN current_mp.marc_vr_format <> rec_descriptor.vr_format;
1713             END IF;
1714
1715             IF current_mp.juvenile_flag IS NOT NULL THEN
1716                 CONTINUE WHEN current_mp.juvenile_flag <> user_object.juvenile;
1717             END IF;
1718
1719             IF current_mp.ref_flag IS NOT NULL THEN
1720                 CONTINUE WHEN current_mp.ref_flag <> item_object.ref;
1721             END IF;
1722
1723
1724             -- caclulate the rule match weight
1725             IF current_mp.item_owning_ou IS NOT NULL AND current_mp.item_owning_ou <> root_ou.id THEN
1726                 SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_owning_ou, item_cn_object.owning_lib)::FLOAT + 1.0)::FLOAT;
1727                 current_mp_weight := current_mp_weight - tmp_weight;
1728             END IF;
1729
1730             IF current_mp.item_circ_ou IS NOT NULL AND current_mp.item_circ_ou <> root_ou.id THEN
1731                 SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.item_circ_ou, item_object.circ_lib)::FLOAT + 1.0)::FLOAT;
1732                 current_mp_weight := current_mp_weight - tmp_weight;
1733             END IF;
1734
1735             IF current_mp.pickup_ou IS NOT NULL AND current_mp.pickup_ou <> root_ou.id THEN
1736                 SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.pickup_ou, pickup_ou)::FLOAT + 1.0)::FLOAT;
1737                 current_mp_weight := current_mp_weight - tmp_weight;
1738             END IF;
1739
1740             IF current_mp.request_ou IS NOT NULL AND current_mp.request_ou <> root_ou.id THEN
1741                 SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.request_ou, request_ou)::FLOAT + 1.0)::FLOAT;
1742                 current_mp_weight := current_mp_weight - tmp_weight;
1743             END IF;
1744
1745             IF current_mp.user_home_ou IS NOT NULL AND current_mp.user_home_ou <> root_ou.id THEN
1746                 SELECT INTO tmp_weight 1.0 / (actor.org_unit_proximity(current_mp.user_home_ou, user_object.home_ou)::FLOAT + 1.0)::FLOAT;
1747                 current_mp_weight := current_mp_weight - tmp_weight;
1748             END IF;
1749
1750             -- set the matchpoint if we found the best one
1751             IF matchpoint_weight IS NULL OR matchpoint_weight > current_mp_weight THEN
1752                 matchpoint = current_mp;
1753                 matchpoint_weight = current_mp_weight;
1754             END IF;
1755
1756         END LOOP;
1757
1758         EXIT WHEN current_requestor_group.parent IS NULL OR matchpoint.id IS NOT NULL;
1759
1760         SELECT INTO current_requestor_group * FROM permission.grp_tree WHERE id = current_requestor_group.parent;
1761     END LOOP;
1762
1763     RETURN matchpoint.id;
1764 END;
1765 $func$ LANGUAGE plpgsql;
1766
1767
1768 CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT ) RETURNS SETOF action.matrix_test_result AS
1769 $func$
1770 DECLARE
1771     matchpoint_id        INT;
1772     user_object        actor.usr%ROWTYPE;
1773     age_protect_object    config.rule_age_hold_protect%ROWTYPE;
1774     standing_penalty    config.standing_penalty%ROWTYPE;
1775     transit_range_ou_type    actor.org_unit_type%ROWTYPE;
1776     transit_source        actor.org_unit%ROWTYPE;
1777     item_object        asset.copy%ROWTYPE;
1778     result            action.matrix_test_result;
1779     hold_test        config.hold_matrix_matchpoint%ROWTYPE;
1780     hold_count        INT;
1781     hold_transit_prox    INT;
1782     frozen_hold_count    INT;
1783     context_org_list    INT[];
1784     done            BOOL := FALSE;
1785 BEGIN
1786     SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
1787     SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
1788
1789     -- Fail if we couldn't find a user
1790     IF user_object.id IS NULL THEN
1791         result.fail_part := 'no_user';
1792         result.success := FALSE;
1793         done := TRUE;
1794         RETURN NEXT result;
1795         RETURN;
1796     END IF;
1797
1798     -- Fail if user is barred
1799     IF user_object.barred IS TRUE THEN
1800         result.fail_part := 'actor.usr.barred';
1801         result.success := FALSE;
1802         done := TRUE;
1803         RETURN NEXT result;
1804         RETURN;
1805     END IF;
1806
1807     SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
1808
1809     -- Fail if we couldn't find a copy
1810     IF item_object.id IS NULL THEN
1811         result.fail_part := 'no_item';
1812         result.success := FALSE;
1813         done := TRUE;
1814         RETURN NEXT result;
1815         RETURN;
1816     END IF;
1817
1818     SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
1819
1820     -- Fail if we couldn't find any matchpoint (requires a default)
1821     IF matchpoint_id IS NULL THEN
1822         result.fail_part := 'no_matchpoint';
1823         result.success := FALSE;
1824         done := TRUE;
1825         RETURN NEXT result;
1826         RETURN;
1827     END IF;
1828
1829     SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
1830
1831     result.matchpoint := hold_test.id;
1832     result.success := TRUE;
1833
1834     IF hold_test.holdable IS FALSE THEN
1835         result.fail_part := 'config.hold_matrix_test.holdable';
1836         result.success := FALSE;
1837         done := TRUE;
1838         RETURN NEXT result;
1839     END IF;
1840
1841     IF hold_test.transit_range IS NOT NULL THEN
1842         SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
1843         IF hold_test.distance_is_from_owner THEN
1844             SELECT INTO transit_source ou.* FROM actor.org_unit ou JOIN asset.call_number cn ON (cn.owning_lib = ou.id) WHERE cn.id = item_object.call_number;
1845         ELSE
1846             SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
1847         END IF;
1848
1849         PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
1850
1851         IF NOT FOUND THEN
1852             result.fail_part := 'transit_range';
1853             result.success := FALSE;
1854             done := TRUE;
1855             RETURN NEXT result;
1856         END IF;
1857     END IF;
1858
1859     FOR standing_penalty IN
1860         SELECT  DISTINCT csp.*
1861           FROM  actor.usr_standing_penalty usp
1862                 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
1863           WHERE usr = match_user
1864                 AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
1865                 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
1866                 AND csp.block_list LIKE '%HOLD%' LOOP
1867
1868         result.fail_part := standing_penalty.name;
1869         result.success := FALSE;
1870         done := TRUE;
1871         RETURN NEXT result;
1872     END LOOP;
1873
1874     IF hold_test.stop_blocked_user IS TRUE THEN
1875         FOR standing_penalty IN
1876             SELECT  DISTINCT csp.*
1877               FROM  actor.usr_standing_penalty usp
1878                     JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
1879               WHERE usr = match_user
1880                     AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
1881                     AND (usp.stop_date IS NULL or usp.stop_date > NOW())
1882                     AND csp.block_list LIKE '%CIRC%' LOOP
1883
1884             result.fail_part := standing_penalty.name;
1885             result.success := FALSE;
1886             done := TRUE;
1887             RETURN NEXT result;
1888         END LOOP;
1889     END IF;
1890
1891     IF hold_test.max_holds IS NOT NULL THEN
1892         SELECT    INTO hold_count COUNT(*)
1893           FROM    action.hold_request
1894           WHERE    usr = match_user
1895             AND fulfillment_time IS NULL
1896             AND cancel_time IS NULL
1897             AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
1898
1899         IF hold_count >= hold_test.max_holds THEN
1900             result.fail_part := 'config.hold_matrix_test.max_holds';
1901             result.success := FALSE;
1902             done := TRUE;
1903             RETURN NEXT result;
1904         END IF;
1905     END IF;
1906
1907     IF item_object.age_protect IS NOT NULL THEN
1908         SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
1909
1910         IF item_object.create_date + age_protect_object.age > NOW() THEN
1911             IF hold_test.distance_is_from_owner THEN
1912                 SELECT INTO hold_transit_prox prox FROM actor.org_unit_prox WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
1913             ELSE
1914                 SELECT INTO hold_transit_prox prox FROM actor.org_unit_prox WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
1915             END IF;
1916
1917             IF hold_transit_prox > age_protect_object.prox THEN
1918                 result.fail_part := 'config.rule_age_hold_protect.prox';
1919                 result.success := FALSE;
1920                 done := TRUE;
1921                 RETURN NEXT result;
1922             END IF;
1923         END IF;
1924     END IF;
1925
1926     IF NOT done THEN
1927         RETURN NEXT result;
1928     END IF;
1929
1930     RETURN;
1931 END;
1932 $func$ LANGUAGE plpgsql;
1933
1934 CREATE SCHEMA acq;
1935
1936
1937 -- Tables
1938
1939
1940 CREATE TABLE acq.currency_type (
1941     code    TEXT PRIMARY KEY,
1942     label   TEXT
1943 );
1944
1945 -- Use the ISO 4217 abbreviations for currency codes
1946 INSERT INTO acq.currency_type (code, label) VALUES ('USD','US Dollars');
1947 INSERT INTO acq.currency_type (code, label) VALUES ('CAN','Canadian Dollars');
1948 INSERT INTO acq.currency_type (code, label) VALUES ('EUR','Euros');
1949
1950 CREATE TABLE acq.exchange_rate (
1951     id              SERIAL  PRIMARY KEY,
1952     from_currency   TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
1953     to_currency     TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
1954     ratio           NUMERIC NOT NULL,
1955     CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency)
1956 );
1957
1958 INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','CAN',1.2);
1959 INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','EUR',0.5);
1960
1961 CREATE TABLE acq.provider (
1962     id                  SERIAL  PRIMARY KEY,
1963     name                TEXT    NOT NULL,
1964     owner               INT     NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
1965     currency_type       TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
1966     code                TEXT    UNIQUE,
1967     holding_tag         TEXT,
1968     CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner)
1969 );
1970
1971 CREATE TABLE acq.provider_holding_subfield_map (
1972     id          SERIAL  PRIMARY KEY,
1973     provider    INT     NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
1974     name        TEXT    NOT NULL, -- barcode, price, etc
1975     subfield    TEXT    NOT NULL,
1976     CONSTRAINT name_once_per_provider UNIQUE (provider,name)
1977 );
1978
1979 CREATE TABLE acq.provider_address (
1980     id      SERIAL  PRIMARY KEY,
1981     valid       BOOL    NOT NULL DEFAULT TRUE,
1982     address_type    TEXT,
1983     provider    INT     NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
1984     street1     TEXT    NOT NULL,
1985     street2     TEXT,
1986     city        TEXT    NOT NULL,
1987     county      TEXT,
1988     state       TEXT    NOT NULL,
1989     country     TEXT    NOT NULL,
1990     post_code   TEXT    NOT NULL
1991 );
1992
1993 CREATE TABLE acq.provider_contact (
1994     id      SERIAL  PRIMARY KEY,
1995     provider    INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
1996     name    TEXT NULL NULL,
1997     role    TEXT, -- free-form.. e.g. "our sales guy"
1998     email   TEXT,
1999     phone   TEXT
2000 );
2001
2002 CREATE TABLE acq.provider_contact_address (
2003     id          SERIAL  PRIMARY KEY,
2004     valid           BOOL    NOT NULL DEFAULT TRUE,
2005     address_type    TEXT,
2006     contact         INT     NOT NULL REFERENCES acq.provider_contact (id) DEFERRABLE INITIALLY DEFERRED,
2007     street1         TEXT    NOT NULL,
2008     street2         TEXT,
2009     city            TEXT    NOT NULL,
2010     county          TEXT,
2011     state           TEXT    NOT NULL,
2012     country         TEXT    NOT NULL,
2013     post_code       TEXT    NOT NULL
2014 );
2015
2016
2017 CREATE TABLE acq.funding_source (
2018     id      SERIAL  PRIMARY KEY,
2019     name        TEXT    NOT NULL,
2020     owner       INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
2021     currency_type   TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
2022     code        TEXT    UNIQUE,
2023     CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner)
2024 );
2025
2026 CREATE TABLE acq.funding_source_credit (
2027     id  SERIAL  PRIMARY KEY,
2028     funding_source    INT     NOT NULL REFERENCES acq.funding_source (id) DEFERRABLE INITIALLY DEFERRED,
2029     amount  NUMERIC NOT NULL,
2030     note    TEXT
2031 );
2032
2033 CREATE TABLE acq.fund (
2034     id              SERIAL  PRIMARY KEY,
2035     org             INT     NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2036     name            TEXT    NOT NULL,
2037     year            INT     NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ),
2038     currency_type   TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
2039     code            TEXT    UNIQUE,
2040     CONSTRAINT name_once_per_org_year UNIQUE (org,name,year)
2041 );
2042
2043 CREATE TABLE acq.fund_debit (
2044     id          SERIAL  PRIMARY KEY,
2045     fund            INT     NOT NULL REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
2046     origin_amount       NUMERIC NOT NULL,  -- pre-exchange-rate amount
2047     origin_currency_type    TEXT    NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
2048     amount          NUMERIC NOT NULL,
2049     encumbrance     BOOL    NOT NULL DEFAULT TRUE,
2050     debit_type      TEXT    NOT NULL,
2051     xfer_destination    INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
2052     create_time     TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
2053 );
2054
2055 CREATE TABLE acq.fund_allocation (
2056     id          SERIAL  PRIMARY KEY,
2057     funding_source        INT     NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2058     fund        INT     NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2059     amount      NUMERIC,
2060     percent     NUMERIC CHECK (percent IS NULL OR percent BETWEEN 0.0 AND 100.0),
2061     allocator   INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2062     note        TEXT,
2063     create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
2064     CONSTRAINT allocation_amount_or_percent CHECK ((percent IS NULL AND amount IS NOT NULL) OR (percent IS NOT NULL AND amount IS NULL))
2065 );
2066
2067
2068 CREATE TABLE acq.picklist (
2069     id      SERIAL              PRIMARY KEY,
2070     owner       INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2071     creator         INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2072     editor          INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2073     org_unit    INT             NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
2074     name        TEXT                NOT NULL,
2075     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2076     edit_time   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2077     CONSTRAINT name_once_per_owner UNIQUE (name,owner)
2078 );
2079
2080 CREATE TABLE acq.purchase_order (
2081     id      SERIAL              PRIMARY KEY,
2082     owner       INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2083     creator         INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2084     editor          INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2085     ordering_agency INT             NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
2086     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2087     edit_time   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2088     provider    INT             NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
2089     state       TEXT                NOT NULL DEFAULT 'new'
2090 );
2091 CREATE INDEX po_owner_idx ON acq.purchase_order (owner);
2092 CREATE INDEX po_provider_idx ON acq.purchase_order (provider);
2093 CREATE INDEX po_state_idx ON acq.purchase_order (state);
2094
2095 CREATE TABLE acq.po_note (
2096     id      SERIAL              PRIMARY KEY,
2097     purchase_order  INT             NOT NULL REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
2098     creator     INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2099     editor      INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2100     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2101     edit_time   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2102     value       TEXT                NOT NULL
2103 );
2104 CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order);
2105
2106 CREATE TABLE acq.lineitem (
2107     id                  BIGSERIAL                   PRIMARY KEY,
2108     creator             INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2109     editor              INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2110     selector            INT                         NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2111     provider            INT                         REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
2112     purchase_order      INT                         REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
2113     picklist            INT                         REFERENCES acq.picklist (id) DEFERRABLE INITIALLY DEFERRED,
2114     expected_recv_time  TIMESTAMP WITH TIME ZONE,
2115     create_time         TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2116     edit_time           TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2117     marc                TEXT                        NOT NULL,
2118     eg_bib_id           INT                         REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
2119     source_label        TEXT,
2120     item_count          INT                         NOT NULL DEFAULT 0,
2121     state               TEXT                        NOT NULL DEFAULT 'new',
2122     CONSTRAINT picklist_or_po CHECK (picklist IS NOT NULL OR purchase_order IS NOT NULL)
2123 );
2124 CREATE INDEX li_po_idx ON acq.lineitem (purchase_order);
2125 CREATE INDEX li_pl_idx ON acq.lineitem (picklist);
2126
2127 CREATE TABLE acq.lineitem_note (
2128     id      SERIAL              PRIMARY KEY,
2129     lineitem    INT             NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
2130     creator     INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2131     editor      INT             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
2132     create_time TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2133     edit_time   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT NOW(),
2134     value       TEXT                NOT NULL
2135 );
2136 CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem);
2137
2138 CREATE TABLE acq.lineitem_detail (
2139     id          BIGSERIAL   PRIMARY KEY,
2140     lineitem    INT         NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
2141     fund        INT         REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
2142     fund_debit  INT         REFERENCES acq.fund_debit (id) DEFERRABLE INITIALLY DEFERRED,
2143     eg_copy_id  BIGINT      REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2144     barcode     TEXT,
2145     cn_label    TEXT,
2146     note        TEXT,
2147     collection_code TEXT,
2148     circ_modifier   TEXT    REFERENCES config.circ_modifier (code) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2149     owning_lib  INT         REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2150     location    INT         REFERENCES asset.copy_location (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2151     recv_time   TIMESTAMP WITH TIME ZONE
2152 );
2153
2154 CREATE INDEX li_detail_li_idx ON acq.lineitem_detail (lineitem);
2155
2156 CREATE TABLE acq.lineitem_attr_definition (
2157     id      BIGSERIAL   PRIMARY KEY,
2158     code        TEXT        NOT NULL,
2159     description TEXT        NOT NULL,
2160     remove      TEXT        NOT NULL DEFAULT '',
2161     ident       BOOL        NOT NULL DEFAULT FALSE
2162 );
2163
2164 CREATE TABLE acq.lineitem_marc_attr_definition (
2165     id      BIGINT  PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
2166     xpath       TEXT        NOT NULL
2167 ) INHERITS (acq.lineitem_attr_definition);
2168
2169 CREATE TABLE acq.lineitem_provider_attr_definition (
2170     id      BIGINT  PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
2171     xpath       TEXT        NOT NULL,
2172     provider    INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED
2173 ) INHERITS (acq.lineitem_attr_definition);
2174
2175 CREATE TABLE acq.lineitem_generated_attr_definition (
2176     id      BIGINT  PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
2177     xpath       TEXT        NOT NULL
2178 ) INHERITS (acq.lineitem_attr_definition);
2179
2180 CREATE TABLE acq.lineitem_usr_attr_definition (
2181     id      BIGINT  PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
2182     usr     INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED
2183 ) INHERITS (acq.lineitem_attr_definition);
2184
2185 CREATE TABLE acq.lineitem_local_attr_definition (
2186     id      BIGINT  PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq')
2187 ) INHERITS (acq.lineitem_attr_definition);
2188
2189 CREATE TABLE acq.lineitem_attr (
2190     id      BIGSERIAL   PRIMARY KEY,
2191     definition  BIGINT      NOT NULL,
2192     lineitem    BIGINT      NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
2193     attr_type   TEXT        NOT NULL,
2194     attr_name   TEXT        NOT NULL,
2195     attr_value  TEXT        NOT NULL
2196 );
2197
2198 CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem);
2199 CREATE INDEX li_attr_value_idx ON acq.lineitem_attr (attr_value);
2200 CREATE INDEX li_attr_definition_idx ON acq.lineitem_attr (definition);
2201
2202
2203 -- Seed data
2204
2205
2206 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]');
2207 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('author','Author of work','//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]');
2208 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('language','Language of work','//*[@tag="240"]/*[@code="l"][1]');
2209 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]');
2210 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
2211 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
2212 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
2213 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('identifier','Identifier','//*[@tag="001"]');
2214 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]');
2215 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]');
2216 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]');
2217
2218 INSERT INTO acq.lineitem_local_attr_definition ( code, description ) VALUES ('estimated_price', 'Estimated Price');
2219
2220
2221 CREATE TABLE acq.distribution_formula (
2222     id      SERIAL PRIMARY KEY,
2223     owner   INT NOT NULL
2224             REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
2225     name    TEXT NOT NULL,
2226     skip_count  INT NOT NULL DEFAULT 0,
2227     CONSTRAINT acqdf_name_once_per_owner UNIQUE (name, owner)
2228 );
2229
2230 CREATE TABLE acq.distribution_formula_entry (
2231     id          SERIAL PRIMARY KEY,
2232     formula     INTEGER NOT NULL REFERENCES acq.distribution_formula(id)
2233                 ON DELETE CASCADE
2234                 DEFERRABLE INITIALLY DEFERRED,
2235     position    INTEGER NOT NULL,
2236     item_count  INTEGER NOT NULL,
2237     owning_lib  INTEGER REFERENCES actor.org_unit(id)
2238                 DEFERRABLE INITIALLY DEFERRED,
2239     location    INTEGER REFERENCES asset.copy_location(id),
2240     CONSTRAINT acqdfe_lib_once_per_formula UNIQUE( formula, position ),
2241     CONSTRAINT acqdfe_must_be_somewhere
2242                 CHECK( owning_lib IS NOT NULL OR location IS NOT NULL )
2243 );
2244
2245 CREATE TABLE acq.fund_tag (
2246     id      SERIAL PRIMARY KEY,
2247     owner   INT NOT NULL
2248             REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
2249     name    TEXT NOT NULL,
2250     CONSTRAINT acqft_tag_once_per_owner UNIQUE (name, owner)
2251 );
2252
2253 CREATE TABLE acq.fund_tag_map (
2254     id          SERIAL PRIMARY KEY,
2255     fund        INTEGER NOT NULL REFERENCES acq.fund(id)
2256                 DEFERRABLE INITIALLY DEFERRED,
2257     tag         INTEGER REFERENCES acq.fund_tag(id)
2258                 ON DELETE CASCADE
2259                 DEFERRABLE INITIALLY DEFERRED,
2260     CONSTRAINT acqftm_fund_once_per_tag UNIQUE( fund, tag )
2261 );
2262
2263 -- Functions
2264
2265 CREATE TYPE acq.flat_lineitem_holding_subfield AS (lineitem int, holding int, subfield text, data text);
2266 CREATE OR REPLACE FUNCTION acq.extract_holding_attr_table (lineitem int, tag text) RETURNS SETOF acq.flat_lineitem_holding_subfield AS $$
2267 DECLARE
2268     counter INT;
2269     lida    acq.flat_lineitem_holding_subfield%ROWTYPE;
2270 BEGIN
2271
2272     SELECT  COUNT(*) INTO counter
2273       FROM  xpath_table(
2274                 'id',
2275                 'marc',
2276                 'acq.lineitem',
2277                 '//*[@tag="' || tag || '"]',
2278                 'id=' || lineitem
2279             ) as t(i int,c text);
2280
2281     FOR i IN 1 .. counter LOOP
2282         FOR lida IN
2283             SELECT  *
2284               FROM  (   SELECT  id,i,t,v
2285                           FROM  xpath_table(
2286                                     'id',
2287                                     'marc',
2288                                     'acq.lineitem',
2289                                     '//*[@tag="' || tag || '"][position()=' || i || ']/*/@code|' ||
2290                                         '//*[@tag="' || tag || '"][position()=' || i || ']/*[@code]',
2291                                     'id=' || lineitem
2292                                 ) as t(id int,t text,v text)
2293                     )x
2294         LOOP
2295             RETURN NEXT lida;
2296         END LOOP;
2297     END LOOP;
2298
2299     RETURN;
2300 END;
2301 $$ LANGUAGE PLPGSQL;
2302
2303 CREATE TYPE acq.flat_lineitem_detail AS (lineitem int, holding int, attr text, data text);
2304 CREATE OR REPLACE FUNCTION acq.extract_provider_holding_data ( lineitem_i int ) RETURNS SETOF acq.flat_lineitem_detail AS $$
2305 DECLARE
2306     prov_i  INT;
2307     tag_t   TEXT;
2308     lida    acq.flat_lineitem_detail%ROWTYPE;
2309 BEGIN
2310     SELECT provider INTO prov_i FROM acq.lineitem WHERE id = lineitem_i;
2311     IF NOT FOUND THEN RETURN; END IF;
2312
2313     SELECT holding_tag INTO tag_t FROM acq.provider WHERE id = prov_i;
2314     IF NOT FOUND OR tag_t IS NULL THEN RETURN; END IF;
2315
2316     FOR lida IN
2317         SELECT  lineitem_i,
2318                 h.holding,
2319                 a.name,
2320                 h.data
2321           FROM  acq.extract_holding_attr_table( lineitem_i, tag_t ) h
2322                 JOIN acq.provider_holding_subfield_map a USING (subfield)
2323           WHERE a.provider = prov_i
2324     LOOP
2325         RETURN NEXT lida;
2326     END LOOP;
2327
2328     RETURN;
2329 END;
2330 $$ LANGUAGE PLPGSQL;
2331
2332 -- select * from acq.extract_provider_holding_data(699);
2333
2334 CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) RETURNS TEXT AS $$
2335     SELECT public.extract_marc_field('acq.lineitem', $1, $2, $3);
2336 $$ LANGUAGE SQL;
2337
2338 CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $$
2339 DECLARE
2340     value       TEXT;
2341     atype       TEXT;
2342     prov        INT;
2343     adef        RECORD;
2344     xpath_string    TEXT;
2345 BEGIN
2346     FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP
2347
2348         SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid;
2349
2350         IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN
2351             IF (atype = 'lineitem_provider_attr_definition') THEN
2352                 SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
2353                 CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider;
2354             END IF;
2355
2356             IF (atype = 'lineitem_provider_attr_definition') THEN
2357                 SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
2358             ELSIF (atype = 'lineitem_marc_attr_definition') THEN
2359                 SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id;
2360             ELSIF (atype = 'lineitem_generated_attr_definition') THEN
2361                 SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id;
2362             END IF;
2363
2364             SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
2365
2366             IF (value IS NOT NULL AND value <> '') THEN
2367                 INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
2368                     VALUES (NEW.id, adef.id, atype, adef.code, value);
2369             END IF;
2370
2371         END IF;
2372
2373     END LOOP;
2374
2375     RETURN NULL;
2376 END;
2377 $$ LANGUAGE PLPGSQL;
2378
2379 CREATE OR REPLACE FUNCTION public.cleanup_acq_marc ( ) RETURNS TRIGGER AS $$
2380 BEGIN
2381     IF TG_OP = 'UPDATE' THEN
2382         DELETE FROM acq.lineitem_attr
2383                 WHERE lineitem = OLD.id AND attr_type IN ('lineitem_provider_attr_definition', 'lineitem_marc_attr_definition','lineitem_generated_attr_definition');
2384         RETURN NEW;
2385     ELSE
2386         DELETE FROM acq.lineitem_attr WHERE lineitem = OLD.id;
2387         RETURN OLD;
2388     END IF;
2389 END;
2390 $$ LANGUAGE PLPGSQL;
2391
2392 CREATE TRIGGER cleanup_lineitem_trigger
2393     BEFORE UPDATE OR DELETE ON acq.lineitem
2394     FOR EACH ROW EXECUTE PROCEDURE public.cleanup_acq_marc();
2395
2396 CREATE TRIGGER ingest_lineitem_trigger
2397     AFTER INSERT OR UPDATE ON acq.lineitem
2398     FOR EACH ROW EXECUTE PROCEDURE public.ingest_acq_marc();
2399
2400 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( from_ex TEXT, to_ex TEXT ) RETURNS NUMERIC AS $$
2401 DECLARE
2402     rat NUMERIC;
2403 BEGIN
2404     IF from_ex = to_ex THEN
2405         RETURN 1.0;
2406     END IF;
2407
2408     SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = from_ex AND to_currency = to_ex;
2409
2410     IF FOUND THEN
2411         RETURN rat;
2412     ELSE
2413         SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = to_ex AND to_currency = from_ex;
2414         IF FOUND THEN
2415             RETURN 1.0/rat;
2416         END IF;
2417     END IF;
2418
2419     RETURN NULL;
2420
2421 END;
2422 $$ LANGUAGE PLPGSQL;
2423
2424 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( TEXT, TEXT, NUMERIC ) RETURNS NUMERIC AS $$
2425     SELECT $3 * acq.exchange_ratio($1, $2);
2426 $$ LANGUAGE SQL;
2427
2428 CREATE OR REPLACE VIEW acq.funding_source_credit_total AS
2429     SELECT  funding_source,
2430             SUM(amount) AS amount
2431       FROM  acq.funding_source_credit
2432       GROUP BY 1;
2433
2434 CREATE OR REPLACE VIEW acq.funding_source_allocation_total AS
2435     SELECT  funding_source,
2436             SUM(amount)::NUMERIC(100,2) AS amount
2437       FROM (
2438             SELECT  funding_source,
2439                     SUM(a.amount)::NUMERIC(100,2) AS amount
2440               FROM  acq.fund_allocation a
2441               WHERE a.percent IS NULL
2442               GROUP BY 1
2443                             UNION ALL
2444             SELECT  funding_source,
2445                     SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * (a.percent/100.0) )::NUMERIC(100,2) AS amount
2446               FROM  acq.fund_allocation a
2447               WHERE a.amount IS NULL
2448               GROUP BY 1
2449         ) x
2450       GROUP BY 1;
2451
2452 CREATE OR REPLACE VIEW acq.funding_source_balance AS
2453     SELECT  COALESCE(c.funding_source, a.funding_source) AS funding_source,
2454             SUM(COALESCE(c.amount,0.0) - COALESCE(a.amount,0.0))::NUMERIC(100,2) AS amount
2455       FROM  acq.funding_source_credit_total c
2456             FULL JOIN acq.funding_source_allocation_total a USING (funding_source)
2457       GROUP BY 1;
2458
2459 CREATE OR REPLACE VIEW acq.fund_allocation_total AS
2460     SELECT  fund,
2461             SUM(amount)::NUMERIC(100,2) AS amount
2462       FROM (
2463             SELECT  fund,
2464                     SUM(a.amount * acq.exchange_ratio(s.currency_type, f.currency_type))::NUMERIC(100,2) AS amount
2465               FROM  acq.fund_allocation a
2466                     JOIN acq.fund f ON (a.fund = f.id)
2467                     JOIN acq.funding_source s ON (a.funding_source = s.id)
2468               WHERE a.percent IS NULL
2469               GROUP BY 1
2470                             UNION ALL
2471             SELECT  fund,
2472                     SUM( (SELECT SUM(amount) FROM acq.funding_source_credit c WHERE c.funding_source = a.funding_source) * acq.exchange_ratio(s.currency_type, f.currency_type) * (a.percent/100.0) )::NUMERIC(100,2) AS amount
2473               FROM  acq.fund_allocation a
2474                     JOIN acq.fund f ON (a.fund = f.id)
2475                     JOIN acq.funding_source s ON (a.funding_source = s.id)
2476               WHERE a.amount IS NULL
2477               GROUP BY 1
2478         ) x
2479       GROUP BY 1;
2480
2481 CREATE OR REPLACE VIEW acq.fund_debit_total AS
2482     SELECT  id AS fund,
2483             encumbrance,
2484             SUM(amount) AS amount
2485       FROM  acq.fund_debit
2486       GROUP BY 1,2;
2487
2488 CREATE OR REPLACE VIEW acq.fund_encumbrance_total AS
2489     SELECT  fund,
2490             SUM(amount) AS amount
2491       FROM  acq.fund_debit_total
2492       WHERE encumbrance IS TRUE
2493       GROUP BY 1;
2494
2495 CREATE OR REPLACE VIEW acq.fund_spent_total AS
2496     SELECT  fund,
2497             SUM(amount) AS amount
2498       FROM  acq.fund_debit_total
2499       WHERE encumbrance IS FALSE
2500       GROUP BY 1;
2501
2502 CREATE OR REPLACE VIEW acq.fund_combined_balance AS
2503     SELECT  c.fund,
2504             c.amount - COALESCE(d.amount,0.0) AS amount
2505       FROM  acq.fund_allocation_total c
2506             LEFT JOIN acq.fund_debit_total d USING (fund);
2507
2508 CREATE OR REPLACE VIEW acq.fund_spent_balance AS
2509     SELECT  c.fund,
2510             c.amount - COALESCE(d.amount,0.0) AS amount
2511       FROM  acq.fund_allocation_total c
2512             LEFT JOIN acq.fund_spent_total d USING (fund);
2513
2514 CREATE SCHEMA serial;
2515
2516 CREATE TABLE serial.record_entry (
2517     id      BIGSERIAL   PRIMARY KEY,
2518     record      BIGINT      REFERENCES biblio.record_entry (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2519     owning_lib  INT     NOT NULL DEFAULT 1 REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2520     creator     INT     NOT NULL DEFAULT 1,
2521     editor      INT     NOT NULL DEFAULT 1,
2522     source      INT,
2523     create_date TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now(),
2524     edit_date   TIMESTAMP WITH TIME ZONE    NOT NULL DEFAULT now(),
2525     active      BOOL        NOT NULL DEFAULT TRUE,
2526     deleted     BOOL        NOT NULL DEFAULT FALSE,
2527     marc        TEXT        NOT NULL,
2528     last_xact_id    TEXT        NOT NULL
2529 );
2530 CREATE INDEX serial_record_entry_creator_idx ON serial.record_entry ( creator );
2531 CREATE INDEX serial_record_entry_editor_idx ON serial.record_entry ( editor );
2532 CREATE INDEX serial_record_entry_owning_lib_idx ON serial.record_entry ( owning_lib, deleted );
2533
2534 CREATE TABLE serial.subscription (
2535     id      SERIAL  PRIMARY KEY,
2536     callnumber  BIGINT  REFERENCES asset.call_number (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2537     uri     INT REFERENCES asset.uri (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
2538     start_date  DATE    NOT NULL,
2539     end_date    DATE    -- interpret NULL as current subscription 
2540 );
2541
2542 CREATE TABLE serial.binding_unit (
2543     id      SERIAL  PRIMARY KEY,
2544     subscription    INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2545     label       TEXT    NOT NULL,
2546     CONSTRAINT bu_label_once_per_sub UNIQUE (subscription, label)
2547 );
2548
2549 CREATE TABLE serial.issuance (
2550     id      SERIAL  PRIMARY KEY,
2551     subscription    INT NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2552     target_copy BIGINT  REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2553     location    BIGINT  REFERENCES asset.copy_location(id) DEFERRABLE INITIALLY DEFERRED,
2554     binding_unit    INT REFERENCES serial.binding_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2555     label       TEXT
2556 );
2557
2558 CREATE TABLE serial.bib_summary (
2559     id          SERIAL  PRIMARY KEY,
2560     subscription        INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2561     generated_coverage  TEXT    NOT NULL,
2562     textual_holdings    TEXT
2563 );
2564
2565 CREATE TABLE serial.sup_summary (
2566     id          SERIAL  PRIMARY KEY,
2567     subscription        INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2568     generated_coverage  TEXT    NOT NULL,
2569     textual_holdings    TEXT
2570 );
2571
2572 CREATE TABLE serial.index_summary (
2573     id          SERIAL  PRIMARY KEY,
2574     subscription        INT UNIQUE NOT NULL REFERENCES serial.subscription (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2575     generated_coverage  TEXT    NOT NULL,
2576     textual_holdings    TEXT
2577 );
2578
2579
2580 CREATE OR REPLACE FUNCTION search.staged_fts (
2581
2582     param_search_ou INT,
2583     param_depth     INT,
2584     param_searches  TEXT, -- JSON hash, to be turned into a resultset via search.parse_search_args
2585     param_statuses  INT[],
2586     param_locations INT[],
2587     param_audience  TEXT[],
2588     param_language  TEXT[],
2589     param_lit_form  TEXT[],
2590     param_types     TEXT[],
2591     param_forms     TEXT[],
2592     param_vformats  TEXT[],
2593     param_bib_level TEXT[],
2594     param_before    TEXT,
2595     param_after     TEXT,
2596     param_during    TEXT,
2597     param_between   TEXT[],
2598     param_pref_lang TEXT,
2599     param_pref_lang_multiplier REAL,
2600     param_sort      TEXT,
2601     param_sort_desc BOOL,
2602     metarecord      BOOL,
2603     staff           BOOL,
2604     param_rel_limit INT,
2605     param_chk_limit INT,
2606     param_skip_chk  INT
2607
2608 ) RETURNS SETOF search.search_result AS $func$
2609 DECLARE
2610
2611     current_res         search.search_result%ROWTYPE;
2612     query_part          search.search_args%ROWTYPE;
2613     phrase_query_part   search.search_args%ROWTYPE;
2614     rank_adjust_id      INT;
2615     core_rel_limit      INT;
2616     core_chk_limit      INT;
2617     core_skip_chk       INT;
2618     rank_adjust         search.relevance_adjustment%ROWTYPE;
2619     query_table         TEXT;
2620     tmp_text            TEXT;
2621     tmp_int             INT;
2622     current_rank        TEXT;
2623     ranks               TEXT[] := '{}';
2624     query_table_alias   TEXT;
2625     from_alias_array    TEXT[] := '{}';
2626     used_ranks          TEXT[] := '{}';
2627     mb_field            INT;
2628     mb_field_list       INT[];
2629     search_org_list     INT[];
2630     select_clause       TEXT := 'SELECT';
2631     from_clause         TEXT := ' FROM  metabib.metarecord_source_map m JOIN metabib.rec_descriptor mrd ON (m.source = mrd.record) ';
2632     where_clause        TEXT := ' WHERE 1=1 ';
2633     mrd_used            BOOL := FALSE;
2634     sort_desc           BOOL := FALSE;
2635
2636     core_result         RECORD;
2637     core_cursor         REFCURSOR;
2638     core_rel_query      TEXT;
2639     vis_limit_query     TEXT;
2640     inner_where_clause  TEXT;
2641
2642     total_count         INT := 0;
2643     check_count         INT := 0;
2644     deleted_count       INT := 0;
2645     visible_count       INT := 0;
2646     excluded_count      INT := 0;
2647
2648 BEGIN
2649
2650     core_rel_limit := COALESCE( param_rel_limit, 25000 );
2651     core_chk_limit := COALESCE( param_chk_limit, 1000 );
2652     core_skip_chk := COALESCE( param_skip_chk, 1 );
2653
2654     IF metarecord THEN
2655         select_clause := select_clause || ' m.metarecord as id, array_accum(distinct m.source) as records,';
2656     ELSE
2657         select_clause := select_clause || ' m.source as id, array_accum(distinct m.source) as records,';
2658     END IF;
2659
2660     -- first we need to construct the base query
2661     FOR query_part IN SELECT * FROM search.parse_search_args(param_searches) WHERE term_type = 'fts_query' LOOP
2662
2663         inner_where_clause := 'index_vector @@ ' || query_part.term;
2664
2665         IF query_part.field_name IS NOT NULL THEN
2666
2667            SELECT  id INTO mb_field
2668              FROM  config.metabib_field
2669              WHERE field_class = query_part.field_class
2670                    AND name = query_part.field_name;
2671
2672             IF FOUND THEN
2673                 inner_where_clause := inner_where_clause ||
2674                     ' AND ' || 'field = ' || mb_field;
2675             END IF;
2676
2677         END IF;
2678
2679         -- moving on to the rank ...
2680         SELECT  * INTO query_part
2681           FROM  search.parse_search_args(param_searches)
2682           WHERE term_type = 'fts_rank'
2683                 AND table_alias = query_part.table_alias;
2684
2685         current_rank := query_part.term || ' * ' || query_part.table_alias || '_weight.weight';
2686
2687         IF query_part.field_name IS NOT NULL THEN
2688
2689            SELECT  array_accum(distinct id) INTO mb_field_list
2690              FROM  config.metabib_field
2691              WHERE field_class = query_part.field_class
2692                    AND name = query_part.field_name;
2693
2694         ELSE
2695
2696            SELECT  array_accum(distinct id) INTO mb_field_list
2697              FROM  config.metabib_field
2698              WHERE field_class = query_part.field_class;
2699
2700         END IF;
2701
2702         FOR rank_adjust IN SELECT * FROM search.relevance_adjustment WHERE active AND field IN ( SELECT * FROM search.explode_array( mb_field_list ) ) LOOP
2703
2704             IF NOT rank_adjust.bump_type = ANY (used_ranks) THEN
2705
2706                 IF rank_adjust.bump_type = 'first_word' THEN
2707                     SELECT  term INTO tmp_text
2708                       FROM  search.parse_search_args(param_searches)
2709                       WHERE table_alias = query_part.table_alias AND term_type = 'word'
2710                       ORDER BY id
2711                       LIMIT 1;
2712
2713                     tmp_text := query_part.table_alias || '.value ILIKE ' || quote_literal( tmp_text || '%' );
2714
2715                 ELSIF rank_adjust.bump_type = 'word_order' THEN
2716                     SELECT  array_to_string( array_accum( term ), '%' ) INTO tmp_text
2717                       FROM  search.parse_search_args(param_searches)
2718                       WHERE table_alias = query_part.table_alias AND term_type = 'word';
2719
2720                     tmp_text := query_part.table_alias || '.value ILIKE ' || quote_literal( '%' || tmp_text || '%' );
2721
2722                 ELSIF rank_adjust.bump_type = 'full_match' THEN
2723                     SELECT  array_to_string( array_accum( term ), E'\\s+' ) INTO tmp_text
2724                       FROM  search.parse_search_args(param_searches)
2725                       WHERE table_alias = query_part.table_alias AND term_type = 'word';
2726
2727                     tmp_text := query_part.table_alias || '.value  ~ ' || quote_literal( '^' || tmp_text || E'\\W*$' );
2728
2729                 END IF;
2730
2731
2732                 IF tmp_text IS NOT NULL THEN
2733                     current_rank := current_rank || ' * ( CASE WHEN ' || tmp_text ||
2734                         ' THEN ' || rank_adjust.multiplier || '::REAL ELSE 1.0 END )';
2735                 END IF;
2736
2737                 used_ranks := array_append( used_ranks, rank_adjust.bump_type );
2738
2739             END IF;
2740
2741         END LOOP;
2742
2743         ranks := array_append( ranks, current_rank );
2744         used_ranks := '{}';
2745
2746         FOR phrase_query_part IN
2747             SELECT  *
2748               FROM  search.parse_search_args(param_searches)
2749               WHERE term_type = 'phrase'
2750                     AND table_alias = query_part.table_alias LOOP
2751
2752             tmp_text := replace( phrase_query_part.term, '*', E'\\*' );
2753             tmp_text := replace( tmp_text, '?', E'\\?' );
2754             tmp_text := replace( tmp_text, '+', E'\\+' );
2755             tmp_text := replace( tmp_text, '|', E'\\|' );
2756             tmp_text := replace( tmp_text, '(', E'\\(' );
2757             tmp_text := replace( tmp_text, ')', E'\\)' );
2758             tmp_text := replace( tmp_text, '[', E'\\[' );
2759             tmp_text := replace( tmp_text, ']', E'\\]' );
2760
2761             inner_where_clause := inner_where_clause || ' AND ' || 'value  ~* ' || quote_literal( E'(^|\\W+)' || regexp_replace(tmp_text, E'\\s+',E'\\\\s+','g') || E'(\\W+|\$)' );
2762
2763         END LOOP;
2764
2765         query_table := search.pick_table(query_part.field_class);
2766
2767         from_clause := from_clause ||
2768             ' JOIN ( SELECT * FROM ' || query_table || ' WHERE ' || inner_where_clause ||
2769                     CASE WHEN core_rel_limit > 0 THEN ' LIMIT ' || core_rel_limit::TEXT ELSE '' END || ' ) AS ' || query_part.table_alias ||
2770                 ' ON ( m.source = ' || query_part.table_alias || '.source )' ||
2771             ' JOIN config.metabib_field AS ' || query_part.table_alias || '_weight' ||
2772                 ' ON ( ' || query_part.table_alias || '.field = ' || query_part.table_alias || '_weight.id  AND  ' || query_part.table_alias || '_weight.search_field)';
2773
2774         from_alias_array := array_append(from_alias_array, query_part.table_alias);
2775
2776     END LOOP;
2777
2778     IF param_pref_lang IS NOT NULL AND param_pref_lang_multiplier IS NOT NULL THEN
2779         current_rank := ' CASE WHEN mrd.item_lang = ' || quote_literal( param_pref_lang ) ||
2780             ' THEN ' || param_pref_lang_multiplier || '::REAL ELSE 1.0 END ';
2781
2782         -- ranks := array_append( ranks, current_rank );
2783     END IF;
2784
2785     current_rank := ' AVG( ( (' || array_to_string( ranks, ') + (' ) || ') ) * ' || current_rank || ' ) ';
2786     select_clause := select_clause || current_rank || ' AS rel,';
2787
2788     sort_desc = param_sort_desc;
2789
2790     IF param_sort = 'pubdate' THEN
2791
2792         tmp_text := '999999';
2793         IF param_sort_desc THEN tmp_text := '0'; END IF;
2794
2795         current_rank := $$ COALESCE( FIRST(NULLIF(REGEXP_REPLACE(mrd.date1, E'\\D+', '9', 'g'),'')), $$ || quote_literal(tmp_text) || $$ )::INT $$;
2796
2797     ELSIF param_sort = 'title' THEN
2798
2799         tmp_text := 'zzzzzz';
2800         IF param_sort_desc THEN tmp_text := '    '; END IF;
2801
2802         current_rank := $$
2803             ( COALESCE( FIRST ((
2804                 SELECT  LTRIM(SUBSTR( frt.value, COALESCE(SUBSTRING(frt.ind2 FROM E'\\d+'),'0')::INT + 1 ))
2805                   FROM  metabib.full_rec frt
2806                   WHERE frt.record = m.source
2807                     AND frt.tag = '245'
2808                     AND frt.subfield = 'a'
2809                   LIMIT 1
2810             )),$$ || quote_literal(tmp_text) || $$))
2811         $$;
2812
2813     ELSIF param_sort = 'author' THEN
2814
2815         tmp_text := 'zzzzzz';
2816         IF param_sort_desc THEN tmp_text := '    '; END IF;
2817
2818         current_rank := $$
2819             ( COALESCE( FIRST ((
2820                 SELECT  LTRIM(fra.value)
2821                   FROM  metabib.full_rec fra
2822                   WHERE fra.record = m.source
2823                     AND fra.tag LIKE '1%'
2824                     AND fra.subfield = 'a'
2825                   ORDER BY fra.tag::text::int
2826                   LIMIT 1
2827             )),$$ || quote_literal(tmp_text) || $$))
2828         $$;
2829
2830     ELSIF param_sort = 'create_date' THEN
2831             current_rank := $$( FIRST (( SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source)) )$$;
2832     ELSIF param_sort = 'edit_date' THEN
2833             current_rank := $$( FIRST (( SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source)) )$$;
2834     ELSE
2835         sort_desc := NOT COALESCE(param_sort_desc, FALSE);
2836     END IF;
2837
2838     select_clause := select_clause || current_rank || ' AS rank';
2839
2840     -- now add the other qualifiers
2841     IF param_audience IS NOT NULL AND array_upper(param_audience, 1) > 0 THEN
2842         where_clause = where_clause || $$ AND mrd.audience IN ('$$ || array_to_string(param_audience, $$','$$) || $$') $$;
2843     END IF;
2844
2845     IF param_language IS NOT NULL AND array_upper(param_language, 1) > 0 THEN
2846         where_clause = where_clause || $$ AND mrd.item_lang IN ('$$ || array_to_string(param_language, $$','$$) || $$') $$;
2847     END IF;
2848
2849     IF param_lit_form IS NOT NULL AND array_upper(param_lit_form, 1) > 0 THEN
2850         where_clause = where_clause || $$ AND mrd.lit_form IN ('$$ || array_to_string(param_lit_form, $$','$$) || $$') $$;
2851     END IF;
2852
2853     IF param_types IS NOT NULL AND array_upper(param_types, 1) > 0 THEN
2854         where_clause = where_clause || $$ AND mrd.item_type IN ('$$ || array_to_string(param_types, $$','$$) || $$') $$;
2855     END IF;
2856
2857     IF param_forms IS NOT NULL AND array_upper(param_forms, 1) > 0 THEN
2858         where_clause = where_clause || $$ AND mrd.item_form IN ('$$ || array_to_string(param_forms, $$','$$) || $$') $$;
2859     END IF;
2860
2861     IF param_vformats IS NOT NULL AND array_upper(param_vformats, 1) > 0 THEN
2862         where_clause = where_clause || $$ AND mrd.vr_format IN ('$$ || array_to_string(param_vformats, $$','$$) || $$') $$;
2863     END IF;
2864
2865     IF param_bib_level IS NOT NULL AND array_upper(param_bib_level, 1) > 0 THEN
2866         where_clause = where_clause || $$ AND mrd.bib_level IN ('$$ || array_to_string(param_bib_level, $$','$$) || $$') $$;
2867     END IF;
2868
2869     IF param_before IS NOT NULL AND param_before <> '' THEN
2870         where_clause = where_clause || $$ AND mrd.date1 <= $$ || quote_literal(param_before) || ' ';
2871     END IF;
2872
2873     IF param_after IS NOT NULL AND param_after <> '' THEN
2874         where_clause = where_clause || $$ AND mrd.date1 >= $$ || quote_literal(param_after) || ' ';
2875     END IF;
2876
2877     IF param_during IS NOT NULL AND param_during <> '' THEN
2878         where_clause = where_clause || $$ AND $$ || quote_literal(param_during) || $$ BETWEEN mrd.date1 AND mrd.date2 $$;
2879     END IF;
2880
2881     IF param_between IS NOT NULL AND array_upper(param_between, 1) > 1 THEN
2882         where_clause = where_clause || $$ AND mrd.date1 BETWEEN '$$ || array_to_string(param_between, $$' AND '$$) || $$' $$;
2883     END IF;
2884
2885     core_rel_query := select_clause || from_clause || where_clause ||
2886                         ' GROUP BY 1 ORDER BY 4' || CASE WHEN sort_desc THEN ' DESC' ELSE ' ASC' END || ';';
2887     --RAISE NOTICE 'Base Query:  %', core_rel_query;
2888
2889     IF param_search_ou > 0 THEN
2890         IF param_depth IS NOT NULL THEN
2891             SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth );
2892         ELSE
2893             SELECT array_accum(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou );
2894         END IF;
2895     ELSIF param_search_ou < 0 THEN
2896         SELECT array_accum(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou;
2897     ELSIF param_search_ou = 0 THEN
2898         -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure.
2899     END IF;
2900
2901     OPEN core_cursor FOR EXECUTE core_rel_query;
2902
2903     LOOP
2904
2905         FETCH core_cursor INTO core_result;
2906         EXIT WHEN NOT FOUND;
2907
2908
2909         IF total_count % 1000 = 0 THEN
2910             -- RAISE NOTICE ' % total, % checked so far ... ', total_count, check_count;
2911         END IF;
2912
2913         IF core_chk_limit > 0 AND total_count - core_skip_chk + 1 >= core_chk_limit THEN
2914             total_count := total_count + 1;
2915             CONTINUE;
2916         END IF;
2917
2918         total_count := total_count + 1;
2919
2920         CONTINUE WHEN param_skip_chk IS NOT NULL and total_count < param_skip_chk;
2921
2922         check_count := check_count + 1;
2923
2924         PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM search.explode_array( core_result.records ) );
2925         IF NOT FOUND THEN
2926             -- RAISE NOTICE ' % were all deleted ... ', core_result.records;
2927             deleted_count := deleted_count + 1;
2928             CONTINUE;
2929         END IF;
2930
2931         PERFORM 1
2932           FROM  biblio.record_entry b
2933                 JOIN config.bib_source s ON (b.source = s.id)
2934           WHERE s.transcendant
2935                 AND b.id IN ( SELECT * FROM search.explode_array( core_result.records ) );
2936
2937         IF FOUND THEN
2938             -- RAISE NOTICE ' % were all transcendant ... ', core_result.records;
2939             visible_count := visible_count + 1;
2940
2941             current_res.id = core_result.id;
2942             current_res.rel = core_result.rel;
2943
2944             tmp_int := 1;
2945             IF metarecord THEN
2946                 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
2947             END IF;
2948
2949             IF tmp_int = 1 THEN
2950                 current_res.record = core_result.records[1];
2951             ELSE
2952                 current_res.record = NULL;
2953             END IF;
2954
2955             RETURN NEXT current_res;
2956
2957             CONTINUE;
2958         END IF;
2959
2960         PERFORM 1
2961           FROM  asset.call_number cn
2962                 JOIN asset.uri_call_number_map map ON (map.call_number = cn.id)
2963                 JOIN asset.uri uri ON (map.uri = uri.id)
2964           WHERE NOT cn.deleted
2965                 AND cn.label = '##URI##'
2966                 AND uri.active
2967                 AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL )
2968                 AND cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
2969                 AND cn.owning_lib IN ( SELECT * FROM search.explode_array( search_org_list ) )
2970           LIMIT 1;
2971
2972         IF FOUND THEN
2973             -- RAISE NOTICE ' % have at least one URI ... ', core_result.records;
2974             visible_count := visible_count + 1;
2975
2976             current_res.id = core_result.id;
2977             current_res.rel = core_result.rel;
2978
2979             tmp_int := 1;
2980             IF metarecord THEN
2981                 SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
2982             END IF;
2983
2984             IF tmp_int = 1 THEN
2985                 current_res.record = core_result.records[1];
2986             ELSE
2987                 current_res.record = NULL;
2988             END IF;
2989
2990             RETURN NEXT current_res;
2991
2992             CONTINUE;
2993         END IF;
2994
2995         IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN
2996
2997             PERFORM 1
2998               FROM  asset.call_number cn
2999                     JOIN asset.copy cp ON (cp.call_number = cn.id)
3000               WHERE NOT cn.deleted
3001                     AND NOT cp.deleted
3002                     AND cp.status IN ( SELECT * FROM search.explode_array( param_statuses ) )
3003                     AND cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
3004                     AND cp.circ_lib IN ( SELECT * FROM search.explode_array( search_org_list ) )
3005               LIMIT 1;
3006
3007             IF NOT FOUND THEN
3008                 -- RAISE NOTICE ' % were all status-excluded ... ', core_result.records;
3009                 excluded_count := excluded_count + 1;
3010                 CONTINUE;
3011             END IF;
3012
3013         END IF;
3014
3015         IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN
3016
3017             PERFORM 1
3018               FROM  asset.call_number cn
3019                     JOIN asset.copy cp ON (cp.call_number = cn.id)
3020               WHERE NOT cn.deleted
3021                     AND NOT cp.deleted
3022                     AND cp.location IN ( SELECT * FROM search.explode_array( param_locations ) )
3023                     AND cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
3024                     AND cp.circ_lib IN ( SELECT * FROM search.explode_array( search_org_list ) )
3025               LIMIT 1;
3026
3027             IF NOT FOUND THEN
3028                 -- RAISE NOTICE ' % were all copy_location-excluded ... ', core_result.records;
3029                 excluded_count := excluded_count + 1;
3030                 CONTINUE;
3031             END IF;
3032
3033         END IF;
3034
3035         IF staff IS NULL OR NOT staff THEN
3036
3037             PERFORM 1
3038               FROM  asset.call_number cn
3039                     JOIN asset.copy cp ON (cp.call_number = cn.id)
3040                     JOIN actor.org_unit a ON (cp.circ_lib = a.id)
3041                     JOIN asset.copy_location cl ON (cp.location = cl.id)
3042                     JOIN config.copy_status cs ON (cp.status = cs.id)
3043               WHERE NOT cn.deleted
3044                     AND NOT cp.deleted
3045                     AND cs.opac_visible
3046                     AND cl.opac_visible
3047                     AND cp.opac_visible
3048                     AND a.opac_visible
3049                     AND cp.circ_lib IN ( SELECT * FROM search.explode_array( search_org_list ) )
3050                     AND cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
3051               LIMIT 1;
3052
3053             IF NOT FOUND THEN
3054                 -- RAISE NOTICE ' % were all visibility-excluded ... ', core_result.records;
3055                 excluded_count := excluded_count + 1;
3056                 CONTINUE;
3057             END IF;
3058
3059         ELSE
3060
3061             PERFORM 1
3062               FROM  asset.call_number cn
3063                     JOIN asset.copy cp ON (cp.call_number = cn.id)
3064                     JOIN actor.org_unit a ON (cp.circ_lib = a.id)
3065                     JOIN asset.copy_location cl ON (cp.location = cl.id)
3066               WHERE NOT cn.deleted
3067                     AND NOT cp.deleted
3068                     AND cp.circ_lib IN ( SELECT * FROM search.explode_array( search_org_list ) )
3069                     AND cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
3070               LIMIT 1;
3071
3072             IF NOT FOUND THEN
3073
3074                 PERFORM 1
3075                   FROM  asset.call_number cn
3076                   WHERE cn.record IN ( SELECT * FROM search.explode_array( core_result.records ) )
3077                   LIMIT 1;
3078
3079                 IF FOUND THEN
3080                     -- RAISE NOTICE ' % were all visibility-excluded ... ', core_result.records;
3081                     excluded_count := excluded_count + 1;
3082                     CONTINUE;
3083                 END IF;
3084
3085             END IF;
3086
3087         END IF;
3088
3089         visible_count := visible_count + 1;
3090
3091         current_res.id = core_result.id;
3092         current_res.rel = core_result.rel;
3093
3094         tmp_int := 1;
3095         IF metarecord THEN
3096             SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id;
3097         END IF;
3098
3099         IF tmp_int = 1 THEN
3100             current_res.record = core_result.records[1];
3101         ELSE
3102             current_res.record = NULL;
3103         END IF;
3104
3105         RETURN NEXT current_res;
3106
3107         IF visible_count % 1000 = 0 THEN
3108             -- RAISE NOTICE ' % visible so far ... ', visible_count;
3109         END IF;
3110
3111     END LOOP;
3112
3113     current_res.id = NULL;
3114     current_res.rel = NULL;
3115     current_res.record = NULL;
3116     current_res.total = total_count;
3117     current_res.checked = check_count;
3118     current_res.deleted = deleted_count;
3119     current_res.visible = visible_count;
3120     current_res.excluded = excluded_count;
3121
3122     CLOSE core_cursor;
3123
3124     RETURN NEXT current_res;
3125
3126 END;
3127 $func$ LANGUAGE PLPGSQL;
3128
3129
3130 CREATE TABLE config.idl_field_doc (
3131     id              BIGSERIAL   PRIMARY KEY,
3132     fm_class        TEXT        NOT NULL,
3133     field           TEXT        NOT NULL,
3134     owner           INT         NOT NULL    REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
3135     string          TEXT        NOT NULL
3136 );
3137 CREATE UNIQUE INDEX idl_field_doc_identity ON config.idl_field_doc (fm_class,field,owner);
3138
3139
3140 INSERT INTO config.xml_transform VALUES ( 'mods33', 'http://www.loc.gov/mods/v3', 'mods33', '');
3141  
3142 INSERT INTO container.copy_bucket_type (code,label) VALUES ('misc', 'Miscellaneous');
3143 INSERT INTO container.copy_bucket_type (code,label) VALUES ('staff_client', 'General Staff Client container');
3144 INSERT INTO container.call_number_bucket_type (code,label) VALUES ('misc', 'Miscellaneous');
3145 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('misc', 'Miscellaneous');
3146 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('staff_client', 'General Staff Client container');
3147 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('bookbag', 'Book Bag');
3148 INSERT INTO container.biblio_record_entry_bucket_type (code,label) VALUES ('reading_list', 'Reading List');
3149
3150 INSERT INTO container.user_bucket_type (code,label) VALUES ('misc', 'Miscellaneous');
3151 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks', 'Friends');
3152 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:pub_book_bags.view', 'List Published Book Bags');
3153 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:pub_book_bags.add', 'Add to Published Book Bags');
3154 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:circ.view', 'View Circulations');
3155 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:circ.renew', 'Renew Circulations');
3156 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:circ.checkout', 'Checkout Items');
3157 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:hold.view', 'View Holds');
3158 INSERT INTO container.user_bucket_type (code,label) VALUES ('folks:hold.cancel', 'Cancel Holds');
3159
3160
3161
3162 CREATE SCHEMA action_trigger;
3163
3164 CREATE TABLE action_trigger.hook (
3165     key         TEXT    PRIMARY KEY,
3166     core_type   TEXT    NOT NULL,
3167     description TEXT,
3168     passive     BOOL    NOT NULL DEFAULT FALSE
3169 );
3170 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkout','circ','Item checked out to user');
3171 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkin','circ','Item checked in');
3172 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost','circ','Circulating Item marked Lost');
3173 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost.found','circ','Lost Circulating Item checked in');
3174 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost.auto','circ','Circulating Item automatically marked lost');
3175 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('claims_returned','circ','Circulating Item marked Claims Returned');
3176 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('claims_returned.found','circ','Claims Returned Circulating Item is checked in');
3177 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('missing','acp','Item marked Missing');
3178 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('missing.found','acp','Missing Item checked in');
3179 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('transit.start','acp','An Item is placed into transit');
3180 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('transit.finish','acp','An Item is received from a transit');
3181 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_request.success','ahr','A hold is succefully placed');
3182 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_request.failure','ahr','A hold is attempted by not succefully placed');
3183 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold.capture','ahr','A targeted Item is captured for a hold');
3184 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold.available','ahr','A held item is ready for pickup');
3185 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_transit.start','ahtc','A hold-captured Item is placed into transit');
3186 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_transit.finish','ahtc','A hold-captured Item is received from a transit');
3187 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('checkout.due','circ','Checked out Item is Due',TRUE);
3188 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_FINES','ausp','Patron has exceeded allowed fines',TRUE);
3189 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_OVERDUE_COUNT','ausp','Patron has exceeded allowed overdue count',TRUE);
3190 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_CHECKOUT_COUNT','ausp','Patron has exceeded allowed checkout count',TRUE);
3191 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_COLLECTIONS_WARNING','ausp','Patron has exceeded maximum fine amount for collections department warning',TRUE);
3192 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('format.po.jedi','acqpo','Formats a Purchase Order as a JEDI document',TRUE);
3193 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('format.po.html','acqpo','Formats a Purchase Order as an HTML document',TRUE);
3194 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('format.po.pdf','acqpo','Formats a Purchase Order as a PDF document',TRUE);
3195 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('damaged','acp','Item marked damaged');
3196 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkout.damaged','circ','A circulating item is marked damaged and the patron is fined');
3197 -- and much more, I'm sure
3198
3199 -- Specialized collection modules.  Given an FM object, gather some info and return a scalar or ref.
3200 CREATE TABLE action_trigger.collector (
3201     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Collector:: namespace
3202     description TEXT
3203 );
3204 INSERT INTO action_trigger.collector (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
3205 --INSERT INTO action_trigger.collector (module,description) VALUES ('CircCountsByCircMod','Count of Circulations for a User, broken down by circulation modifier');
3206
3207 -- Simple tests on an FM object from hook.core_type to test for "should we still do this."
3208 CREATE TABLE action_trigger.validator (
3209     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Validator:: namespace
3210     description TEXT
3211 );
3212 INSERT INTO action_trigger.validator (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
3213 INSERT INTO action_trigger.validator (module,description) VALUES ('NOOP_True','Always returns true -- validation always passes');
3214 INSERT INTO action_trigger.validator (module,description) VALUES ('NOOP_False','Always returns false -- validation always fails');
3215 INSERT INTO action_trigger.validator (module,description) VALUES ('CircIsOpen','Check that the circulation is still open');
3216 INSERT INTO action_trigger.validator (module,description) VALUES ('HoldIsAvailable','Check that an item is on the hold shelf');
3217 INSERT INTO action_trigger.validator (module,description) VALUES ('CircIsOverdue','Check that the circulation is overdue');
3218
3219 -- After an event passes validation (action_trigger.validator), the reactor processes it.
3220 CREATE TABLE action_trigger.reactor (
3221     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Reactor:: namespace
3222     description TEXT
3223 );
3224 INSERT INTO action_trigger.reactor (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
3225 INSERT INTO action_trigger.reactor (module,description) VALUES ('NOOP_True','Always returns true -- reaction always passes');
3226 INSERT INTO action_trigger.reactor (module,description) VALUES ('NOOP_False','Always returns false -- reaction always fails');
3227 INSERT INTO action_trigger.reactor (module,description) VALUES ('SendEmail','Send an email based on a user-defined template');
3228 INSERT INTO action_trigger.reactor (module,description) VALUES ('GenerateBatchOverduePDF','Output a batch PDF of overdue notices for printing');
3229 INSERT INTO action_trigger.reactor (module,description) VALUES ('MarkItemLost','Marks a circulation and associated item as lost');
3230 INSERT INTO action_trigger.reactor (module,description) VALUES ('ApplyCircFee','Applies a billing with a pre-defined amount to a circulation');
3231 INSERT INTO action_trigger.reactor (module,description) VALUES ('ProcessTemplate', 'Processes the configured template');
3232
3233 -- After an event is reacted to (either succes or failure) a cleanup module is run against the resulting environment
3234 CREATE TABLE action_trigger.cleanup (
3235     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Cleanup:: namespace
3236     description TEXT
3237 );
3238 INSERT INTO action_trigger.cleanup (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
3239 INSERT INTO action_trigger.cleanup (module,description) VALUES ('NOOP_True','Always returns true -- cleanup always passes');
3240 INSERT INTO action_trigger.cleanup (module,description) VALUES ('NOOP_False','Always returns false -- cleanup always fails');
3241 INSERT INTO action_trigger.cleanup (module,description) VALUES ('ClearAllPending','Remove all future, pending notifications for this target');
3242
3243 CREATE TABLE action_trigger.event_definition (
3244     id              SERIAL      PRIMARY KEY,
3245     active          BOOL        NOT NULL DEFAULT TRUE,
3246     owner           INT         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
3247     name            TEXT        NOT NULL,
3248     hook            TEXT        NOT NULL REFERENCES action_trigger.hook (key) DEFERRABLE INITIALLY DEFERRED,
3249     validator       TEXT        NOT NULL REFERENCES action_trigger.validator (module) DEFERRABLE INITIALLY DEFERRED,
3250     reactor         TEXT        NOT NULL REFERENCES action_trigger.reactor (module) DEFERRABLE INITIALLY DEFERRED,
3251     cleanup_success TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
3252     cleanup_failure TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
3253     delay           INTERVAL    NOT NULL DEFAULT '5 minutes',
3254     delay_field     TEXT,                 -- for instance, xact_start on a circ hook ... look for fields on hook.core_type where datatype=timestamp? If not set, delay from now()
3255     group_field     TEXT,                 -- field from this.hook.core_type to batch event targets together on, fed into reactor a group at a time.
3256     template        TEXT,                 -- the TT block.  will have an 'environment' hash (or array of hashes, grouped events) built up by validator and collector(s), which can be modified.
3257     CONSTRAINT ev_def_owner_hook_val_react_clean_delay_once UNIQUE (owner, hook, validator, reactor, delay, delay_field),
3258     CONSTRAINT ev_def_name_owner_once UNIQUE (owner, name)
3259 );
3260
3261 CREATE TABLE action_trigger.environment (
3262     id          SERIAL  PRIMARY KEY,
3263     event_def   INT     NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
3264     path        TEXT,       -- fields to flesh. given a hook with a core_type of circ, imagine circ_lib.parent_ou expanding to
3265                             -- {flesh: 2, flesh_fields: {circ: ['circ_lib'], aou: ['parent_ou']}} ... default is to flesh all
3266                             -- at flesh depth 1
3267     collector   TEXT    REFERENCES action_trigger.collector (module) DEFERRABLE INITIALLY DEFERRED, -- if set, given the object at 'path', return some data
3268                                                                       -- to be stashed at environment.<label>
3269     label       TEXT    CHECK (label NOT IN ('result','target','event')),
3270     CONSTRAINT env_event_label_once UNIQUE (event_def,label)
3271 );
3272
3273 CREATE TABLE action_trigger.event_output (
3274     id              BIGSERIAL   PRIMARY KEY,
3275     create_time     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
3276     is_error        BOOLEAN     NOT NULL DEFAULT FALSE,
3277     data            TEXT        NOT NULL
3278 );
3279
3280 CREATE TABLE action_trigger.event (
3281     id              BIGSERIAL   PRIMARY KEY,
3282     target          BIGINT      NOT NULL, -- points at the id from class defined by event_def.hook.core_type
3283     event_def       INT         REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
3284     add_time        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
3285     run_time        TIMESTAMPTZ NOT NULL,
3286     start_time      TIMESTAMPTZ,
3287     update_time     TIMESTAMPTZ,
3288     complete_time   TIMESTAMPTZ,
3289     update_process  INT,
3290     state           TEXT        NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','invalid','found','collecting','collected','validating','valid','reacting','reacted','cleaning','complete','error')),
3291     template_output BIGINT      REFERENCES action_trigger.event_output (id),
3292     error_output    BIGINT      REFERENCES action_trigger.event_output (id)
3293 );
3294
3295 CREATE TABLE action_trigger.event_params (
3296     id          BIGSERIAL   PRIMARY KEY,
3297     event_def   INT         NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
3298     param       TEXT        NOT NULL, -- the key under environment.event.params to store the output of ...
3299     value       TEXT        NOT NULL, -- ... the eval() output of this.  Has access to environment (and, well, all of perl)
3300     CONSTRAINT event_params_event_def_param_once UNIQUE (event_def,param)
3301 );
3302
3303
3304
3305 -- Trigger Event Definitions -------------------------------------------------
3306
3307 -- Sample Overdue Notice --
3308
3309 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template) 
3310     VALUES (1, 'f', 1, '7 Day Overdue Email Notification', 'checkout.due', 'CircIsOverdue', 'SendEmail', '7 days', 'due_date', 'usr', 
3311 $$
3312 [%- USE date -%]
3313 [%- user = target.0.usr -%]
3314 To: [%- params.recipient_email || user.email %]
3315 From: [%- params.sender_email || default_sender %]
3316 Subject: Overdue Notification
3317
3318 Dear [% user.family_name %], [% user.first_given_name %]
3319 Our records indicate the following items are overdue.
3320
3321 [% FOR circ IN target %]
3322     Title: [% circ.target_copy.call_number.record.simple_record.title %] 
3323     Barcode: [% circ.target_copy.barcode %] 
3324     Due: [% date.format(helpers.format_date(circ.due_date), '%Y-%m-%d') %]
3325     Item Cost: [% helpers.get_copy_price(circ.target_copy) %]
3326     Total Owed For Transaction: [% circ.billable_transaction.summary.total_owed %]
3327     Library: [% circ.circ_lib.name %]
3328 [% END %]
3329
3330 $$);
3331
3332 INSERT INTO action_trigger.environment (event_def, path) VALUES 
3333     (1, 'target_copy.call_number.record.simple_record'),
3334     (1, 'usr'),
3335     (1, 'billable_transaction.summary'),
3336     (1, 'circ_lib.billing_address');
3337   
3338
3339 -- Sample Mark Long-Overdue Item Lost --
3340
3341 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field) 
3342     VALUES (2, 'f', 1, '90 Day Overdue Mark Lost', 'checkout.due', 'CircIsOverdue', 'MarkItemLost', '90 days', 'due_date');
3343
3344 -- Sample Auto Mark Lost Notice --
3345
3346 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, group_field, template) 
3347     VALUES (3, 'f', 1, '90 Day Overdue Mark Lost Notice', 'lost.auto', 'NOOP_True', 'SendEmail', 'usr',
3348 $$
3349 [%- USE date -%]
3350 [%- user = target.0.usr -%]
3351 To: [%- params.recipient_email || user.email %]
3352 From: [%- params.sender_email || default_sender %]
3353 Subject: Overdue Items Marked Lost
3354
3355 Dear [% user.family_name %], [% user.first_given_name %]
3356 The following items are 90 days overdue and have been marked LOST.
3357
3358 [% FOR circ IN target %]
3359     Title: [% circ.target_copy.call_number.record.simple_record.title %] 
3360     Barcode: [% circ.target_copy.barcode %] 
3361     Due: [% date.format(helpers.format_date(circ.due_date), '%Y-%m-%d') %]
3362     Item Cost: [% helpers.get_copy_price(circ.target_copy) %]
3363     Total Owed For Transaction: [% circ.billable_transaction.summary.total_owed %]
3364     Library: [% circ.circ_lib.name %]
3365 [% END %]
3366
3367 $$);
3368
3369
3370 INSERT INTO action_trigger.environment (event_def, path) VALUES 
3371     (3, 'target_copy.call_number.record.simple_record'),
3372     (3, 'usr'),
3373     (3, 'billable_transaction.summary'),
3374     (3, 'circ_lib.billing_address');
3375
3376 -- Sample Purchase Order HTML Template --
3377
3378 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, template) 
3379     VALUES (4, 't', 1, 'PO HTML', 'format.po.html', 'NOOP_True', 'ProcessTemplate', 
3380 $$
3381 [%- USE date -%]
3382 [%-
3383     # find a lineitem attribute by name and optional type
3384     BLOCK get_li_attr;
3385         FOR attr IN li.attributes;
3386             IF attr.attr_name == attr_name;
3387                 IF !attr_type OR attr_type == attr.attr_type;
3388                     attr.attr_value;
3389                     LAST;
3390                 END;
3391             END;
3392         END;
3393     END
3394 -%]
3395
3396 <h2>Purchase Order [% target.id %]</h2>
3397 <br/>
3398 date <b>[% date.format(date.now, '%Y%m%d') %]</b>
3399 <br/>
3400
3401 <style>
3402     table td { padding:5px; border:1px solid #aaa;}
3403     table { width:95%; border-collapse:collapse; }
3404 </style>
3405 <table id='vendor-table'>
3406   <tr>
3407     <td valign='top'>Vendor</td>
3408     <td>
3409       <div>[% target.provider.name %]</div>
3410       <div>[% target.provider.addresses.0.street1 %]</div>
3411       <div>[% target.provider.addresses.0.street2 %]</div>
3412       <div>[% target.provider.addresses.0.city %]</div>
3413       <div>[% target.provider.addresses.0.state %]</div>
3414       <div>[% target.provider.addresses.0.country %]</div>
3415       <div>[% target.provider.addresses.0.post_code %]</div>
3416     </td>
3417     <td valign='top'>Ship to / Bill to</td>
3418     <td>
3419       <div>[% target.ordering_agency.name %]</div>
3420       <div>[% target.ordering_agency.billing_address.street1 %]</div>
3421       <div>[% target.ordering_agency.billing_address.street2 %]</div>
3422       <div>[% target.ordering_agency.billing_address.city %]</div>
3423       <div>[% target.ordering_agency.billing_address.state %]</div>
3424       <div>[% target.ordering_agency.billing_address.country %]</div>
3425       <div>[% target.ordering_agency.billing_address.post_code %]</div>
3426     </td>
3427   </tr>
3428 </table>
3429
3430 <br/><br/><br/>
3431
3432 <table>
3433   <thead>
3434     <tr>
3435       <th>PO#</th>
3436       <th>ISBN or Item #</th>
3437       <th>Title</th>
3438       <th>Quantity</th>
3439       <th>Unit Price</th>
3440       <th>Line Total</th>
3441     </tr>
3442   </thead>
3443   <tbody>
3444
3445   [% subtotal = 0 %]
3446   [% FOR li IN target.lineitems %]
3447
3448   <tr>
3449     [% count = li.lineitem_details.size %]
3450     [% price = PROCESS get_li_attr attr_name = 'estimated_price' %]
3451     [% litotal = (price * count) %]
3452     [% subtotal = subtotal + litotal %]
3453     [% isbn = PROCESS get_li_attr attr_name = 'isbn' %]
3454     [% ident = PROCESS get_li_attr attr_name = 'identifier' %]
3455
3456     <td>[% target.id %]</td>
3457     <td>[% isbn || ident %]</td>
3458     <td>[% PROCESS get_li_attr attr_name = 'title' %]</td>
3459     <td>[% count %]</td>
3460     <td>[% price %]</td>
3461     <td>[% litotal %]</td>
3462   </tr>
3463   [% END %]
3464   <tr>
3465     <td/><td/><td/><td/>
3466     <td>Sub Total</td>
3467     <td>[% subtotal %]</td>
3468   </tr>
3469   </tbody>
3470 </table>
3471
3472 <br/>
3473
3474 Total Line Item Count: [% target.lineitems.size %]
3475 $$);
3476
3477 INSERT INTO action_trigger.environment (event_def, path) VALUES 
3478     (4, 'lineitems.lineitem_details.fund'),
3479     (4, 'lineitems.lineitem_details.location'),
3480     (4, 'lineitems.lineitem_details.owning_lib'),
3481     (4, 'ordering_agency.mailing_address'),
3482     (4, 'ordering_agency.billing_address'),
3483     (4, 'provider.addresses'),
3484     (4, 'lineitems.attributes');
3485
3486 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template)
3487     VALUES (5, 'f', 1, 'Hold Ready for Pickup Email Notification', 'hold.available', 'HoldIsAvailable', 'SendEmail', '30 minutes', 'capture_time', 'usr',
3488 $$
3489 [%- USE date -%]
3490 [%- user = target.0.usr -%]
3491 To: [%- params.recipient_email || user.email %]
3492 From: [%- params.sender_email || default_sender %]
3493 Subject: Hold Available Notification
3494
3495 Dear [% user.family_name %], [% user.first_given_name %]
3496 The item(s) you requested are available for pickup from the Library.
3497
3498 [% FOR hold IN target %]
3499     Title: [% hold.current_copy.call_number.record.simple_record.title %]
3500     Author: [% hold.current_copy.call_number.record.simple_record.author %]
3501     Call Number: [% hold.current_copy.call_number.label %]
3502     Barcode: [% hold.current_copy.barcode %]
3503     Library: [% hold.pickup_lib.name %]
3504 [% END %]
3505
3506 $$);
3507
3508 INSERT INTO action_trigger.environment (event_def, path) VALUES
3509     (5, 'current_copy.call_number.record.simple_record'),
3510     (5, 'usr'),
3511     (5, 'pickup_lib.billing_address');
3512
3513
3514 SELECT SETVAL('action_trigger.event_definition_id_seq'::TEXT, 100);
3515
3516
3517 CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$
3518 DECLARE
3519     moved_objects INT := 0;
3520     source_cn     asset.call_number%ROWTYPE;
3521     target_cn     asset.call_number%ROWTYPE;
3522     metarec       metabib.metarecord%ROWTYPE;
3523     hold          action.hold_request%ROWTYPE;
3524     ser_rec       serial.record_entry%ROWTYPE;
3525     uri_count     INT := 0;
3526     counter       INT := 0;
3527     uri_datafield TEXT;
3528     uri_text      TEXT := '';
3529 BEGIN
3530
3531     -- move any 856 entries on records that have at least one MARC-mapped URI entry
3532     SELECT  INTO uri_count COUNT(*)
3533       FROM  asset.uri_call_number_map m
3534             JOIN asset.call_number cn ON (m.call_number = cn.id)
3535       WHERE cn.record = source_record;
3536
3537     IF uri_count > 0 THEN
3538
3539         SELECT  COUNT(*) INTO counter
3540           FROM  xpath_table(
3541                     'id',
3542                     'marc',
3543                     'acq.lineitem',
3544                     '//*[@tag="856"]',
3545                     'id=' || lineitem
3546                 ) as t(i int,c text);
3547
3548         FOR i IN 1 .. counter LOOP
3549             SELECT  '<datafield xmlns="http://www.loc.gov/MARC21/slim" tag="856">' ||
3550                         array_to_string(
3551                             array_accum(
3552                                 '<subfield code="' || subfield || '">' ||
3553                                 regexp_replace(
3554                                     regexp_replace(
3555                                         regexp_replace(data,'&','&amp;','g'),
3556                                         '>', '&gt;', 'g'
3557                                     ),
3558                                     '<', '&lt;', 'g'
3559                                 ) || '</subfield>'
3560                             ), ''
3561                         ) || '</datafield>' INTO uri_datafield
3562               FROM  xpath_table(
3563                         'id',
3564                         'marc',
3565                         'biblio.record_entry',
3566                         '//*[@tag="856"][position()=' || i || ']/*/@code|' ||
3567                         '//*[@tag="856"][position()=' || i || ']/*[@code]',
3568                         'id=' || source_record
3569                     ) as t(id int,subfield text,data text);
3570
3571             uri_text := uri_text || uri_datafield;
3572         END LOOP;
3573
3574         IF uri_text <> '' THEN
3575             UPDATE  biblio.record_entry
3576               SET   marc = regexp_replace(marc,'(</[^>]*record>)', uri_text || E'\\1')
3577               WHERE id = target_record;
3578         END IF;
3579
3580     END IF;
3581
3582     -- Find and move metarecords to the target record
3583     SELECT  INTO metarec *
3584       FROM  metabib.metarecord
3585       WHERE master_record = source_record;
3586
3587     IF FOUND THEN
3588         UPDATE  metabib.metarecord
3589           SET   master_record = target_record,
3590             mods = NULL
3591           WHERE id = metarec.id;
3592
3593         moved_objects := moved_objects + 1;
3594     END IF;
3595
3596     -- Find call numbers attached to the source ...
3597     FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP
3598
3599         SELECT  INTO target_cn *
3600           FROM  asset.call_number
3601           WHERE label = source_cn.label
3602             AND owning_lib = source_cn.owning_lib
3603             AND record = target_record;
3604
3605         -- ... and if there's a conflicting one on the target ...
3606         IF FOUND THEN
3607
3608             -- ... move the copies to that, and ...
3609             UPDATE  asset.copy
3610               SET   call_number = target_cn.id
3611               WHERE call_number = source_cn.id;
3612
3613             -- ... move V holds to the move-target call number
3614             FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP
3615
3616                 UPDATE  action.hold_request
3617                   SET   target = target_cn.id
3618                   WHERE id = hold.id;
3619
3620                 moved_objects := moved_objects + 1;
3621             END LOOP;
3622
3623         -- ... if not ...
3624         ELSE
3625             -- ... just move the call number to the target record
3626             UPDATE  asset.call_number
3627               SET   record = target_record
3628               WHERE id = source_cn.id;
3629         END IF;
3630
3631         moved_objects := moved_objects + 1;
3632     END LOOP;
3633
3634     -- Find T holds targeting the source record ...
3635     FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP
3636
3637         -- ... and move them to the target record
3638         UPDATE  action.hold_request
3639           SET   target = target_record
3640           WHERE id = hold.id;
3641
3642         moved_objects := moved_objects + 1;
3643     END LOOP;
3644
3645     -- Find serial records targeting the source record ...
3646     FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP
3647         -- ... and move them to the target record
3648         UPDATE  serial.record_entry
3649           SET   record = target_record
3650           WHERE id = ser_rec.id;
3651
3652         moved_objects := moved_objects + 1;
3653     END LOOP;
3654
3655     -- Finally, "delete" the source record
3656     DELETE FROM biblio.record_entry WHERE id = source_record;
3657
3658     -- That's all, folks!
3659     RETURN moved_objects;
3660 END;
3661 $func$ LANGUAGE plpgsql;
3662
3663
3664 CREATE OR REPLACE FUNCTION actor.usr_merge_rows( table_name TEXT, col_name TEXT, src_usr INT, dest_usr INT ) RETURNS VOID AS $$
3665 DECLARE
3666     sel TEXT;
3667     upd TEXT;
3668     del TEXT;
3669     cur_row RECORD;
3670 BEGIN
3671     sel := 'SELECT id::BIGINT FROM ' || table_name || ' WHERE ' || quote_ident(col_name) || ' = ' || quote_literal(src_usr);
3672     upd := 'UPDATE ' || table_name || ' SET ' || quote_ident(col_name) || ' = ' || quote_literal(dest_usr) || ' WHERE id = ';
3673     del := 'DELETE FROM ' || table_name || ' WHERE id = ';
3674     FOR cur_row IN EXECUTE sel LOOP
3675         BEGIN
3676             --RAISE NOTICE 'Attempting to merge % %', table_name, cur_row.id;
3677             EXECUTE upd || cur_row.id;
3678         EXCEPTION WHEN unique_violation THEN
3679             --RAISE NOTICE 'Deleting conflicting % %', table_name, cur_row.id;
3680             EXECUTE del || cur_row.id;
3681         END;
3682     END LOOP;
3683 END;
3684 $$ LANGUAGE plpgsql;
3685
3686 COMMENT ON FUNCTION actor.usr_merge_rows(TEXT, TEXT, INT, INT) IS $$
3687 /**
3688  * Attempts to move each row of the specified table from src_user to dest_user.  
3689  * Where conflicts exist, the conflicting "source" row is deleted.
3690  */
3691 $$;
3692
3693
3694 CREATE OR REPLACE FUNCTION actor.usr_merge( src_usr INT, dest_usr INT, del_addrs BOOLEAN, del_cards BOOLEAN, deactivate_cards BOOLEAN ) RETURNS VOID AS $$
3695 BEGIN
3696
3697     -- do some initial cleanup 
3698     UPDATE actor.usr SET card = NULL WHERE id = src_usr;
3699     UPDATE actor.usr SET mailing_address = NULL WHERE id = src_usr;
3700     UPDATE actor.usr SET billing_address = NULL WHERE id = src_usr;
3701
3702     -- actor.*
3703     IF del_cards THEN
3704         DELETE FROM actor.card where usr = src_usr;
3705     ELSE
3706         IF deactivate_cards THEN
3707             UPDATE actor.card SET active = 'f' WHERE usr = src_usr;
3708         END IF;
3709         UPDATE actor.card SET usr = dest_usr WHERE usr = src_usr;
3710     END IF;
3711
3712
3713     IF del_addrs THEN
3714         DELETE FROM actor.usr_address WHERE usr = src_usr;
3715     ELSE
3716         UPDATE actor.usr_address SET usr = dest_usr WHERE usr = src_usr;
3717     END IF;
3718
3719     UPDATE actor.usr_note SET usr = dest_usr WHERE usr = src_usr;
3720     -- dupes are technically OK in actor.usr_standing_penalty, should manually delete them...
3721     UPDATE actor.usr_standing_penalty SET usr = dest_usr WHERE usr = src_usr;
3722     PERFORM actor.usr_merge_rows('actor.usr_org_unit_opt_in', 'usr', src_usr, dest_usr);
3723     PERFORM actor.usr_merge_rows('actor.usr_setting', 'usr', src_usr, dest_usr);
3724
3725     -- permission.*
3726     PERFORM actor.usr_merge_rows('permission.usr_perm_map', 'usr', src_usr, dest_usr);
3727     PERFORM actor.usr_merge_rows('permission.usr_object_perm_map', 'usr', src_usr, dest_usr);
3728     PERFORM actor.usr_merge_rows('permission.usr_grp_map', 'usr', src_usr, dest_usr);
3729     PERFORM actor.usr_merge_rows('permission.usr_work_ou_map', 'usr', src_usr, dest_usr);
3730
3731
3732     -- container.*
3733     PERFORM actor.usr_merge_rows('container.biblio_record_entry_bucket', 'owner', src_usr, dest_usr);
3734     PERFORM actor.usr_merge_rows('container.call_number_bucket', 'owner', src_usr, dest_usr);
3735     PERFORM actor.usr_merge_rows('container.copy_bucket', 'owner', src_usr, dest_usr);
3736     PERFORM actor.usr_merge_rows('container.user_bucket', 'owner', src_usr, dest_usr);
3737     PERFORM actor.usr_merge_rows('container.user_bucket_item', 'target_user', src_usr, dest_usr);
3738
3739     -- vandelay.*
3740     PERFORM actor.usr_merge_rows('vandelay.queue', 'owner', src_usr, dest_usr);
3741
3742     -- money.*
3743     PERFORM actor.usr_merge_rows('money.collections_tracker', 'usr', src_usr, dest_usr);
3744     PERFORM actor.usr_merge_rows('money.collections_tracker', 'collector', src_usr, dest_usr);
3745     UPDATE money.billable_xact SET usr = dest_usr WHERE usr = src_usr;
3746     UPDATE money.billing SET voider = dest_usr WHERE voider = src_usr;
3747     UPDATE money.bnm_payment SET accepting_usr = dest_usr WHERE accepting_usr = src_usr;
3748
3749     -- action.*
3750     UPDATE action.circulation SET usr = dest_usr WHERE usr = src_usr;
3751     UPDATE action.circulation SET circ_staff = dest_usr WHERE circ_staff = src_usr;
3752     UPDATE action.circulation SET checkin_staff = dest_usr WHERE checkin_staff = src_usr;
3753
3754     UPDATE action.hold_request SET usr = dest_usr WHERE usr = src_usr;
3755     UPDATE action.hold_request SET fulfillment_staff = dest_usr WHERE fulfillment_staff = src_usr;
3756     UPDATE action.hold_request SET requestor = dest_usr WHERE requestor = src_usr;
3757     UPDATE action.hold_notification SET notify_staff = dest_usr WHERE notify_staff = src_usr;
3758
3759     UPDATE action.in_house_use SET staff = dest_usr WHERE staff = src_usr;
3760     UPDATE action.non_cataloged_circulation SET staff = dest_usr WHERE staff = src_usr;
3761     UPDATE action.non_cataloged_circulation SET patron = dest_usr WHERE patron = src_usr;
3762     UPDATE action.non_cat_in_house_use SET staff = dest_usr WHERE staff = src_usr;
3763     UPDATE action.survey_response SET usr = dest_usr WHERE usr = src_usr;
3764
3765     -- acq.*
3766     UPDATE acq.fund_allocation SET allocator = dest_usr WHERE allocator = src_usr;
3767     PERFORM actor.usr_merge_rows('acq.picklist', 'owner', src_usr, dest_usr);
3768     UPDATE acq.purchase_order SET owner = dest_usr WHERE owner = src_usr;
3769     UPDATE acq.po_note SET creator = dest_usr WHERE creator = src_usr;
3770     UPDATE acq.po_note SET editor = dest_usr WHERE editor = src_usr;
3771     UPDATE acq.lineitem_note SET creator = dest_usr WHERE creator = src_usr;
3772     UPDATE acq.lineitem_note SET editor = dest_usr WHERE editor = src_usr;
3773     UPDATE acq.lineitem_usr_attr_definition SET usr = dest_usr WHERE usr = src_usr;
3774
3775     -- asset.*
3776     UPDATE asset.copy SET creator = dest_usr WHERE creator = src_usr;
3777     UPDATE asset.copy SET editor = dest_usr WHERE editor = src_usr;
3778     UPDATE asset.copy_note SET creator = dest_usr WHERE creator = src_usr;
3779     UPDATE asset.call_number SET creator = dest_usr WHERE creator = src_usr;
3780     UPDATE asset.call_number SET editor = dest_usr WHERE editor = src_usr;
3781     UPDATE asset.call_number_note SET creator = dest_usr WHERE creator = src_usr;
3782
3783     -- serial.*
3784     UPDATE serial.record_entry SET creator = dest_usr WHERE creator = src_usr;
3785     UPDATE serial.record_entry SET editor = dest_usr WHERE editor = src_usr;
3786
3787     -- reporter.*
3788     -- It's not uncommon to define the reporter schema in a replica 
3789     -- DB only, so don't assume these tables exist in the write DB.
3790     BEGIN
3791         PERFORM actor.usr_merge_rows('reporter.template', 'owner', src_usr, dest_usr);
3792     EXCEPTION WHEN undefined_table THEN
3793         -- do nothing
3794     END;
3795     BEGIN
3796         PERFORM actor.usr_merge_rows('reporter.report', 'owner', src_usr, dest_usr);
3797     EXCEPTION WHEN undefined_table THEN
3798         -- do nothing
3799     END;
3800     BEGIN
3801         PERFORM actor.usr_merge_rows('reporter.schedule', 'runner', src_usr, dest_usr);
3802     EXCEPTION WHEN undefined_table THEN
3803         -- do nothing
3804     END;
3805     BEGIN
3806         PERFORM actor.usr_merge_rows('reporter.template_folder', 'owner', src_usr, dest_usr);
3807     EXCEPTION WHEN undefined_table THEN
3808         -- do nothing
3809     END;
3810     BEGIN
3811         PERFORM actor.usr_merge_rows('reporter.report_folder', 'owner', src_usr, dest_usr);
3812     EXCEPTION WHEN undefined_table THEN
3813         -- do nothing
3814     END;
3815     BEGIN
3816         PERFORM actor.usr_merge_rows('reporter.output_folder', 'owner', src_usr, dest_usr);
3817     EXCEPTION WHEN undefined_table THEN
3818         -- do nothing
3819     END;
3820
3821     -- Finally, delete the source user
3822     DELETE FROM actor.usr WHERE id = src_usr;
3823
3824 END;
3825 $$ LANGUAGE plpgsql;
3826
3827 COMMENT ON FUNCTION actor.usr_merge(INT, INT, BOOLEAN, BOOLEAN, BOOLEAN) IS $$
3828 /**
3829  * Merges all user date from src_usr to dest_usr.  When collisions occur, 
3830  * keep dest_usr's data and delete src_usr's data.
3831  */
3832 $$;
3833
3834
3835
3836 CREATE OR REPLACE FUNCTION actor.approve_pending_address(pending_id INT) RETURNS BIGINT AS $$
3837 DECLARE
3838     old_id INT;
3839 BEGIN
3840     SELECT INTO old_id replaces FROM actor.usr_address where id = pending_id;
3841     IF old_id IS NULL THEN
3842         UPDATE actor.usr_address SET pending = 'f' WHERE id = pending_id;
3843         RETURN pending_id;
3844     END IF;
3845     -- address replaces an existing address
3846     DELETE FROM actor.usr_address WHERE id = -old_id;
3847     UPDATE actor.usr_address SET id = -id WHERE id = old_id;
3848     UPDATE actor.usr_address SET replaces = NULL, id = old_id, pending = 'f' WHERE id = pending_id;
3849     RETURN old_id;
3850 END
3851 $$ LANGUAGE plpgsql;
3852
3853 COMMENT ON FUNCTION actor.approve_pending_address(INT) IS $$
3854 /**
3855  * Replaces an address with a pending address.  This is done by giving the pending 
3856  * address the ID of the old address.  The replaced address is retained with -id.
3857  */
3858 $$;
3859
3860 COMMIT;
3861
3862
3863 ---------!!!!!!!!!!!!!!!!!!!!!!---------------
3864 --  Must go after COMMIT!!
3865 ---------!!!!!!!!!!!!!!!!!!!!!!---------------
3866
3867 INSERT INTO config.z3950_source (name, label, host, port, db, auth)
3868         VALUES ('biblios', oils_i18n_gettext('biblios','biblios.net', 'czs', 'label'), 'z3950.biblios.net', 210, 'bibliographic', FALSE);
3869  
3870
3871 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3872         VALUES (19, 'biblios','tcn', oils_i18n_gettext(19, 'Title Control Number', 'cza', 'label'), 12, 1);
3873 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3874         VALUES (20, 'biblios', 'isbn', oils_i18n_gettext(20, 'ISBN', 'cza', 'label'), 7, 6);
3875 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3876         VALUES (21, 'biblios', 'lccn', oils_i18n_gettext(21, 'LCCN', 'cza', 'label'), 9, 1);
3877 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3878         VALUES (22, 'biblios', 'author', oils_i18n_gettext(22, 'Author', 'cza', 'label'), 1003, 6);
3879 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3880         VALUES (23, 'biblios', 'title', oils_i18n_gettext(23, 'Title', 'cza', 'label'), 4, 6);
3881 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3882         VALUES (24, 'biblios', 'issn', oils_i18n_gettext(24, 'ISSN', 'cza', 'label'), 8, 1);
3883 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3884         VALUES (25, 'biblios', 'publisher', oils_i18n_gettext(25, 'Publisher', 'cza', 'label'), 1018, 6);
3885 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3886         VALUES (26, 'biblios', 'pubdate', oils_i18n_gettext(26, 'Publication Date', 'cza', 'label'), 31, 1);
3887 INSERT INTO config.z3950_attr (id, source, name, label, code, format)
3888         VALUES (27, 'biblios', 'item_type', oils_i18n_gettext(27, 'Item Type', 'cza', 'label'), 1001, 1);
3889
3890 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_FUNDING_SOURCE', 'Allow a user to delete a funding source');
3891 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_FUNDING_SOURCE', 'Allow a user to view a funding source');
3892 INSERT INTO permission.perm_list (code, description) VALUES ('UPDATE_FUNDING_SOURCE', 'Allow a user to update a funding source');
3893 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_FUND', 'Allow a user to create a new fund');
3894 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_FUND', 'Allow a user to delete a fund');
3895 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_FUND', 'Allow a user to view a fund');
3896 INSERT INTO permission.perm_list (code, description) VALUES ('UPDATE_FUND', 'Allow a user to update a fund');
3897 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_FUND_ALLOCATION', 'Allow a user to create a new fund allocation');
3898 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_FUND_ALLOCATION', 'Allow a user to delete a fund allocation');
3899 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_FUND_ALLOCATION', 'Allow a user to view a fund allocation');
3900 INSERT INTO permission.perm_list (code, description) VALUES ('UPDATE_FUND_ALLOCATION', 'Allow a user to update a fund allocation');
3901 INSERT INTO permission.perm_list (code, description) VALUES ('GENERAL_ACQ', 'Lowest level permission required to access the ACQ interface');
3902 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_PROVIDER', 'Allow a user to create a new provider');
3903 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_PROVIDER', 'Allow a user to delate a provider');
3904 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_PROVIDER', 'Allow a user to view a provider');
3905 INSERT INTO permission.perm_list (code, description) VALUES ('UPDATE_PROVIDER', 'Allow a user to update a provider');
3906 INSERT INTO permission.perm_list (code, description) VALUES ('ADMIN_FUNDING_SOURCE', 'Allow a user to create/view/update/delete a funding source');
3907 INSERT INTO permission.perm_list (code, description) VALUES ('ADMIN_ACQ_FUND', 'Allow a user to create/view/update/delete a fund');
3908 INSERT INTO permission.perm_list (code, description) VALUES ('ADMIN_FUND', 'Allow a user to create/view/update/delete a fund');
3909 INSERT INTO permission.perm_list (code, description) VALUES ('MANAGE_FUNDING_SOURCE', 'Allow a user to view/credit/debit a funding source');
3910 INSERT INTO permission.perm_list (code, description) VALUES ('MANAGE_FUND', 'Allow a user to view/credit/debit a fund');
3911 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_PICKLIST', 'Allows a user to create a picklist');
3912 INSERT INTO permission.perm_list (code, description) VALUES ('ADMIN_PROVIDER', 'Allow a user to create/view/update/delete a provider');
3913 INSERT INTO permission.perm_list (code, description) VALUES ('MANAGE_PROVIDER', 'Allow a user to view and purchase from a provider');
3914 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_PICKLIST', 'Allow a user to view another users picklist');
3915 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_RECORD', 'Allow a staff member to directly remove a bibliographic record');
3916 INSERT INTO permission.perm_list (code, description) VALUES ('ADMIN_CURRENCY_TYPE', 'Allow a user to create/view/update/delete a currency_type');
3917 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_BAD_DEBT', 'Allow a user to mark a transaction as bad (unrecoverable) debt');
3918 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_BILLING_TYPE', 'Allow a user to view billing types');
3919 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_AVAILABLE', 'Allow a user to mark an item status as ''available''');
3920 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_CHECKED_OUT', 'Allow a user to mark an item status as ''checked out''');
3921 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_BINDERY', 'Allow a user to mark an item status as ''bindery''');
3922 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_LOST', 'Allow a user to mark an item status as ''lost''');
3923 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_MISSING', 'Allow a user to mark an item status as ''missing''');
3924 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_IN_PROCESS', 'Allow a user to mark an item status as ''in process''');
3925 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_IN_TRANSIT', 'Allow a user to mark an item status as ''in transit''');
3926 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_RESHELVING', 'Allow a user to mark an item status as ''reshelving''');
3927 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_ON_HOLDS_SHELF', 'Allow a user to mark an item status as ''on holds shelf''');
3928 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_ON_ORDER', 'Allow a user to mark an item status as ''on order''');
3929 INSERT INTO permission.perm_list (code, description) VALUES ('MARK_ITEM_ILL', 'Allow a user to mark an item status as ''inter-library loan''');
3930 INSERT INTO permission.perm_list (code, description) VALUES ('group_application.user.staff.acq', 'Allows a user to add/remove/edit users in the "ACQ" group');
3931 INSERT INTO permission.perm_list (code, description) VALUES ('group_application.user.staff.acq_admin', 'Allows a user to add/remove/edit users in the "Acquisitions Administrator" group');
3932 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_PURCHASE_ORDER', 'Allows a user to create a purchase order');
3933 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_PURCHASE_ORDER', 'Allows a user to view a purchase order');
3934 INSERT INTO permission.perm_list (code, description) VALUES ('IMPORT_ACQ_LINEITEM_BIB_RECORD', 'Allows a user to import a bib record from the acq staging area (on-order record) into the ILS bib data set');
3935 INSERT INTO permission.perm_list (code, description) VALUES ('RECEIVE_PURCHASE_ORDER', 'Allows a user to mark a purchase order, lineitem, or individual copy as received');
3936 INSERT INTO permission.perm_list (code, description) VALUES ('VIEW_ORG_SETTINGS','Allows a user to view all org settings at the specified level');
3937 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_MFHD_RECORD', 'Allows a user to create a new MFHD record');
3938 INSERT INTO permission.perm_list (code, description) VALUES ('UPDATE_MFHD_RECORD', 'Allows a user to update an MFHD record');
3939 INSERT INTO permission.perm_list (code, description) VALUES ('DELETE_MFHD_RECORD', 'Allows a user to delete an MFHD record');
3940 INSERT INTO permission.perm_list (code, description) VALUES ('CREATE_FUNDING_SOURCE', 'Allow a user to create a new funding source');
3941
3942 INSERT INTO permission.perm_list (code) VALUES ('CREATE_ACQ_FUNDING_SOURCE');
3943 INSERT INTO permission.perm_list (code) VALUES ('DELETE_ACQ_FUNDING_SOURCE');
3944 INSERT INTO permission.perm_list (code) VALUES ('UPDATE_ACQ_FUNDING_SOURCE');
3945 INSERT INTO permission.perm_list (code) VALUES ('VIEW_ACQ_FUNDING_SOURCE');
3946 INSERT INTO permission.perm_list (code) VALUES ('UPDATE_ORG_UNIT_SETTING.global.password_regex');
3947 INSERT INTO permission.perm_list (code) VALUES ('UPDATE_ORG_UNIT_SETTING.global.juvenile_age_threshold');
3948 INSERT INTO permission.perm_list (code) VALUES ('UPDATE_ORG_UNIT_SETTING.patron.password.use_phone');
3949
3950 INSERT INTO permission.grp_tree (name, parent, description, perm_interval, usergroup, application_perm) VALUES ('Acquisitions', 3, NULL, '3 years', TRUE, 'group_application.user.staff.acq');
3951 INSERT INTO permission.grp_tree (name, parent, description, perm_interval, usergroup, application_perm) VALUES ('Acquisitions Administrators', (SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), NULL, '3 years', TRUE, 'group_application.user.staff.acq_admin');
3952
3953 -- You can't log into the staff client without this
3954 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES (3, (SELECT id FROM permission.perm_list WHERE code = 'VIEW_ORG_SETTINGS'), 1, false);
3955 -- MFHD permissions are necessary for serials work; add to the default catalogers group
3956 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES (5, (SELECT id FROM permission.perm_list WHERE code = 'CREATE_MFHD_RECORD'), 1, false);
3957 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES (5, (SELECT id FROM permission.perm_list WHERE code = 'DELETE_MFHD_RECORD'), 1, false);
3958 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES (5, (SELECT id FROM permission.perm_list WHERE code = 'UPDATE_MFHD_RECORD'), 1, false);
3959
3960 -- Add basic acquisitions permissions to the Acquisitions group
3961 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'GENERAL_ACQ'), 1, false);
3962 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'VIEW_PICKLIST'), 1, false);
3963 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'CREATE_PICKLIST'), 1, false);
3964 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'CREATE_PURCHASE_ORDER'), 1, false);
3965 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'VIEW_PURCHASE_ORDER'), 1, false);
3966 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'RECEIVE_PURCHASE_ORDER'), 1, false);
3967 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'VIEW_PROVIDER'), 1, false);
3968 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'UPDATE_COPY'), 1, false);
3969 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions'), (SELECT id FROM permission.perm_list WHERE code = 'UPDATE_VOLUME'), 1, false);
3970
3971 -- Add acquisitions administration permissions to the Acquisitions group
3972 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions Administrators'), (SELECT id FROM permission.perm_list WHERE code = 'ADMIN_PROVIDER'), 1, false);
3973 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions Administrators'), (SELECT id FROM permission.perm_list WHERE code = 'ADMIN_FUNDING_SOURCE'), 1, false);
3974 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions Administrators'), (SELECT id FROM permission.perm_list WHERE code = 'ADMIN_ACQ_FUND'), 1, false);
3975 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions Administrators'), (SELECT id FROM permission.perm_list WHERE code = 'ADMIN_FUND'), 1, false);
3976 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable) VALUES ((SELECT id FROM permission.grp_tree WHERE name = 'Acquisitions Administrators'), (SELECT id FROM permission.perm_list WHERE code = 'ADMIN_CURRENCY_TYPE'), 1, false);
3977
3978 SELECT SETVAL('permission.perm_list_id_seq'::TEXT, (SELECT MAX(id) FROM permission.perm_list));
3979
3980 ALTER TABLE auditor.action_hold_request_history ADD COLUMN cancel_cause INT;
3981 ALTER TABLE auditor.action_hold_request_history ADD COLUMN cancel_note TEXT;
3982
3983 CREATE OR REPLACE FUNCTION tmp_populate_p_b_bt () RETURNS BOOL AS $$
3984 DECLARE
3985     p   RECORD;
3986 BEGIN
3987     FOR p IN
3988         SELECT  DISTINCT xact
3989           FROM  money.payment
3990           WHERE NOT voided
3991                 AND amount > 0.0
3992     LOOP
3993
3994         INSERT INTO money.materialized_payment_by_billing_type (
3995             xact, payment, billing, payment_ts, billing_ts,
3996             payment_type, billing_type, amount, billing_ou, payment_ou
3997         ) SELECT    xact, payment, billing, payment_ts, billing_ts,
3998                     payment_type, billing_type, amount, billing_ou, payment_ou
3999           FROM money.payment_by_billing_type( p.xact );
4000
4001     END LOOP;
4002
4003     RETURN TRUE;
4004 END;
4005 $$ LANGUAGE PLPGSQL;
4006
4007 SELECT tmp_populate_p_b_bt();
4008
4009 DROP FUNCTION tmp_populate_p_b_bt ();
4010
4011
4012 UPDATE config.xml_transform SET xslt=$$<xsl:stylesheet xmlns="http://www.loc.gov/mods/v3" xmlns:marc="http://www.loc.gov/MARC21/slim"
4013         xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4014         exclude-result-prefixes="xlink marc" version="1.0">
4015         <xsl:output encoding="UTF-8" indent="yes" method="xml"/>
4016
4017         <xsl:variable name="ascii">
4018                 <xsl:text> !"#$%&amp;'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~</xsl:text>
4019         </xsl:variable>
4020
4021         <xsl:variable name="latin1">
4022                 <xsl:text> ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ</xsl:text>
4023         </xsl:variable>
4024         <!-- Characters that usually don't need to be escaped -->
4025         <xsl:variable name="safe">
4026                 <xsl:text>!'()*-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~</xsl:text>
4027         </xsl:variable>
4028
4029         <xsl:variable name="hex">0123456789ABCDEF</xsl:variable>
4030
4031
4032         
4033         <!--MARC21slim2MODS3-3.xsl
4034 Revision 1.27 - Mapped 648 to <subject> 2009/03/13 tmee
4035 Revision 1.26 - Added subfield $s mapping for 130/240/730  2008/10/16 tmee
4036 Revision 1.25 - Mapped 040e to <descriptiveStandard> and Leader/18 to <descriptive standard>aacr2  2008/09/18 tmee
4037 Revision 1.24 - Mapped 852 subfields $h, $i, $j, $k, $l, $m, $t to <shelfLocation> and 852 subfield $u to <physicalLocation> with @xlink 2008/09/17 tmee
4038 Revision 1.23 - Commented out xlink/uri for subfield 0 for 130/240/730, 100/700, 110/710, 111/711 as these are currently unactionable  2008/09/17  tmee
4039 Revision 1.22 - Mapped 022 subfield $l to type "issn-l" subfield $m to output identifier element with corresponding @type and @invalid eq 'yes'2008/09/17  tmee
4040 Revision 1.21 - Mapped 856 ind2=1 or ind2=2 to <relatedItem><location><url>  2008/07/03  tmee
4041 Revision 1.20 - Added genre w/@auth="contents of 2" and type= "musical composition"  2008/07/01  tmee
4042 Revision 1.19 - Added genre offprint for 008/24+ BK code 2  2008/07/01  tmee
4043 Revision 1.18 - Added xlink/uri for subfield 0 for 130/240/730, 100/700, 110/710, 111/711  2008/06/26  tmee
4044 Revision 1.17 - Added mapping of 662 2008/05/14 tmee    
4045 Revision 1.16 - Changed @authority from "marc" to "marcgt" for 007 and 008 codes mapped to a term in <genre> 2007/07/10  tmee
4046 Revision 1.15 - For field 630, moved call to part template outside title element  2007/07/10  tmee
4047 Revision 1.14 - Fixed template isValid and fields 010, 020, 022, 024, 028, and 037 to output additional identifier elements with corresponding @type and @invalid eq 'yes' when subfields z or y (in the case of 022) exist in the MARCXML ::: 2007/01/04 17:35:20 cred
4048 Revision 1.13 - Changed order of output under cartographics to reflect schema  2006/11/28  tmee
4049 Revision 1.12 - Updated to reflect MODS 3.2 Mapping  2006/10/11  tmee
4050 Revision 1.11 - The attribute objectPart moved from <languageTerm> to <language>  2006/04/08  jrad
4051 Revision 1.10 - MODS 3.1 revisions to language and classification elements  (plus ability to find marc:collection embedded in wrapper elements such as SRU zs: wrappers)  2006/02/06  ggar
4052 Revision 1.9 - Subfield $y was added to field 242 2004/09/02 10:57 jrad
4053 Revision 1.8 - Subject chopPunctuation expanded and attribute fixes 2004/08/12 jrad
4054 Revision 1.7 - 2004/03/25 08:29 jrad
4055 Revision 1.6 - Various validation fixes 2004/02/20 ntra
4056 Revision 1.5 - MODS2 to MODS3 updates, language unstacking and de-duping, chopPunctuation expanded  2003/10/02 16:18:58  ntra
4057 Revision 1.3 - Additional Changes not related to MODS Version 2.0 by ntra
4058 Revision 1.2 - Added Log Comment  2003/03/24 19:37:42  ckeith
4059 -->
4060         <xsl:template match="/">
4061                 <xsl:choose>
4062                         <xsl:when test="//marc:collection">
4063                                 <modsCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4064                                         xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
4065                                         <xsl:for-each select="//marc:collection/marc:record">
4066                                                 <mods version="3.3">
4067                                                         <xsl:call-template name="marcRecord"/>
4068                                                 </mods>
4069                                         </xsl:for-each>
4070                                 </modsCollection>
4071                         </xsl:when>
4072                         <xsl:otherwise>
4073                                 <mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3"
4074                                         xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
4075                                         <xsl:for-each select="//marc:record">
4076                                                 <xsl:call-template name="marcRecord"/>
4077                                         </xsl:for-each>
4078                                 </mods>
4079                         </xsl:otherwise>
4080                 </xsl:choose>
4081         </xsl:template>
4082         <xsl:template name="marcRecord">
4083                 <xsl:variable name="leader" select="marc:leader"/>
4084                 <xsl:variable name="leader6" select="substring($leader,7,1)"/>
4085                 <xsl:variable name="leader7" select="substring($leader,8,1)"/>
4086                 <xsl:variable name="controlField008" select="marc:controlfield[@tag='008']"/>
4087                 <xsl:variable name="typeOf008">
4088                         <xsl:choose>
4089                                 <xsl:when test="$leader6='a'">
4090                                         <xsl:choose>
4091                                                 <xsl:when
4092                                                         test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'">BK</xsl:when>
4093                                                 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'">SE</xsl:when>
4094                                         </xsl:choose>
4095                                 </xsl:when>
4096                                 <xsl:when test="$leader6='t'">BK</xsl:when>
4097                                 <xsl:when test="$leader6='p'">MM</xsl:when>
4098                                 <xsl:when test="$leader6='m'">CF</xsl:when>
4099                                 <xsl:when test="$leader6='e' or $leader6='f'">MP</xsl:when>
4100                                 <xsl:when test="$leader6='g' or $leader6='k' or $leader6='o' or $leader6='r'">VM</xsl:when>
4101                                 <xsl:when test="$leader6='c' or $leader6='d' or $leader6='i' or $leader6='j'"
4102                                 >MU</xsl:when>
4103                         </xsl:choose>
4104                 </xsl:variable>
4105                 <xsl:for-each select="marc:datafield[@tag='245']">
4106                         <titleInfo>
4107                                 <xsl:variable name="title">
4108                                         <xsl:choose>
4109                                                 <xsl:when test="marc:subfield[@code='b']">
4110                                                         <xsl:call-template name="specialSubfieldSelect">
4111                                                                 <xsl:with-param name="axis">b</xsl:with-param>
4112                                                                 <xsl:with-param name="beforeCodes">afgk</xsl:with-param>
4113                                                         </xsl:call-template>
4114                                                 </xsl:when>
4115                                                 <xsl:otherwise>
4116                                                         <xsl:call-template name="subfieldSelect">
4117                                                                 <xsl:with-param name="codes">abfgk</xsl:with-param>
4118                                                         </xsl:call-template>
4119                                                 </xsl:otherwise>
4120                                         </xsl:choose>
4121                                 </xsl:variable>
4122                                 <xsl:variable name="titleChop">
4123                                         <xsl:call-template name="chopPunctuation">
4124                                                 <xsl:with-param name="chopString">
4125                                                         <xsl:value-of select="$title"/>
4126                                                 </xsl:with-param>
4127                                         </xsl:call-template>
4128                                 </xsl:variable>
4129                                 <xsl:choose>
4130                                         <xsl:when test="@ind2&gt;0">
4131                                                 <nonSort>
4132                                                         <xsl:value-of select="substring($titleChop,1,@ind2)"/>
4133                                                 </nonSort>
4134                                                 <title>
4135                                                         <xsl:value-of select="substring($titleChop,@ind2+1)"/>
4136                                                 </title>
4137                                         </xsl:when>
4138                                         <xsl:otherwise>
4139                                                 <title>
4140                                                         <xsl:value-of select="$titleChop"/>
4141                                                 </title>
4142                                         </xsl:otherwise>
4143                                 </xsl:choose>
4144                                 <xsl:if test="marc:subfield[@code='b']">
4145                                         <subTitle>
4146                                                 <xsl:call-template name="chopPunctuation">
4147                                                         <xsl:with-param name="chopString">
4148                                                                 <xsl:call-template name="specialSubfieldSelect">
4149                                                                         <xsl:with-param name="axis">b</xsl:with-param>
4150                                                                         <xsl:with-param name="anyCodes">b</xsl:with-param>
4151                                                                         <xsl:with-param name="afterCodes">afgk</xsl:with-param>
4152                                                                 </xsl:call-template>
4153                                                         </xsl:with-param>
4154                                                 </xsl:call-template>
4155                                         </subTitle>
4156                                 </xsl:if>
4157                                 <xsl:call-template name="part"/>
4158                         </titleInfo>
4159                 </xsl:for-each>
4160                 <xsl:for-each select="marc:datafield[@tag='210']">
4161                         <titleInfo type="abbreviated">
4162                                 <title>
4163                                         <xsl:call-template name="chopPunctuation">
4164                                                 <xsl:with-param name="chopString">
4165                                                         <xsl:call-template name="subfieldSelect">
4166                                                                 <xsl:with-param name="codes">a</xsl:with-param>
4167                                                         </xsl:call-template>
4168                                                 </xsl:with-param>
4169                                         </xsl:call-template>
4170                                 </title>
4171                                 <xsl:call-template name="subtitle"/>
4172                         </titleInfo>
4173                 </xsl:for-each>
4174                 <xsl:for-each select="marc:datafield[@tag='242']">
4175                         <titleInfo type="translated">
4176                                 <!--09/01/04 Added subfield $y-->
4177                                 <xsl:for-each select="marc:subfield[@code='y']">
4178                                         <xsl:attribute name="lang">
4179                                                 <xsl:value-of select="text()"/>
4180                                         </xsl:attribute>
4181                                 </xsl:for-each>
4182                                 <xsl:for-each select="marc:subfield[@code='i']">
4183                                         <xsl:attribute name="displayLabel">
4184                                                 <xsl:value-of select="text()"/>
4185                                         </xsl:attribute>
4186                                 </xsl:for-each>
4187                                 <title>
4188                                         <xsl:call-template name="chopPunctuation">
4189                                                 <xsl:with-param name="chopString">
4190                                                         <xsl:call-template name="subfieldSelect">
4191                                                                 <!-- 1/04 removed $h, b -->
4192                                                                 <xsl:with-param name="codes">a</xsl:with-param>
4193                                                         </xsl:call-template>
4194                                                 </xsl:with-param>
4195                                         </xsl:call-template>
4196                                 </title>
4197                                 <!-- 1/04 fix -->
4198                                 <xsl:call-template name="subtitle"/>
4199                                 <xsl:call-template name="part"/>
4200                         </titleInfo>
4201                 </xsl:for-each>
4202                 <xsl:for-each select="marc:datafield[@tag='246']">
4203                         <titleInfo type="alternative">
4204                                 <xsl:for-each select="marc:subfield[@code='i']">
4205                                         <xsl:attribute name="displayLabel">
4206                                                 <xsl:value-of select="text()"/>
4207                                         </xsl:attribute>
4208                                 </xsl:for-each>
4209                                 <title>
4210                                         <xsl:call-template name="chopPunctuation">
4211                                                 <xsl:with-param name="chopString">
4212                                                         <xsl:call-template name="subfieldSelect">
4213                                                                 <!-- 1/04 removed $h, $b -->
4214                                                                 <xsl:with-param name="codes">af</xsl:with-param>
4215                                                         </xsl:call-template>
4216                                                 </xsl:with-param>
4217                                         </xsl:call-template>
4218                                 </title>
4219                                 <xsl:call-template name="subtitle"/>
4220                                 <xsl:call-template name="part"/>
4221                         </titleInfo>
4222                 </xsl:for-each>
4223                 <xsl:for-each
4224                         select="marc:datafield[@tag='130']|marc:datafield[@tag='240']|marc:datafield[@tag='730'][@ind2!='2']">
4225                         <titleInfo type="uniform">
4226                                 <title>
4227                                         <!-- deleted uri for subfield 0
4228                                                 <xsl:call-template name="uri"/>
4229                                         -->
4230
4231                                         <xsl:variable name="str">
4232                                                 <xsl:for-each select="marc:subfield">
4233                                                         <xsl:if
4234                                                                 test="(contains('adfklmors',@code) and (not(../marc:subfield[@code='n' or @code='p']) or (following-sibling::marc:subfield[@code='n' or @code='p'])))">
4235                                                                 <xsl:value-of select="text()"/>
4236                                                                 <xsl:text> </xsl:text>
4237                                                         </xsl:if>
4238                                                 </xsl:for-each>
4239                                         </xsl:variable>
4240                                         <xsl:call-template name="chopPunctuation">
4241                                                 <xsl:with-param name="chopString">
4242                                                         <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
4243                                                 </xsl:with-param>
4244                                         </xsl:call-template>
4245                                 </title>
4246                                 <xsl:call-template name="part"/>
4247                         </titleInfo>
4248                 </xsl:for-each>
4249                 <xsl:for-each select="marc:datafield[@tag='740'][@ind2!='2']">
4250                         <titleInfo type="alternative">
4251                                 <title>
4252                                         <xsl:call-template name="chopPunctuation">
4253                                                 <xsl:with-param name="chopString">
4254                                                         <xsl:call-template name="subfieldSelect">
4255                                                                 <xsl:with-param name="codes">ah</xsl:with-param>
4256                                                         </xsl:call-template>
4257                                                 </xsl:with-param>
4258                                         </xsl:call-template>
4259                                 </title>
4260                                 <xsl:call-template name="part"/>
4261                         </titleInfo>
4262                 </xsl:for-each>
4263                 <xsl:for-each select="marc:datafield[@tag='100']">
4264                         <name type="personal">
4265
4266                                 <!-- deleted uri for subfield 0
4267                                 <xsl:call-template name="uri"/>
4268                                 -->
4269
4270                                 <xsl:call-template name="nameABCDQ"/>
4271                                 <xsl:call-template name="affiliation"/>
4272                                 <role>
4273                                         <roleTerm authority="marcrelator" type="text">creator</roleTerm>
4274                                 </role>
4275                                 <xsl:call-template name="role"/>
4276                         </name>
4277                 </xsl:for-each>
4278                 <xsl:for-each select="marc:datafield[@tag='110']">
4279                         <name type="corporate">
4280
4281                                 <!-- deleted uri for subfield 0
4282                                         <xsl:call-template name="uri"/>
4283                                 -->
4284
4285                                 <xsl:call-template name="nameABCDN"/>
4286                                 <role>
4287                                         <roleTerm authority="marcrelator" type="text">creator</roleTerm>
4288                                 </role>
4289                                 <xsl:call-template name="role"/>
4290                         </name>
4291                 </xsl:for-each>
4292                 <xsl:for-each select="marc:datafield[@tag='111']">
4293                         <name type="conference">
4294
4295                                 <!-- deleted uri for subfield 0
4296                                         <xsl:call-template name="uri"/>
4297                                 -->
4298
4299                                 <xsl:call-template name="nameACDEQ"/>
4300                                 <role>
4301                                         <roleTerm authority="marcrelator" type="text">creator</roleTerm>
4302                                 </role>
4303                                 <xsl:call-template name="role"/>
4304                         </name>
4305                 </xsl:for-each>
4306                 <xsl:for-each select="marc:datafield[@tag='700'][not(marc:subfield[@code='t'])]">
4307                         <name type="personal">
4308
4309                                 <!-- deleted uri for subfield 0
4310                                         <xsl:call-template name="uri"/>
4311                                 -->
4312
4313                                 <xsl:call-template name="nameABCDQ"/>
4314                                 <xsl:call-template name="affiliation"/>
4315                                 <xsl:call-template name="role"/>
4316                         </name>
4317                 </xsl:for-each>
4318                 <xsl:for-each select="marc:datafield[@tag='710'][not(marc:subfield[@code='t'])]">
4319                         <name type="corporate">
4320
4321                                 <!-- deleted uri for subfield 0
4322                                         <xsl:call-template name="uri"/>
4323                                 -->
4324
4325                                 <xsl:call-template name="nameABCDN"/>
4326                                 <xsl:call-template name="role"/>
4327                         </name>
4328                 </xsl:for-each>
4329                 <xsl:for-each select="marc:datafield[@tag='711'][not(marc:subfield[@code='t'])]">
4330                         <name type="conference">
4331
4332                                 <!-- deleted uri for subfield 0
4333                                         <xsl:call-template name="uri"/>
4334                                 -->
4335
4336                                 <xsl:call-template name="nameACDEQ"/>
4337                                 <xsl:call-template name="role"/>
4338                         </name>
4339                 </xsl:for-each>
4340                 <xsl:for-each select="marc:datafield[@tag='720'][not(marc:subfield[@code='t'])]">
4341                         <name>
4342                                 <xsl:if test="@ind1=1">
4343                                         <xsl:attribute name="type">
4344                                                 <xsl:text>personal</xsl:text>
4345                                         </xsl:attribute>
4346                                 </xsl:if>
4347                                 <namePart>
4348                                         <xsl:value-of select="marc:subfield[@code='a']"/>
4349                                 </namePart>
4350                                 <xsl:call-template name="role"/>
4351                         </name>
4352                 </xsl:for-each>
4353                 <typeOfResource>
4354                         <xsl:if test="$leader7='c'">
4355                                 <xsl:attribute name="collection">yes</xsl:attribute>
4356                         </xsl:if>
4357                         <xsl:if test="$leader6='d' or $leader6='f' or $leader6='p' or $leader6='t'">
4358                                 <xsl:attribute name="manuscript">yes</xsl:attribute>
4359                         </xsl:if>
4360                         <xsl:choose>
4361                                 <xsl:when test="$leader6='a' or $leader6='t'">text</xsl:when>
4362                                 <xsl:when test="$leader6='e' or $leader6='f'">cartographic</xsl:when>
4363                                 <xsl:when test="$leader6='c' or $leader6='d'">notated music</xsl:when>
4364                                 <xsl:when test="$leader6='i'">sound recording-nonmusical</xsl:when>
4365                                 <xsl:when test="$leader6='j'">sound recording-musical</xsl:when>
4366                                 <xsl:when test="$leader6='k'">still image</xsl:when>
4367                                 <xsl:when test="$leader6='g'">moving image</xsl:when>
4368                                 <xsl:when test="$leader6='r'">three dimensional object</xsl:when>
4369                                 <xsl:when test="$leader6='m'">software, multimedia</xsl:when>
4370                                 <xsl:when test="$leader6='p'">mixed material</xsl:when>
4371                         </xsl:choose>
4372                 </typeOfResource>
4373                 <xsl:if test="substring($controlField008,26,1)='d'">
4374                         <genre authority="marcgt">globe</genre>
4375                 </xsl:if>
4376                 <xsl:if
4377                         test="marc:controlfield[@tag='007'][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
4378                         <genre authority="marcgt">remote-sensing image</genre>
4379                 </xsl:if>
4380                 <xsl:if test="$typeOf008='MP'">
4381                         <xsl:variable name="controlField008-25" select="substring($controlField008,26,1)"/>
4382                         <xsl:choose>
4383                                 <xsl:when
4384                                         test="$controlField008-25='a' or $controlField008-25='b' or $controlField008-25='c' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
4385                                         <genre authority="marcgt">map</genre>
4386                                 </xsl:when>
4387                                 <xsl:when
4388                                         test="$controlField008-25='e' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
4389                                         <genre authority="marcgt">atlas</genre>
4390                                 </xsl:when>
4391                         </xsl:choose>
4392                 </xsl:if>
4393                 <xsl:if test="$typeOf008='SE'">
4394                         <xsl:variable name="controlField008-21" select="substring($controlField008,22,1)"/>
4395                         <xsl:choose>
4396                                 <xsl:when test="$controlField008-21='d'">
4397                                         <genre authority="marcgt">database</genre>
4398                                 </xsl:when>
4399                                 <xsl:when test="$controlField008-21='l'">
4400                                         <genre authority="marcgt">loose-leaf</genre>
4401                                 </xsl:when>
4402                                 <xsl:when test="$controlField008-21='m'">
4403                                         <genre authority="marcgt">series</genre>
4404                                 </xsl:when>
4405                                 <xsl:when test="$controlField008-21='n'">
4406                                         <genre authority="marcgt">newspaper</genre>
4407                                 </xsl:when>
4408                                 <xsl:when test="$controlField008-21='p'">
4409                                         <genre authority="marcgt">periodical</genre>
4410                                 </xsl:when>
4411                                 <xsl:when test="$controlField008-21='w'">
4412                                         <genre authority="marcgt">web site</genre>
4413                                 </xsl:when>
4414                         </xsl:choose>
4415                 </xsl:if>
4416                 <xsl:if test="$typeOf008='BK' or $typeOf008='SE'">
4417                         <xsl:variable name="controlField008-24" select="substring($controlField008,25,4)"/>
4418                         <xsl:choose>
4419                                 <xsl:when test="contains($controlField008-24,'a')">
4420                                         <genre authority="marcgt">abstract or summary</genre>
4421                                 </xsl:when>
4422                                 <xsl:when test="contains($controlField008-24,'b')">
4423                                         <genre authority="marcgt">bibliography</genre>
4424                                 </xsl:when>
4425                                 <xsl:when test="contains($controlField008-24,'c')">
4426                                         <genre authority="marcgt">catalog</genre>
4427                                 </xsl:when>
4428                                 <xsl:when test="contains($controlField008-24,'d')">
4429                                         <genre authority="marcgt">dictionary</genre>
4430                                 </xsl:when>
4431                                 <xsl:when test="contains($controlField008-24,'e')">
4432                                         <genre authority="marcgt">encyclopedia</genre>
4433                                 </xsl:when>
4434                                 <xsl:when test="contains($controlField008-24,'f')">
4435                                         <genre authority="marcgt">handbook</genre>
4436                                 </xsl:when>
4437                                 <xsl:when test="contains($controlField008-24,'g')">
4438                                         <genre authority="marcgt">legal article</genre>
4439                                 </xsl:when>
4440                                 <xsl:when test="contains($controlField008-24,'i')">
4441                                         <genre authority="marcgt">index</genre>
4442                                 </xsl:when>
4443                                 <xsl:when test="contains($controlField008-24,'k')">
4444                                         <genre authority="marcgt">discography</genre>
4445                                 </xsl:when>
4446                                 <xsl:when test="contains($controlField008-24,'l')">
4447                                         <genre authority="marcgt">legislation</genre>
4448                                 </xsl:when>
4449                                 <xsl:when test="contains($controlField008-24,'m')">
4450                                         <genre authority="marcgt">theses</genre>
4451                                 </xsl:when>
4452                                 <xsl:when test="contains($controlField008-24,'n')">
4453                                         <genre authority="marcgt">survey of literature</genre>
4454                                 </xsl:when>
4455                                 <xsl:when test="contains($controlField008-24,'o')">
4456                                         <genre authority="marcgt">review</genre>
4457                                 </xsl:when>
4458                                 <xsl:when test="contains($controlField008-24,'p')">
4459                                         <genre authority="marcgt">programmed text</genre>
4460                                 </xsl:when>
4461                                 <xsl:when test="contains($controlField008-24,'q')">
4462                                         <genre authority="marcgt">filmography</genre>
4463                                 </xsl:when>
4464                                 <xsl:when test="contains($controlField008-24,'r')">
4465                                         <genre authority="marcgt">directory</genre>
4466                                 </xsl:when>
4467                                 <xsl:when test="contains($controlField008-24,'s')">
4468                                         <genre authority="marcgt">statistics</genre>
4469                                 </xsl:when>
4470                                 <xsl:when test="contains($controlField008-24,'t')">
4471                                         <genre authority="marcgt">technical report</genre>
4472                                 </xsl:when>
4473                                 <xsl:when test="contains($controlField008-24,'v')">
4474                                         <genre authority="marcgt">legal case and case notes</genre>
4475                                 </xsl:when>
4476                                 <xsl:when test="contains($controlField008-24,'w')">
4477                                         <genre authority="marcgt">law report or digest</genre>
4478                                 </xsl:when>
4479                                 <xsl:when test="contains($controlField008-24,'z')">
4480                                         <genre authority="marcgt">treaty</genre>
4481                                 </xsl:when>
4482                         </xsl:choose>
4483                         <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"/>
4484                         <xsl:choose>
4485                                 <xsl:when test="$controlField008-29='1'">
4486                                         <genre authority="marcgt">conference publication</genre>
4487                                 </xsl:when>
4488                         </xsl:choose>
4489                 </xsl:if>
4490                 <xsl:if test="$typeOf008='CF'">
4491                         <xsl:variable name="controlField008-26" select="substring($controlField008,27,1)"/>
4492                         <xsl:choose>
4493                                 <xsl:when test="$controlField008-26='a'">
4494                                         <genre authority="marcgt">numeric data</genre>
4495                                 </xsl:when>
4496                                 <xsl:when test="$controlField008-26='e'">
4497                                         <genre authority="marcgt">database</genre>
4498                                 </xsl:when>
4499                                 <xsl:when test="$controlField008-26='f'">
4500                                         <genre authority="marcgt">font</genre>
4501                                 </xsl:when>
4502                                 <xsl:when test="$controlField008-26='g'">
4503                                         <genre authority="marcgt">game</genre>
4504                                 </xsl:when>
4505                         </xsl:choose>
4506                 </xsl:if>
4507                 <xsl:if test="$typeOf008='BK'">
4508                         <xsl:if test="substring($controlField008,25,1)='j'">
4509                                 <genre authority="marcgt">patent</genre>
4510                         </xsl:if>
4511                         <xsl:if test="substring($controlField008,25,1)='2'">
4512                                 <genre authority="marcgt">offprint</genre>
4513                         </xsl:if>
4514                         <xsl:if test="substring($controlField008,31,1)='1'">
4515                                 <genre authority="marcgt">festschrift</genre>
4516                         </xsl:if>
4517                         <xsl:variable name="controlField008-34" select="substring($controlField008,35,1)"/>
4518                         <xsl:if
4519                                 test="$controlField008-34='a' or $controlField008-34='b' or $controlField008-34='c' or $controlField008-34='d'">
4520                                 <genre authority="marcgt">biography</genre>
4521                         </xsl:if>
4522                         <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"/>
4523                         <xsl:choose>
4524                                 <xsl:when test="$controlField008-33='e'">
4525                                         <genre authority="marcgt">essay</genre>
4526                                 </xsl:when>
4527                                 <xsl:when test="$controlField008-33='d'">
4528                                         <genre authority="marcgt">drama</genre>
4529                                 </xsl:when>
4530                                 <xsl:when test="$controlField008-33='c'">
4531                                         <genre authority="marcgt">comic strip</genre>
4532                                 </xsl:when>
4533                                 <xsl:when test="$controlField008-33='l'">
4534                                         <genre authority="marcgt">fiction</genre>
4535                                 </xsl:when>
4536                                 <xsl:when test="$controlField008-33='h'">
4537                                         <genre authority="marcgt">humor, satire</genre>
4538                                 </xsl:when>
4539                                 <xsl:when test="$controlField008-33='i'">
4540                                         <genre authority="marcgt">letter</genre>
4541                                 </xsl:when>
4542                                 <xsl:when test="$controlField008-33='f'">
4543                                         <genre authority="marcgt">novel</genre>
4544                                 </xsl:when>
4545                                 <xsl:when test="$controlField008-33='j'">
4546                                         <genre authority="marcgt">short story</genre>
4547                                 </xsl:when>
4548                                 <xsl:when test="$controlField008-33='s'">
4549                                         <genre authority="marcgt">speech</genre>
4550                                 </xsl:when>
4551                         </xsl:choose>
4552                 </xsl:if>
4553                 <xsl:if test="$typeOf008='MU'">
4554                         <xsl:variable name="controlField008-30-31" select="substring($controlField008,31,2)"/>
4555                         <xsl:if test="contains($controlField008-30-31,'b')">
4556                                 <genre authority="marcgt">biography</genre>
4557                         </xsl:if>
4558                         <xsl:if test="contains($controlField008-30-31,'c')">
4559                                 <genre authority="marcgt">conference publication</genre>
4560                         </xsl:if>
4561                         <xsl:if test="contains($controlField008-30-31,'d')">
4562                                 <genre authority="marcgt">drama</genre>
4563                         </xsl:if>
4564                         <xsl:if test="contains($controlField008-30-31,'e')">
4565                                 <genre authority="marcgt">essay</genre>
4566                         </xsl:if>
4567                         <xsl:if test="contains($controlField008-30-31,'f')">
4568                                 <genre authority="marcgt">fiction</genre>
4569                         </xsl:if>
4570                         <xsl:if test="contains($controlField008-30-31,'o')">
4571                                 <genre authority="marcgt">folktale</genre>
4572                         </xsl:if>
4573                         <xsl:if test="contains($controlField008-30-31,'h')">
4574                                 <genre authority="marcgt">history</genre>
4575                         </xsl:if>
4576                         <xsl:if test="contains($controlField008-30-31,'k')">
4577                                 <genre authority="marcgt">humor, satire</genre>
4578                         </xsl:if>
4579                         <xsl:if test="contains($controlField008-30-31,'m')">
4580                                 <genre authority="marcgt">memoir</genre>
4581                         </xsl:if>
4582                         <xsl:if test="contains($controlField008-30-31,'p')">
4583                                 <genre authority="marcgt">poetry</genre>
4584                         </xsl:if>
4585                         <xsl:if test="contains($controlField008-30-31,'r')">
4586                                 <genre authority="marcgt">rehearsal</genre>
4587                         </xsl:if>
4588                         <xsl:if test="contains($controlField008-30-31,'g')">
4589                                 <genre authority="marcgt">reporting</genre>
4590                         </xsl:if>
4591                         <xsl:if test="contains($controlField008-30-31,'s')">
4592                                 <genre authority="marcgt">sound</genre>
4593                         </xsl:if>
4594                         <xsl:if test="contains($controlField008-30-31,'l')">
4595                                 <genre authority="marcgt">speech</genre>
4596                         </xsl:if>
4597                 </xsl:if>
4598                 <xsl:if test="$typeOf008='VM'">
4599                         <xsl:variable name="controlField008-33" select="substring($controlField008,34,1)"/>
4600                         <xsl:choose>
4601                                 <xsl:when test="$controlField008-33='a'">
4602                                         <genre authority="marcgt">art original</genre>
4603                                 </xsl:when>
4604                                 <xsl:when test="$controlField008-33='b'">
4605                                         <genre authority="marcgt">kit</genre>
4606                                 </xsl:when>
4607                                 <xsl:when test="$controlField008-33='c'">
4608                                         <genre authority="marcgt">art reproduction</genre>
4609                                 </xsl:when>
4610                                 <xsl:when test="$controlField008-33='d'">
4611                                         <genre authority="marcgt">diorama</genre>
4612                                 </xsl:when>
4613                                 <xsl:when test="$controlField008-33='f'">
4614                                         <genre authority="marcgt">filmstrip</genre>
4615                                 </xsl:when>
4616                                 <xsl:when test="$controlField008-33='g'">
4617                                         <genre authority="marcgt">legal article</genre>
4618                                 </xsl:when>
4619                                 <xsl:when test="$controlField008-33='i'">
4620                                         <genre authority="marcgt">picture</genre>
4621                                 </xsl:when>
4622                                 <xsl:when test="$controlField008-33='k'">
4623                                         <genre authority="marcgt">graphic</genre>
4624                                 </xsl:when>
4625                                 <xsl:when test="$controlField008-33='l'">
4626                                         <genre authority="marcgt">technical drawing</genre>
4627                                 </xsl:when>
4628                                 <xsl:when test="$controlField008-33='m'">
4629                                         <genre authority="marcgt">motion picture</genre>
4630                                 </xsl:when>
4631                                 <xsl:when test="$controlField008-33='n'">
4632                                         <genre authority="marcgt">chart</genre>
4633                                 </xsl:when>
4634                                 <xsl:when test="$controlField008-33='o'">
4635                                         <genre authority="marcgt">flash card</genre>
4636                                 </xsl:when>
4637                                 <xsl:when test="$controlField008-33='p'">
4638                                         <genre authority="marcgt">microscope slide</genre>
4639                                 </xsl:when>
4640                                 <xsl:when
4641                                         test="$controlField008-33='q' or marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
4642                                         <genre authority="marcgt">model</genre>
4643                                 </xsl:when>
4644                                 <xsl:when test="$controlField008-33='r'">
4645                                         <genre authority="marcgt">realia</genre>
4646                                 </xsl:when>
4647                                 <xsl:when test="$controlField008-33='s'">
4648                                         <genre authority="marcgt">slide</genre>
4649                                 </xsl:when>
4650                                 <xsl:when test="$controlField008-33='t'">
4651                                         <genre authority="marcgt">transparency</genre>
4652                                 </xsl:when>
4653                                 <xsl:when test="$controlField008-33='v'">
4654                                         <genre authority="marcgt">videorecording</genre>
4655                                 </xsl:when>
4656                                 <xsl:when test="$controlField008-33='w'">
4657                                         <genre authority="marcgt">toy</genre>
4658                                 </xsl:when>
4659                         </xsl:choose>
4660                 </xsl:if>
4661
4662                 <!-- 1.20 047 genre tmee-->
4663
4664                 <xsl:for-each select="marc:datafield[@tag=047]">
4665                         <genre authority="marcgt">
4666                                 <xsl:attribute name="authority">
4667                                         <xsl:value-of select="marc:subfield[@code='2']"/>
4668                                 </xsl:attribute>
4669                                 <xsl:call-template name="subfieldSelect">
4670                                         <xsl:with-param name="codes">abcdef</xsl:with-param>
4671                                         <xsl:with-param name="delimeter">-</xsl:with-param>
4672                                 </xsl:call-template>
4673                         </genre>
4674                 </xsl:for-each>
4675                 <xsl:for-each select="marc:datafield[@tag=655]">
4676                         <genre authority="marcgt">
4677                                 <xsl:attribute name="authority">
4678                                         <xsl:value-of select="marc:subfield[@code='2']"/>
4679                                 </xsl:attribute>
4680                                 <xsl:call-template name="subfieldSelect">
4681                                         <xsl:with-param name="codes">abvxyz</xsl:with-param>
4682                                         <xsl:with-param name="delimeter">-</xsl:with-param>
4683                                 </xsl:call-template>
4684                         </genre>
4685                 </xsl:for-each>
4686                 <originInfo>
4687                         <xsl:variable name="MARCpublicationCode"
4688                                 select="normalize-space(substring($controlField008,16,3))"/>
4689                         <xsl:if test="translate($MARCpublicationCode,'|','')">
4690                                 <place>
4691                                         <placeTerm>
4692                                                 <xsl:attribute name="type">code</xsl:attribute>
4693                                                 <xsl:attribute name="authority">marccountry</xsl:attribute>
4694                                                 <xsl:value-of select="$MARCpublicationCode"/>
4695                                         </placeTerm>
4696                                 </place>
4697                         </xsl:if>
4698                         <xsl:for-each select="marc:datafield[@tag=044]/marc:subfield[@code='c']">
4699                                 <place>
4700                                         <placeTerm>
4701                                                 <xsl:attribute name="type">code</xsl:attribute>
4702                                                 <xsl:attribute name="authority">iso3166</xsl:attribute>
4703                                                 <xsl:value-of select="."/>
4704                                         </placeTerm>
4705                                 </place>
4706                         </xsl:for-each>
4707                         <xsl:for-each select="marc:datafield[@tag=260]/marc:subfield[@code='a']">
4708                                 <place>
4709                                         <placeTerm>
4710                                                 <xsl:attribute name="type">text</xsl:attribute>
4711                                                 <xsl:call-template name="chopPunctuationFront">
4712                                                         <xsl:with-param name="chopString">
4713                                                                 <xsl:call-template name="chopPunctuation">
4714                                                                         <xsl:with-param name="chopString" select="."/>
4715                                                                 </xsl:call-template>
4716                                                         </xsl:with-param>
4717                                                 </xsl:call-template>
4718                                         </placeTerm>
4719                                 </place>
4720                         </xsl:for-each>
4721                         <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='m']">
4722                                 <dateValid point="start">
4723                                         <xsl:value-of select="."/>
4724                                 </dateValid>
4725                         </xsl:for-each>
4726                         <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='n']">
4727                                 <dateValid point="end">
4728                                         <xsl:value-of select="."/>
4729                                 </dateValid>
4730                         </xsl:for-each>
4731                         <xsl:for-each select="marc:datafield[@tag=046]/marc:subfield[@code='j']">
4732                                 <dateModified>
4733                                         <xsl:value-of select="."/>
4734                                 </dateModified>
4735                         </xsl:for-each>
4736                         <xsl:for-each
4737                                 select="marc:datafield[@tag=260]/marc:subfield[@code='b' or @code='c' or @code='g']">
4738                                 <xsl:choose>
4739                                         <xsl:when test="@code='b'">
4740                                                 <publisher>
4741                                                         <xsl:call-template name="chopPunctuation">
4742                                                                 <xsl:with-param name="chopString" select="."/>
4743                                                                 <xsl:with-param name="punctuation">
4744                                                                         <xsl:text>:,;/ </xsl:text>
4745                                                                 </xsl:with-param>
4746                                                         </xsl:call-template>
4747                                                 </publisher>
4748                                         </xsl:when>
4749                                         <xsl:when test="@code='c'">
4750                                                 <dateIssued>
4751                                                         <xsl:call-template name="chopPunctuation">
4752                                                                 <xsl:with-param name="chopString" select="."/>
4753                                                         </xsl:call-template>
4754                                                 </dateIssued>
4755                                         </xsl:when>
4756                                         <xsl:when test="@code='g'">
4757                                                 <dateCreated>
4758                                                         <xsl:value-of select="."/>
4759                                                 </dateCreated>
4760                                         </xsl:when>
4761                                 </xsl:choose>
4762                         </xsl:for-each>
4763                         <xsl:variable name="dataField260c">
4764                                 <xsl:call-template name="chopPunctuation">
4765                                         <xsl:with-param name="chopString"
4766                                                 select="marc:datafield[@tag=260]/marc:subfield[@code='c']"/>
4767                                 </xsl:call-template>
4768                         </xsl:variable>
4769                         <xsl:variable name="controlField008-7-10"
4770                                 select="normalize-space(substring($controlField008, 8, 4))"/>
4771                         <xsl:variable name="controlField008-11-14"
4772                                 select="normalize-space(substring($controlField008, 12, 4))"/>
4773                         <xsl:variable name="controlField008-6"
4774                                 select="normalize-space(substring($controlField008, 7, 1))"/>
4775                         <xsl:if
4776                                 test="$controlField008-6='e' or $controlField008-6='p' or $controlField008-6='r' or $controlField008-6='t' or $controlField008-6='s'">
4777                                 <xsl:if test="$controlField008-7-10 and ($controlField008-7-10 != $dataField260c)">
4778                                         <dateIssued encoding="marc">
4779                                                 <xsl:value-of select="$controlField008-7-10"/>
4780                                         </dateIssued>
4781                                 </xsl:if>
4782                         </xsl:if>
4783                         <xsl:if
4784                                 test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
4785                                 <xsl:if test="$controlField008-7-10">
4786                                         <dateIssued encoding="marc" point="start">
4787                                                 <xsl:value-of select="$controlField008-7-10"/>
4788                                         </dateIssued>
4789                                 </xsl:if>
4790                         </xsl:if>
4791                         <xsl:if
4792                                 test="$controlField008-6='c' or $controlField008-6='d' or $controlField008-6='i' or $controlField008-6='k' or $controlField008-6='m' or $controlField008-6='q' or $controlField008-6='u'">
4793                                 <xsl:if test="$controlField008-11-14">
4794                                         <dateIssued encoding="marc" point="end">
4795                                                 <xsl:value-of select="$controlField008-11-14"/>
4796                                         </dateIssued>
4797                                 </xsl:if>
4798                         </xsl:if>
4799                         <xsl:if test="$controlField008-6='q'">
4800                                 <xsl:if test="$controlField008-7-10">
4801                                         <dateIssued encoding="marc" point="start" qualifier="questionable">
4802                                                 <xsl:value-of select="$controlField008-7-10"/>
4803                                         </dateIssued>
4804                                 </xsl:if>
4805                         </xsl:if>
4806                         <xsl:if test="$controlField008-6='q'">
4807                                 <xsl:if test="$controlField008-11-14">
4808                                         <dateIssued encoding="marc" point="end" qualifier="questionable">
4809                                                 <xsl:value-of select="$controlField008-11-14"/>
4810                                         </dateIssued>
4811                                 </xsl:if>
4812                         </xsl:if>
4813                         <xsl:if test="$controlField008-6='t'">
4814                                 <xsl:if test="$controlField008-11-14">
4815                                         <copyrightDate encoding="marc">
4816                                                 <xsl:value-of select="$controlField008-11-14"/>
4817                                         </copyrightDate>
4818                                 </xsl:if>
4819                         </xsl:if>
4820                         <xsl:for-each
4821                                 select="marc:datafield[@tag=033][@ind1=0 or @ind1=1]/marc:subfield[@code='a']">
4822                                 <dateCaptured encoding="iso8601">
4823                                         <xsl:value-of select="."/>
4824                                 </dateCaptured>
4825                         </xsl:for-each>
4826                         <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][1]">
4827                                 <dateCaptured encoding="iso8601" point="start">
4828                                         <xsl:value-of select="."/>
4829                                 </dateCaptured>
4830                         </xsl:for-each>
4831                         <xsl:for-each select="marc:datafield[@tag=033][@ind1=2]/marc:subfield[@code='a'][2]">
4832                                 <dateCaptured encoding="iso8601" point="end">
4833                                         <xsl:value-of select="."/>
4834                                 </dateCaptured>
4835                         </xsl:for-each>
4836                         <xsl:for-each select="marc:datafield[@tag=250]/marc:subfield[@code='a']">
4837                                 <edition>
4838                                         <xsl:value-of select="."/>
4839                                 </edition>
4840                         </xsl:for-each>
4841                         <xsl:for-each select="marc:leader">
4842                                 <issuance>
4843                                         <xsl:choose>
4844                                                 <xsl:when
4845                                                         test="$leader7='a' or $leader7='c' or $leader7='d' or $leader7='m'"
4846                                                         >monographic</xsl:when>
4847                                                 <xsl:when test="$leader7='b' or $leader7='i' or $leader7='s'"
4848                                                 >continuing</xsl:when>
4849                                         </xsl:choose>
4850                                 </issuance>
4851                         </xsl:for-each>
4852                         <xsl:for-each select="marc:datafield[@tag=310]|marc:datafield[@tag=321]">
4853                                 <frequency>
4854                                         <xsl:call-template name="subfieldSelect">
4855                                                 <xsl:with-param name="codes">ab</xsl:with-param>
4856                                         </xsl:call-template>
4857                                 </frequency>
4858                         </xsl:for-each>
4859                 </originInfo>
4860                 <xsl:variable name="controlField008-35-37"
4861                         select="normalize-space(translate(substring($controlField008,36,3),'|#',''))"/>
4862                 <xsl:if test="$controlField008-35-37">
4863                         <language>
4864                                 <languageTerm authority="iso639-2b" type="code">
4865                                         <xsl:value-of select="substring($controlField008,36,3)"/>
4866                                 </languageTerm>
4867                         </language>
4868                 </xsl:if>
4869                 <xsl:for-each select="marc:datafield[@tag=041]">
4870                         <xsl:for-each
4871                                 select="marc:subfield[@code='a' or @code='b' or @code='d' or @code='e' or @code='f' or @code='g' or @code='h']">
4872                                 <xsl:variable name="langCodes" select="."/>
4873                                 <xsl:choose>
4874                                         <xsl:when test="../marc:subfield[@code='2']='rfc3066'">
4875                                                 <!-- not stacked but could be repeated -->
4876                                                 <xsl:call-template name="rfcLanguages">
4877                                                         <xsl:with-param name="nodeNum">
4878                                                                 <xsl:value-of select="1"/>
4879                                                         </xsl:with-param>
4880                                                         <xsl:with-param name="usedLanguages">
4881                                                                 <xsl:text/>
4882                                                         </xsl:with-param>
4883                                                         <xsl:with-param name="controlField008-35-37">
4884                                                                 <xsl:value-of select="$controlField008-35-37"/>
4885                                                         </xsl:with-param>
4886                                                 </xsl:call-template>
4887                                         </xsl:when>
4888                                         <xsl:otherwise>
4889                                                 <!-- iso -->
4890                                                 <xsl:variable name="allLanguages">
4891                                                         <xsl:copy-of select="$langCodes"/>
4892                                                 </xsl:variable>
4893                                                 <xsl:variable name="currentLanguage">
4894                                                         <xsl:value-of select="substring($allLanguages,1,3)"/>
4895                                                 </xsl:variable>
4896                                                 <xsl:call-template name="isoLanguage">
4897                                                         <xsl:with-param name="currentLanguage">
4898                                                                 <xsl:value-of select="substring($allLanguages,1,3)"/>
4899                                                         </xsl:with-param>
4900                                                         <xsl:with-param name="remainingLanguages">
4901                                                                 <xsl:value-of
4902                                                                         select="substring($allLanguages,4,string-length($allLanguages)-3)"
4903                                                                 />
4904                                                         </xsl:with-param>
4905                                                         <xsl:with-param name="usedLanguages">
4906                                                                 <xsl:if test="$controlField008-35-37">
4907                                                                         <xsl:value-of select="$controlField008-35-37"/>
4908                                                                 </xsl:if>
4909                                                         </xsl:with-param>
4910                                                 </xsl:call-template>
4911                                         </xsl:otherwise>
4912                                 </xsl:choose>
4913                         </xsl:for-each>
4914                 </xsl:for-each>
4915                 <xsl:variable name="physicalDescription">
4916                         <!--3.2 change tmee 007/11 -->
4917                         <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='a']">
4918                                 <digitalOrigin>reformatted digital</digitalOrigin>
4919                         </xsl:if>
4920                         <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='b']">
4921                                 <digitalOrigin>digitized microfilm</digitalOrigin>
4922                         </xsl:if>
4923                         <xsl:if test="$typeOf008='CF' and marc:controlfield[@tag=007][substring(.,12,1)='d']">
4924                                 <digitalOrigin>digitized other analog</digitalOrigin>
4925                         </xsl:if>
4926                         <xsl:variable name="controlField008-23" select="substring($controlField008,24,1)"/>
4927                         <xsl:variable name="controlField008-29" select="substring($controlField008,30,1)"/>
4928                         <xsl:variable name="check008-23">
4929                                 <xsl:if
4930                                         test="$typeOf008='BK' or $typeOf008='MU' or $typeOf008='SE' or $typeOf008='MM'">
4931                                         <xsl:value-of select="true()"/>
4932                                 </xsl:if>
4933                         </xsl:variable>
4934                         <xsl:variable name="check008-29">
4935                                 <xsl:if test="$typeOf008='MP' or $typeOf008='VM'">
4936                                         <xsl:value-of select="true()"/>
4937                                 </xsl:if>
4938                         </xsl:variable>
4939                         <xsl:choose>
4940                                 <xsl:when
4941                                         test="($check008-23 and $controlField008-23='f') or ($check008-29 and $controlField008-29='f')">
4942                                         <form authority="marcform">braille</form>
4943                                 </xsl:when>
4944                                 <xsl:when
4945                                         test="($controlField008-23=' ' and ($leader6='c' or $leader6='d')) or (($typeOf008='BK' or $typeOf008='SE') and ($controlField008-23=' ' or $controlField008='r'))">
4946                                         <form authority="marcform">print</form>
4947                                 </xsl:when>
4948                                 <xsl:when
4949                                         test="$leader6 = 'm' or ($check008-23 and $controlField008-23='s') or ($check008-29 and $controlField008-29='s')">
4950                                         <form authority="marcform">electronic</form>
4951                                 </xsl:when>
4952                                 <xsl:when
4953                                         test="($check008-23 and $controlField008-23='b') or ($check008-29 and $controlField008-29='b')">
4954                                         <form authority="marcform">microfiche</form>
4955                                 </xsl:when>
4956                                 <xsl:when
4957                                         test="($check008-23 and $controlField008-23='a') or ($check008-29 and $controlField008-29='a')">
4958                                         <form authority="marcform">microfilm</form>
4959                                 </xsl:when>
4960                         </xsl:choose>
4961                         <!-- 1/04 fix -->
4962                         <xsl:if test="marc:datafield[@tag=130]/marc:subfield[@code='h']">
4963                                 <form authority="gmd">
4964                                         <xsl:call-template name="chopBrackets">
4965                                                 <xsl:with-param name="chopString">
4966                                                         <xsl:value-of select="marc:datafield[@tag=130]/marc:subfield[@code='h']"
4967                                                         />
4968                                                 </xsl:with-param>
4969                                         </xsl:call-template>
4970                                 </form>
4971                         </xsl:if>
4972                         <xsl:if test="marc:datafield[@tag=240]/marc:subfield[@code='h']">
4973                                 <form authority="gmd">
4974                                         <xsl:call-template name="chopBrackets">
4975                                                 <xsl:with-param name="chopString">
4976                                                         <xsl:value-of select="marc:datafield[@tag=240]/marc:subfield[@code='h']"
4977                                                         />
4978                                                 </xsl:with-param>
4979                                         </xsl:call-template>
4980                                 </form>
4981                         </xsl:if>
4982                         <xsl:if test="marc:datafield[@tag=242]/marc:subfield[@code='h']">
4983                                 <form authority="gmd">
4984                                         <xsl:call-template name="chopBrackets">
4985                                                 <xsl:with-param name="chopString">
4986                                                         <xsl:value-of select="marc:datafield[@tag=242]/marc:subfield[@code='h']"
4987                                                         />
4988                                                 </xsl:with-param>
4989                                         </xsl:call-template>
4990                                 </form>
4991                         </xsl:if>
4992                         <xsl:if test="marc:datafield[@tag=245]/marc:subfield[@code='h']">
4993                                 <form authority="gmd">
4994                                         <xsl:call-template name="chopBrackets">
4995                                                 <xsl:with-param name="chopString">
4996                                                         <xsl:value-of select="marc:datafield[@tag=245]/marc:subfield[@code='h']"
4997                                                         />
4998                                                 </xsl:with-param>
4999                                         </xsl:call-template>
5000                                 </form>
5001                         </xsl:if>
5002                         <xsl:if test="marc:datafield[@tag=246]/marc:subfield[@code='h']">
5003                                 <form authority="gmd">
5004                                         <xsl:call-template name="chopBrackets">
5005                                                 <xsl:with-param name="chopString">
5006                                                         <xsl:value-of select="marc:datafield[@tag=246]/marc:subfield[@code='h']"
5007                                                         />
5008                                                 </xsl:with-param>
5009                                         </xsl:call-template>
5010                                 </form>
5011                         </xsl:if>
5012                         <xsl:if test="marc:datafield[@tag=730]/marc:subfield[@code='h']">
5013                                 <form authority="gmd">
5014                                         <xsl:call-template name="chopBrackets">
5015                                                 <xsl:with-param name="chopString">
5016                                                         <xsl:value-of select="marc:datafield[@tag=730]/marc:subfield[@code='h']"
5017                                                         />
5018                                                 </xsl:with-param>
5019                                         </xsl:call-template>
5020                                 </form>
5021                         </xsl:if>
5022                         <xsl:for-each select="marc:datafield[@tag=256]/marc:subfield[@code='a']">
5023                                 <form>
5024                                         <xsl:value-of select="."/>
5025                                 </form>
5026                         </xsl:for-each>
5027                         <xsl:for-each select="marc:controlfield[@tag=007][substring(text(),1,1)='c']">
5028                                 <xsl:choose>
5029                                         <xsl:when test="substring(text(),14,1)='a'">
5030                                                 <reformattingQuality>access</reformattingQuality>
5031                                         </xsl:when>
5032                                         <xsl:when test="substring(text(),14,1)='p'">
5033                                                 <reformattingQuality>preservation</reformattingQuality>
5034                                         </xsl:when>
5035                                         <xsl:when test="substring(text(),14,1)='r'">
5036                                                 <reformattingQuality>replacement</reformattingQuality>
5037                                         </xsl:when>
5038                                 </xsl:choose>
5039                         </xsl:for-each>
5040                         <!--3.2 change tmee 007/01 -->
5041                         <xsl:if
5042                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='b']">
5043                                 <form authority="smd">chip cartridge</form>
5044                         </xsl:if>
5045                         <xsl:if
5046                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='c']">
5047                                 <form authority="smd">computer optical disc cartridge</form>
5048                         </xsl:if>
5049                         <xsl:if
5050                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='j']">
5051                                 <form authority="smd">magnetic disc</form>
5052                         </xsl:if>
5053                         <xsl:if
5054                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='m']">
5055                                 <form authority="smd">magneto-optical disc</form>
5056                         </xsl:if>
5057                         <xsl:if
5058                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='o']">
5059                                 <form authority="smd">optical disc</form>
5060                         </xsl:if>
5061                         <xsl:if
5062                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='r']">
5063                                 <form authority="smd">remote</form>
5064                         </xsl:if>
5065                         <xsl:if
5066                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='a']">
5067                                 <form authority="smd">tape cartridge</form>
5068                         </xsl:if>
5069                         <xsl:if
5070                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='f']">
5071                                 <form authority="smd">tape cassette</form>
5072                         </xsl:if>
5073                         <xsl:if
5074                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='c'][substring(text(),2,1)='h']">
5075                                 <form authority="smd">tape reel</form>
5076                         </xsl:if>
5077
5078                         <xsl:if
5079                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='a']">
5080                                 <form authority="smd">celestial globe</form>
5081                         </xsl:if>
5082                         <xsl:if
5083                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='e']">
5084                                 <form authority="smd">earth moon globe</form>
5085                         </xsl:if>
5086                         <xsl:if
5087                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='b']">
5088                                 <form authority="smd">planetary or lunar globe</form>
5089                         </xsl:if>
5090                         <xsl:if
5091                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='d'][substring(text(),2,1)='c']">
5092                                 <form authority="smd">terrestrial globe</form>
5093                         </xsl:if>
5094
5095                         <xsl:if
5096                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='o'][substring(text(),2,1)='o']">
5097                                 <form authority="smd">kit</form>
5098                         </xsl:if>
5099
5100                         <xsl:if
5101                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='d']">
5102                                 <form authority="smd">atlas</form>
5103                         </xsl:if>
5104                         <xsl:if
5105                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='g']">
5106                                 <form authority="smd">diagram</form>
5107                         </xsl:if>
5108                         <xsl:if
5109                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='j']">
5110                                 <form authority="smd">map</form>
5111                         </xsl:if>
5112                         <xsl:if
5113                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='q']">
5114                                 <form authority="smd">model</form>
5115                         </xsl:if>
5116                         <xsl:if
5117                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='k']">
5118                                 <form authority="smd">profile</form>
5119                         </xsl:if>
5120                         <xsl:if
5121                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='r']">
5122                                 <form authority="smd">remote-sensing image</form>
5123                         </xsl:if>
5124                         <xsl:if
5125                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='s']">
5126                                 <form authority="smd">section</form>
5127                         </xsl:if>
5128                         <xsl:if
5129                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='a'][substring(text(),2,1)='y']">
5130                                 <form authority="smd">view</form>
5131                         </xsl:if>
5132
5133                         <xsl:if
5134                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='a']">
5135                                 <form authority="smd">aperture card</form>
5136                         </xsl:if>
5137                         <xsl:if
5138                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='e']">
5139                                 <form authority="smd">microfiche</form>
5140                         </xsl:if>
5141                         <xsl:if
5142                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='f']">
5143                                 <form authority="smd">microfiche cassette</form>
5144                         </xsl:if>
5145                         <xsl:if
5146                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='b']">
5147                                 <form authority="smd">microfilm cartridge</form>
5148                         </xsl:if>
5149                         <xsl:if
5150                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='c']">
5151                                 <form authority="smd">microfilm cassette</form>
5152                         </xsl:if>
5153                         <xsl:if
5154                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='d']">
5155                                 <form authority="smd">microfilm reel</form>
5156                         </xsl:if>
5157                         <xsl:if
5158                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='h'][substring(text(),2,1)='g']">
5159                                 <form authority="smd">microopaque</form>
5160                         </xsl:if>
5161
5162                         <xsl:if
5163                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='c']">
5164                                 <form authority="smd">film cartridge</form>
5165                         </xsl:if>
5166                         <xsl:if
5167                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='f']">
5168                                 <form authority="smd">film cassette</form>
5169                         </xsl:if>
5170                         <xsl:if
5171                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='m'][substring(text(),2,1)='r']">
5172                                 <form authority="smd">film reel</form>
5173                         </xsl:if>
5174
5175                         <xsl:if
5176                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='n']">
5177                                 <form authority="smd">chart</form>
5178                         </xsl:if>
5179                         <xsl:if
5180                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='c']">
5181                                 <form authority="smd">collage</form>
5182                         </xsl:if>
5183                         <xsl:if
5184                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='d']">
5185                                 <form authority="smd">drawing</form>
5186                         </xsl:if>
5187                         <xsl:if
5188                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='o']">
5189                                 <form authority="smd">flash card</form>
5190                         </xsl:if>
5191                         <xsl:if
5192                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='e']">
5193                                 <form authority="smd">painting</form>
5194                         </xsl:if>
5195                         <xsl:if
5196                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='f']">
5197                                 <form authority="smd">photomechanical print</form>
5198                         </xsl:if>
5199                         <xsl:if
5200                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='g']">
5201                                 <form authority="smd">photonegative</form>
5202                         </xsl:if>
5203                         <xsl:if
5204                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='h']">
5205                                 <form authority="smd">photoprint</form>
5206                         </xsl:if>
5207                         <xsl:if
5208                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='i']">
5209                                 <form authority="smd">picture</form>
5210                         </xsl:if>
5211                         <xsl:if
5212                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='j']">
5213                                 <form authority="smd">print</form>
5214                         </xsl:if>
5215                         <xsl:if
5216                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='k'][substring(text(),2,1)='l']">
5217                                 <form authority="smd">technical drawing</form>
5218                         </xsl:if>
5219
5220                         <xsl:if
5221                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='q'][substring(text(),2,1)='q']">
5222                                 <form authority="smd">notated music</form>
5223                         </xsl:if>
5224
5225                         <xsl:if
5226                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='d']">
5227                                 <form authority="smd">filmslip</form>
5228                         </xsl:if>
5229                         <xsl:if
5230                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='c']">
5231                                 <form authority="smd">filmstrip cartridge</form>
5232                         </xsl:if>
5233                         <xsl:if
5234                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='o']">
5235                                 <form authority="smd">filmstrip roll</form>
5236                         </xsl:if>
5237                         <xsl:if
5238                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='f']">
5239                                 <form authority="smd">other filmstrip type</form>
5240                         </xsl:if>
5241                         <xsl:if
5242                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='s']">
5243                                 <form authority="smd">slide</form>
5244                         </xsl:if>
5245                         <xsl:if
5246                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='g'][substring(text(),2,1)='t']">
5247                                 <form authority="smd">transparency</form>
5248                         </xsl:if>
5249                         <xsl:if
5250                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='r'][substring(text(),2,1)='r']">
5251                                 <form authority="smd">remote-sensing image</form>
5252                         </xsl:if>
5253                         <xsl:if
5254                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='e']">
5255                                 <form authority="smd">cylinder</form>
5256                         </xsl:if>
5257                         <xsl:if
5258                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='q']">
5259                                 <form authority="smd">roll</form>
5260                         </xsl:if>
5261                         <xsl:if
5262                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='g']">
5263                                 <form authority="smd">sound cartridge</form>
5264                         </xsl:if>
5265                         <xsl:if
5266                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='s']">
5267                                 <form authority="smd">sound cassette</form>
5268                         </xsl:if>
5269                         <xsl:if
5270                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='d']">
5271                                 <form authority="smd">sound disc</form>
5272                         </xsl:if>
5273                         <xsl:if
5274                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='t']">
5275                                 <form authority="smd">sound-tape reel</form>
5276                         </xsl:if>
5277                         <xsl:if
5278                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='i']">
5279                                 <form authority="smd">sound-track film</form>
5280                         </xsl:if>
5281                         <xsl:if
5282                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='s'][substring(text(),2,1)='w']">
5283                                 <form authority="smd">wire recording</form>
5284                         </xsl:if>
5285
5286                         <xsl:if
5287                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='c']">
5288                                 <form authority="smd">braille</form>
5289                         </xsl:if>
5290                         <xsl:if
5291                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='b']">
5292                                 <form authority="smd">combination</form>
5293                         </xsl:if>
5294                         <xsl:if
5295                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='a']">
5296                                 <form authority="smd">moon</form>
5297                         </xsl:if>
5298                         <xsl:if
5299                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='f'][substring(text(),2,1)='d']">
5300                                 <form authority="smd">tactile, with no writing system</form>
5301                         </xsl:if>
5302
5303                         <xsl:if
5304                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='c']">
5305                                 <form authority="smd">braille</form>
5306                         </xsl:if>
5307                         <xsl:if
5308                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='b']">
5309                                 <form authority="smd">large print</form>
5310                         </xsl:if>
5311                         <xsl:if
5312                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='a']">
5313                                 <form authority="smd">regular print</form>
5314                         </xsl:if>
5315                         <xsl:if
5316                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='t'][substring(text(),2,1)='d']">
5317                                 <form authority="smd">text in looseleaf binder</form>
5318                         </xsl:if>
5319
5320                         <xsl:if
5321                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='c']">
5322                                 <form authority="smd">videocartridge</form>
5323                         </xsl:if>
5324                         <xsl:if
5325                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='f']">
5326                                 <form authority="smd">videocassette</form>
5327                         </xsl:if>
5328                         <xsl:if
5329                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='d']">
5330                                 <form authority="smd">videodisc</form>
5331                         </xsl:if>
5332                         <xsl:if
5333                                 test="marc:controlfield[@tag=007][substring(text(),1,1)='v'][substring(text(),2,1)='r']">
5334                                 <form authority="smd">videoreel</form>
5335                         </xsl:if>
5336
5337                         <xsl:for-each
5338                                 select="marc:datafield[@tag=856]/marc:subfield[@code='q'][string-length(.)&gt;1]">
5339                                 <internetMediaType>
5340                                         <xsl:value-of select="."/>
5341                                 </internetMediaType>
5342                         </xsl:for-each>
5343                         <xsl:for-each select="marc:datafield[@tag=300]">
5344                                 <extent>
5345                                         <xsl:call-template name="subfieldSelect">
5346                                                 <xsl:with-param name="codes">abce</xsl:with-param>
5347                                         </xsl:call-template>
5348                                 </extent>
5349                         </xsl:for-each>
5350                 </xsl:variable>
5351                 <xsl:if test="string-length(normalize-space($physicalDescription))">
5352                         <physicalDescription>
5353                                 <xsl:copy-of select="$physicalDescription"/>
5354                         </physicalDescription>
5355                 </xsl:if>
5356                 <xsl:for-each select="marc:datafield[@tag=520]">
5357                         <abstract>
5358                                 <xsl:call-template name="uri"/>
5359                                 <xsl:call-template name="subfieldSelect">
5360                                         <xsl:with-param name="codes">ab</xsl:with-param>
5361                                 </xsl:call-template>
5362                         </abstract>
5363                 </xsl:for-each>
5364                 <xsl:for-each select="marc:datafield[@tag=505]">
5365                         <tableOfContents>
5366                                 <xsl:call-template name="uri"/>
5367                                 <xsl:call-template name="subfieldSelect">
5368                                         <xsl:with-param name="codes">agrt</xsl:with-param>
5369                                 </xsl:call-template>
5370                         </tableOfContents>
5371                 </xsl:for-each>
5372                 <xsl:for-each select="marc:datafield[@tag=521]">
5373                         <targetAudience>
5374                                 <xsl:call-template name="subfieldSelect">
5375                                         <xsl:with-param name="codes">ab</xsl:with-param>
5376                                 </xsl:call-template>
5377                         </targetAudience>
5378                 </xsl:for-each>
5379                 <xsl:if test="$typeOf008='BK' or $typeOf008='CF' or $typeOf008='MU' or $typeOf008='VM'">
5380                         <xsl:variable name="controlField008-22" select="substring($controlField008,23,1)"/>
5381                         <xsl:choose>
5382                                 <!-- 01/04 fix -->
5383                                 <xsl:when test="$controlField008-22='d'">
5384                                         <targetAudience authority="marctarget">adolescent</targetAudience>
5385                                 </xsl:when>
5386                                 <xsl:when test="$controlField008-22='e'">
5387                                         <targetAudience authority="marctarget">adult</targetAudience>
5388                                 </xsl:when>
5389                                 <xsl:when test="$controlField008-22='g'">
5390                                         <targetAudience authority="marctarget">general</targetAudience>
5391                                 </xsl:when>
5392                                 <xsl:when
5393                                         test="$controlField008-22='b' or $controlField008-22='c' or $controlField008-22='j'">
5394                                         <targetAudience authority="marctarget">juvenile</targetAudience>
5395                                 </xsl:when>
5396                                 <xsl:when test="$controlField008-22='a'">
5397                                         <targetAudience authority="marctarget">preschool</targetAudience>
5398                                 </xsl:when>
5399                                 <xsl:when test="$controlField008-22='f'">
5400                                         <targetAudience authority="marctarget">specialized</targetAudience>
5401                                 </xsl:when>
5402                         </xsl:choose>
5403                 </xsl:if>
5404                 <xsl:for-each select="marc:datafield[@tag=245]/marc:subfield[@code='c']">
5405                         <note type="statement of responsibility">
5406                                 <xsl:value-of select="."/>
5407                         </note>
5408                 </xsl:for-each>
5409                 <xsl:for-each select="marc:datafield[@tag=500]">
5410                         <note>
5411                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5412                                 <xsl:call-template name="uri"/>
5413                         </note>
5414                 </xsl:for-each>
5415
5416                 <!--3.2 change tmee additional note fields-->
5417
5418                 <xsl:for-each select="marc:datafield[@tag=506]">
5419                         <note type="restrictions">
5420                                 <xsl:call-template name="uri"/>
5421                                 <xsl:variable name="str">
5422                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5423                                                 <xsl:value-of select="."/>
5424                                                 <xsl:text> </xsl:text>
5425                                         </xsl:for-each>
5426                                 </xsl:variable>
5427                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5428                         </note>
5429                 </xsl:for-each>
5430
5431                 <xsl:for-each select="marc:datafield[@tag=510]">
5432                         <note type="citation/reference">
5433                                 <xsl:call-template name="uri"/>
5434                                 <xsl:variable name="str">
5435                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5436                                                 <xsl:value-of select="."/>
5437                                                 <xsl:text> </xsl:text>
5438                                         </xsl:for-each>
5439                                 </xsl:variable>
5440                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5441                         </note>
5442                 </xsl:for-each>
5443
5444
5445                 <xsl:for-each select="marc:datafield[@tag=511]">
5446                         <note type="performers">
5447                                 <xsl:call-template name="uri"/>
5448                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5449                         </note>
5450                 </xsl:for-each>
5451                 <xsl:for-each select="marc:datafield[@tag=518]">
5452                         <note type="venue">
5453                                 <xsl:call-template name="uri"/>
5454                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5455                         </note>
5456                 </xsl:for-each>
5457
5458                 <xsl:for-each select="marc:datafield[@tag=530]">
5459                         <note type="additional physical form">
5460                                 <xsl:call-template name="uri"/>
5461                                 <xsl:variable name="str">
5462                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5463                                                 <xsl:value-of select="."/>
5464                                                 <xsl:text> </xsl:text>
5465                                         </xsl:for-each>
5466                                 </xsl:variable>
5467                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5468                         </note>
5469                 </xsl:for-each>
5470
5471                 <xsl:for-each select="marc:datafield[@tag=533]">
5472                         <note type="reproduction">
5473                                 <xsl:call-template name="uri"/>
5474                                 <xsl:variable name="str">
5475                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5476                                                 <xsl:value-of select="."/>
5477                                                 <xsl:text> </xsl:text>
5478                                         </xsl:for-each>
5479                                 </xsl:variable>
5480                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5481                         </note>
5482                 </xsl:for-each>
5483
5484                 <xsl:for-each select="marc:datafield[@tag=534]">
5485                         <note type="original version">
5486                                 <xsl:call-template name="uri"/>
5487                                 <xsl:variable name="str">
5488                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5489                                                 <xsl:value-of select="."/>
5490                                                 <xsl:text> </xsl:text>
5491                                         </xsl:for-each>
5492                                 </xsl:variable>
5493                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5494                         </note>
5495                 </xsl:for-each>
5496
5497                 <xsl:for-each select="marc:datafield[@tag=538]">
5498                         <note type="system details">
5499                                 <xsl:call-template name="uri"/>
5500                                 <xsl:variable name="str">
5501                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5502                                                 <xsl:value-of select="."/>
5503                                                 <xsl:text> </xsl:text>
5504                                         </xsl:for-each>
5505                                 </xsl:variable>
5506                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5507                         </note>
5508                 </xsl:for-each>
5509
5510                 <xsl:for-each select="marc:datafield[@tag=583]">
5511                         <note type="action">
5512                                 <xsl:call-template name="uri"/>
5513                                 <xsl:variable name="str">
5514                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5515                                                 <xsl:value-of select="."/>
5516                                                 <xsl:text> </xsl:text>
5517                                         </xsl:for-each>
5518                                 </xsl:variable>
5519                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5520                         </note>
5521                 </xsl:for-each>
5522
5523                 <xsl:for-each
5524                         select="marc:datafield[@tag=501 or @tag=502 or @tag=504 or @tag=507 or @tag=508 or  @tag=513 or @tag=514 or @tag=515 or @tag=516 or @tag=522 or @tag=524 or @tag=525 or @tag=526 or @tag=535 or @tag=536 or @tag=540 or @tag=541 or @tag=544 or @tag=545 or @tag=546 or @tag=547 or @tag=550 or @tag=552 or @tag=555 or @tag=556 or @tag=561 or @tag=562 or @tag=565 or @tag=567 or @tag=580 or @tag=581 or @tag=584 or @tag=585 or @tag=586]">
5525                         <note>
5526                                 <xsl:call-template name="uri"/>
5527                                 <xsl:variable name="str">
5528                                         <xsl:for-each select="marc:subfield[@code!='6' or @code!='8']">
5529                                                 <xsl:value-of select="."/>
5530                                                 <xsl:text> </xsl:text>
5531                                         </xsl:for-each>
5532                                 </xsl:variable>
5533                                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
5534                         </note>
5535                 </xsl:for-each>
5536                 <xsl:for-each
5537                         select="marc:datafield[@tag=034][marc:subfield[@code='d' or @code='e' or @code='f' or @code='g']]">
5538                         <subject>
5539                                 <cartographics>
5540                                         <coordinates>
5541                                                 <xsl:call-template name="subfieldSelect">
5542                                                         <xsl:with-param name="codes">defg</xsl:with-param>
5543                                                 </xsl:call-template>
5544                                         </coordinates>
5545                                 </cartographics>
5546                         </subject>
5547                 </xsl:for-each>
5548                 <xsl:for-each select="marc:datafield[@tag=043]">
5549                         <subject>
5550                                 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
5551                                         <geographicCode>
5552                                                 <xsl:attribute name="authority">
5553                                                         <xsl:if test="@code='a'">
5554                                                                 <xsl:text>marcgac</xsl:text>
5555                                                         </xsl:if>
5556                                                         <xsl:if test="@code='b'">
5557                                                                 <xsl:value-of select="following-sibling::marc:subfield[@code=2]"/>
5558                                                         </xsl:if>
5559                                                         <xsl:if test="@code='c'">
5560                                                                 <xsl:text>iso3166</xsl:text>
5561                                                         </xsl:if>
5562                                                 </xsl:attribute>
5563                                                 <xsl:value-of select="self::marc:subfield"/>
5564                                         </geographicCode>
5565                                 </xsl:for-each>
5566                         </subject>
5567                 </xsl:for-each>
5568                 <!-- tmee 2006/11/27 -->
5569                 <xsl:for-each select="marc:datafield[@tag=255]">
5570                         <subject>
5571                                 <xsl:for-each select="marc:subfield[@code='a' or @code='b' or @code='c']">
5572                                         <cartographics>
5573                                                 <xsl:if test="@code='a'">
5574                                                         <scale>
5575                                                                 <xsl:value-of select="."/>
5576                                                         </scale>
5577                                                 </xsl:if>
5578                                                 <xsl:if test="@code='b'">
5579                                                         <projection>
5580                                                                 <xsl:value-of select="."/>
5581                                                         </projection>
5582                                                 </xsl:if>
5583                                                 <xsl:if test="@code='c'">
5584                                                         <coordinates>
5585                                                                 <xsl:value-of select="."/>
5586                                                         </coordinates>
5587                                                 </xsl:if>
5588                                         </cartographics>
5589                                 </xsl:for-each>
5590                         </subject>
5591                 </xsl:for-each>
5592
5593                 <xsl:apply-templates select="marc:datafield[653 &gt;= @tag and @tag &gt;= 600]"/>
5594                 <xsl:apply-templates select="marc:datafield[@tag=656]"/>
5595                 <xsl:for-each select="marc:datafield[@tag=752 or @tag=662]">
5596                         <subject>
5597                                 <hierarchicalGeographic>
5598                                         <xsl:for-each select="marc:subfield[@code='a']">
5599                                                 <country>
5600                                                         <xsl:call-template name="chopPunctuation">
5601                                                                 <xsl:with-param name="chopString" select="."/>
5602                                                         </xsl:call-template>
5603                                                 </country>
5604                                         </xsl:for-each>
5605                                         <xsl:for-each select="marc:subfield[@code='b']">
5606                                                 <state>
5607                                                         <xsl:call-template name="chopPunctuation">
5608                                                                 <xsl:with-param name="chopString" select="."/>
5609                                                         </xsl:call-template>
5610                                                 </state>
5611                                         </xsl:for-each>
5612                                         <xsl:for-each select="marc:subfield[@code='c']">
5613                                                 <county>
5614                                                         <xsl:call-template name="chopPunctuation">
5615                                                                 <xsl:with-param name="chopString" select="."/>
5616                                                         </xsl:call-template>
5617                                                 </county>
5618                                         </xsl:for-each>
5619                                         <xsl:for-each select="marc:subfield[@code='d']">
5620                                                 <city>
5621                                                         <xsl:call-template name="chopPunctuation">
5622                                                                 <xsl:with-param name="chopString" select="."/>
5623                                                         </xsl:call-template>
5624                                                 </city>
5625                                         </xsl:for-each>
5626                                         <xsl:for-each select="marc:subfield[@code='e']">
5627                                                 <citySection>
5628                                                         <xsl:call-template name="chopPunctuation">
5629                                                                 <xsl:with-param name="chopString" select="."/>
5630                                                         </xsl:call-template>
5631                                                 </citySection>
5632                                         </xsl:for-each>
5633                                         <xsl:for-each select="marc:subfield[@code='g']">
5634                                                 <region>
5635                                                         <xsl:call-template name="chopPunctuation">
5636                                                                 <xsl:with-param name="chopString" select="."/>
5637                                                         </xsl:call-template>
5638                                                 </region>
5639                                         </xsl:for-each>
5640                                         <xsl:for-each select="marc:subfield[@code='h']">
5641                                                 <extraterrestrialArea>
5642                                                         <xsl:call-template name="chopPunctuation">
5643                                                                 <xsl:with-param name="chopString" select="."/>
5644                                                         </xsl:call-template>
5645                                                 </extraterrestrialArea>
5646                                         </xsl:for-each>
5647                                 </hierarchicalGeographic>
5648                         </subject>
5649                 </xsl:for-each>
5650                 <xsl:for-each select="marc:datafield[@tag=045][marc:subfield[@code='b']]">
5651                         <subject>
5652                                 <xsl:choose>
5653                                         <xsl:when test="@ind1=2">
5654                                                 <temporal encoding="iso8601" point="start">
5655                                                         <xsl:call-template name="chopPunctuation">
5656                                                                 <xsl:with-param name="chopString">
5657                                                                         <xsl:value-of select="marc:subfield[@code='b'][1]"/>
5658                                                                 </xsl:with-param>
5659                                                         </xsl:call-template>
5660                                                 </temporal>
5661                                                 <temporal encoding="iso8601" point="end">
5662                                                         <xsl:call-template name="chopPunctuation">
5663                                                                 <xsl:with-param name="chopString">
5664                                                                         <xsl:value-of select="marc:subfield[@code='b'][2]"/>
5665                                                                 </xsl:with-param>
5666                                                         </xsl:call-template>
5667                                                 </temporal>
5668                                         </xsl:when>
5669                                         <xsl:otherwise>
5670                                                 <xsl:for-each select="marc:subfield[@code='b']">
5671                                                         <temporal encoding="iso8601">
5672                                                                 <xsl:call-template name="chopPunctuation">
5673                                                                         <xsl:with-param name="chopString" select="."/>
5674                                                                 </xsl:call-template>
5675                                                         </temporal>
5676                                                 </xsl:for-each>
5677                                         </xsl:otherwise>
5678                                 </xsl:choose>
5679                         </subject>
5680                 </xsl:for-each>
5681                 <xsl:for-each select="marc:datafield[@tag=050]">
5682                         <xsl:for-each select="marc:subfield[@code='b']">
5683                                 <classification authority="lcc">
5684                                         <xsl:if test="../marc:subfield[@code='3']">
5685                                                 <xsl:attribute name="displayLabel">
5686                                                         <xsl:value-of select="../marc:subfield[@code='3']"/>
5687                                                 </xsl:attribute>
5688                                         </xsl:if>
5689                                         <xsl:value-of select="preceding-sibling::marc:subfield[@code='a'][1]"/>
5690                                         <xsl:text> </xsl:text>
5691                                         <xsl:value-of select="text()"/>
5692                                 </classification>
5693                         </xsl:for-each>
5694                         <xsl:for-each
5695                                 select="marc:subfield[@code='a'][not(following-sibling::marc:subfield[@code='b'])]">
5696                                 <classification authority="lcc">
5697                                         <xsl:if test="../marc:subfield[@code='3']">
5698                                                 <xsl:attribute name="displayLabel">
5699                                                         <xsl:value-of select="../marc:subfield[@code='3']"/>
5700                                                 </xsl:attribute>
5701                                         </xsl:if>
5702                                         <xsl:value-of select="text()"/>
5703                                 </classification>
5704                         </xsl:for-each>
5705                 </xsl:for-each>
5706                 <xsl:for-each select="marc:datafield[@tag=082]">
5707                         <classification authority="ddc">
5708                                 <xsl:if test="marc:subfield[@code='2']">
5709                                         <xsl:attribute name="edition">
5710                                                 <xsl:value-of select="marc:subfield[@code='2']"/>
5711                                         </xsl:attribute>
5712                                 </xsl:if>
5713                                 <xsl:call-template name="subfieldSelect">
5714                                         <xsl:with-param name="codes">ab</xsl:with-param>
5715                                 </xsl:call-template>
5716                         </classification>
5717                 </xsl:for-each>
5718                 <xsl:for-each select="marc:datafield[@tag=080]">
5719                         <classification authority="udc">
5720                                 <xsl:call-template name="subfieldSelect">
5721                                         <xsl:with-param name="codes">abx</xsl:with-param>
5722                                 </xsl:call-template>
5723                         </classification>
5724                 </xsl:for-each>
5725                 <xsl:for-each select="marc:datafield[@tag=060]">
5726                         <classification authority="nlm">
5727                                 <xsl:call-template name="subfieldSelect">
5728                                         <xsl:with-param name="codes">ab</xsl:with-param>
5729                                 </xsl:call-template>
5730                         </classification>
5731                 </xsl:for-each>
5732                 <xsl:for-each select="marc:datafield[@tag=086][@ind1=0]">
5733                         <classification authority="sudocs">
5734                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5735                         </classification>
5736                 </xsl:for-each>
5737                 <xsl:for-each select="marc:datafield[@tag=086][@ind1=1]">
5738                         <classification authority="candoc">
5739                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5740                         </classification>
5741                 </xsl:for-each>
5742                 <xsl:for-each select="marc:datafield[@tag=086]">
5743                         <classification>
5744                                 <xsl:attribute name="authority">
5745                                         <xsl:value-of select="marc:subfield[@code='2']"/>
5746                                 </xsl:attribute>
5747                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5748                         </classification>
5749                 </xsl:for-each>
5750                 <xsl:for-each select="marc:datafield[@tag=084]">
5751                         <classification>
5752                                 <xsl:attribute name="authority">
5753                                         <xsl:value-of select="marc:subfield[@code='2']"/>
5754                                 </xsl:attribute>
5755                                 <xsl:call-template name="subfieldSelect">
5756                                         <xsl:with-param name="codes">ab</xsl:with-param>
5757                                 </xsl:call-template>
5758                         </classification>
5759                 </xsl:for-each>
5760                 <xsl:for-each select="marc:datafield[@tag=440]">
5761                         <relatedItem type="series">
5762                                 <titleInfo>
5763                                         <title>
5764                                                 <xsl:call-template name="chopPunctuation">
5765                                                         <xsl:with-param name="chopString">
5766                                                                 <xsl:call-template name="subfieldSelect">
5767                                                                         <xsl:with-param name="codes">av</xsl:with-param>
5768                                                                 </xsl:call-template>
5769                                                         </xsl:with-param>
5770                                                 </xsl:call-template>
5771                                         </title>
5772                                         <xsl:call-template name="part"/>
5773                                 </titleInfo>
5774                         </relatedItem>
5775                 </xsl:for-each>
5776                 <xsl:for-each select="marc:datafield[@tag=490][@ind1=0]">
5777                         <relatedItem type="series">
5778                                 <titleInfo>
5779                                         <title>
5780                                                 <xsl:call-template name="chopPunctuation">
5781                                                         <xsl:with-param name="chopString">
5782                                                                 <xsl:call-template name="subfieldSelect">
5783                                                                         <xsl:with-param name="codes">av</xsl:with-param>
5784                                                                 </xsl:call-template>
5785                                                         </xsl:with-param>
5786                                                 </xsl:call-template>
5787                                         </title>
5788                                         <xsl:call-template name="part"/>
5789                                 </titleInfo>
5790                         </relatedItem>
5791                 </xsl:for-each>
5792                 <xsl:for-each select="marc:datafield[@tag=510]">
5793                         <relatedItem type="isReferencedBy">
5794                                 <note>
5795                                         <xsl:call-template name="subfieldSelect">
5796                                                 <xsl:with-param name="codes">abcx3</xsl:with-param>
5797                                         </xsl:call-template>
5798                                 </note>
5799                         </relatedItem>
5800                 </xsl:for-each>
5801                 <xsl:for-each select="marc:datafield[@tag=534]">
5802                         <relatedItem type="original">
5803                                 <xsl:call-template name="relatedTitle"/>
5804                                 <xsl:call-template name="relatedName"/>
5805                                 <xsl:if test="marc:subfield[@code='b' or @code='c']">
5806                                         <originInfo>
5807                                                 <xsl:for-each select="marc:subfield[@code='c']">
5808                                                         <publisher>
5809                                                                 <xsl:value-of select="."/>
5810                                                         </publisher>
5811                                                 </xsl:for-each>
5812                                                 <xsl:for-each select="marc:subfield[@code='b']">
5813                                                         <edition>
5814                                                                 <xsl:value-of select="."/>
5815                                                         </edition>
5816                                                 </xsl:for-each>
5817                                         </originInfo>
5818                                 </xsl:if>
5819                                 <xsl:call-template name="relatedIdentifierISSN"/>
5820                                 <xsl:for-each select="marc:subfield[@code='z']">
5821                                         <identifier type="isbn">
5822                                                 <xsl:value-of select="."/>
5823                                         </identifier>
5824                                 </xsl:for-each>
5825                                 <xsl:call-template name="relatedNote"/>
5826                         </relatedItem>
5827                 </xsl:for-each>
5828                 <xsl:for-each select="marc:datafield[@tag=700][marc:subfield[@code='t']]">
5829                         <relatedItem>
5830                                 <xsl:call-template name="constituentOrRelatedType"/>
5831                                 <titleInfo>
5832                                         <title>
5833                                                 <xsl:call-template name="chopPunctuation">
5834                                                         <xsl:with-param name="chopString">
5835                                                                 <xsl:call-template name="specialSubfieldSelect">
5836                                                                         <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
5837                                                                         <xsl:with-param name="axis">t</xsl:with-param>
5838                                                                         <xsl:with-param name="afterCodes">g</xsl:with-param>
5839                                                                 </xsl:call-template>
5840                                                         </xsl:with-param>
5841                                                 </xsl:call-template>
5842                                         </title>
5843                                         <xsl:call-template name="part"/>
5844                                 </titleInfo>
5845                                 <name type="personal">
5846                                         <namePart>
5847                                                 <xsl:call-template name="specialSubfieldSelect">
5848                                                         <xsl:with-param name="anyCodes">aq</xsl:with-param>
5849                                                         <xsl:with-param name="axis">t</xsl:with-param>
5850                                                         <xsl:with-param name="beforeCodes">g</xsl:with-param>
5851                                                 </xsl:call-template>
5852                                         </namePart>
5853                                         <xsl:call-template name="termsOfAddress"/>
5854                                         <xsl:call-template name="nameDate"/>
5855                                         <xsl:call-template name="role"/>
5856                                 </name>
5857                                 <xsl:call-template name="relatedForm"/>
5858                                 <xsl:call-template name="relatedIdentifierISSN"/>
5859                         </relatedItem>
5860                 </xsl:for-each>
5861                 <xsl:for-each select="marc:datafield[@tag=710][marc:subfield[@code='t']]">
5862                         <relatedItem>
5863                                 <xsl:call-template name="constituentOrRelatedType"/>
5864                                 <titleInfo>
5865                                         <title>
5866                                                 <xsl:call-template name="chopPunctuation">
5867                                                         <xsl:with-param name="chopString">
5868                                                                 <xsl:call-template name="specialSubfieldSelect">
5869                                                                         <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
5870                                                                         <xsl:with-param name="axis">t</xsl:with-param>
5871                                                                         <xsl:with-param name="afterCodes">dg</xsl:with-param>
5872                                                                 </xsl:call-template>
5873                                                         </xsl:with-param>
5874                                                 </xsl:call-template>
5875                                         </title>
5876                                         <xsl:call-template name="relatedPartNumName"/>
5877                                 </titleInfo>
5878                                 <name type="corporate">
5879                                         <xsl:for-each select="marc:subfield[@code='a']">
5880                                                 <namePart>
5881                                                         <xsl:value-of select="."/>
5882                                                 </namePart>
5883                                         </xsl:for-each>
5884                                         <xsl:for-each select="marc:subfield[@code='b']">
5885                                                 <namePart>
5886                                                         <xsl:value-of select="."/>
5887                                                 </namePart>
5888                                         </xsl:for-each>
5889                                         <xsl:variable name="tempNamePart">
5890                                                 <xsl:call-template name="specialSubfieldSelect">
5891                                                         <xsl:with-param name="anyCodes">c</xsl:with-param>
5892                                                         <xsl:with-param name="axis">t</xsl:with-param>
5893                                                         <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
5894                                                 </xsl:call-template>
5895                                         </xsl:variable>
5896                                         <xsl:if test="normalize-space($tempNamePart)">
5897                                                 <namePart>
5898                                                         <xsl:value-of select="$tempNamePart"/>
5899                                                 </namePart>
5900                                         </xsl:if>
5901                                         <xsl:call-template name="role"/>
5902                                 </name>
5903                                 <xsl:call-template name="relatedForm"/>
5904                                 <xsl:call-template name="relatedIdentifierISSN"/>
5905                         </relatedItem>
5906                 </xsl:for-each>
5907                 <xsl:for-each select="marc:datafield[@tag=711][marc:subfield[@code='t']]">
5908                         <relatedItem>
5909                                 <xsl:call-template name="constituentOrRelatedType"/>
5910                                 <titleInfo>
5911                                         <title>
5912                                                 <xsl:call-template name="chopPunctuation">
5913                                                         <xsl:with-param name="chopString">
5914                                                                 <xsl:call-template name="specialSubfieldSelect">
5915                                                                         <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
5916                                                                         <xsl:with-param name="axis">t</xsl:with-param>
5917                                                                         <xsl:with-param name="afterCodes">g</xsl:with-param>
5918                                                                 </xsl:call-template>
5919                                                         </xsl:with-param>
5920                                                 </xsl:call-template>
5921                                         </title>
5922                                         <xsl:call-template name="relatedPartNumName"/>
5923                                 </titleInfo>
5924                                 <name type="conference">
5925                                         <namePart>
5926                                                 <xsl:call-template name="specialSubfieldSelect">
5927                                                         <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
5928                                                         <xsl:with-param name="axis">t</xsl:with-param>
5929                                                         <xsl:with-param name="beforeCodes">gn</xsl:with-param>
5930                                                 </xsl:call-template>
5931                                         </namePart>
5932                                 </name>
5933                                 <xsl:call-template name="relatedForm"/>
5934                                 <xsl:call-template name="relatedIdentifierISSN"/>
5935                         </relatedItem>
5936                 </xsl:for-each>
5937                 <xsl:for-each select="marc:datafield[@tag=730][@ind2=2]">
5938                         <relatedItem>
5939                                 <xsl:call-template name="constituentOrRelatedType"/>
5940                                 <titleInfo>
5941                                         <title>
5942                                                 <xsl:call-template name="chopPunctuation">
5943                                                         <xsl:with-param name="chopString">
5944                                                                 <xsl:call-template name="subfieldSelect">
5945                                                                         <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
5946                                                                 </xsl:call-template>
5947                                                         </xsl:with-param>
5948                                                 </xsl:call-template>
5949                                         </title>
5950                                         <xsl:call-template name="part"/>
5951                                 </titleInfo>
5952                                 <xsl:call-template name="relatedForm"/>
5953                                 <xsl:call-template name="relatedIdentifierISSN"/>
5954                         </relatedItem>
5955                 </xsl:for-each>
5956                 <xsl:for-each select="marc:datafield[@tag=740][@ind2=2]">
5957                         <relatedItem>
5958                                 <xsl:call-template name="constituentOrRelatedType"/>
5959                                 <titleInfo>
5960                                         <title>
5961                                                 <xsl:call-template name="chopPunctuation">
5962                                                         <xsl:with-param name="chopString">
5963                                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
5964                                                         </xsl:with-param>
5965                                                 </xsl:call-template>
5966                                         </title>
5967                                         <xsl:call-template name="part"/>
5968                                 </titleInfo>
5969                                 <xsl:call-template name="relatedForm"/>
5970                         </relatedItem>
5971                 </xsl:for-each>
5972                 <xsl:for-each select="marc:datafield[@tag=760]|marc:datafield[@tag=762]">
5973                         <relatedItem type="series">
5974                                 <xsl:call-template name="relatedItem76X-78X"/>
5975                         </relatedItem>
5976                 </xsl:for-each>
5977                 <xsl:for-each
5978                         select="marc:datafield[@tag=765]|marc:datafield[@tag=767]|marc:datafield[@tag=777]|marc:datafield[@tag=787]">
5979                         <relatedItem>
5980                                 <xsl:call-template name="relatedItem76X-78X"/>
5981                         </relatedItem>
5982                 </xsl:for-each>
5983                 <xsl:for-each select="marc:datafield[@tag=775]">
5984                         <relatedItem type="otherVersion">
5985                                 <xsl:call-template name="relatedItem76X-78X"/>
5986                         </relatedItem>
5987                 </xsl:for-each>
5988                 <xsl:for-each select="marc:datafield[@tag=770]|marc:datafield[@tag=774]">
5989                         <relatedItem type="constituent">
5990                                 <xsl:call-template name="relatedItem76X-78X"/>
5991                         </relatedItem>
5992                 </xsl:for-each>
5993                 <xsl:for-each select="marc:datafield[@tag=772]|marc:datafield[@tag=773]">
5994                         <relatedItem type="host">
5995                                 <xsl:call-template name="relatedItem76X-78X"/>
5996                         </relatedItem>
5997                 </xsl:for-each>
5998                 <xsl:for-each select="marc:datafield[@tag=776]">
5999                         <relatedItem type="otherFormat">
6000                                 <xsl:call-template name="relatedItem76X-78X"/>
6001                         </relatedItem>
6002                 </xsl:for-each>
6003                 <xsl:for-each select="marc:datafield[@tag=780]">
6004                         <relatedItem type="preceding">
6005                                 <xsl:call-template name="relatedItem76X-78X"/>
6006                         </relatedItem>
6007                 </xsl:for-each>
6008                 <xsl:for-each select="marc:datafield[@tag=785]">
6009                         <relatedItem type="succeeding">
6010                                 <xsl:call-template name="relatedItem76X-78X"/>
6011                         </relatedItem>
6012                 </xsl:for-each>
6013                 <xsl:for-each select="marc:datafield[@tag=786]">
6014                         <relatedItem type="original">
6015                                 <xsl:call-template name="relatedItem76X-78X"/>
6016                         </relatedItem>
6017                 </xsl:for-each>
6018                 <xsl:for-each select="marc:datafield[@tag=800]">
6019                         <relatedItem type="series">
6020                                 <titleInfo>
6021                                         <title>
6022                                                 <xsl:call-template name="chopPunctuation">
6023                                                         <xsl:with-param name="chopString">
6024                                                                 <xsl:call-template name="specialSubfieldSelect">
6025                                                                         <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
6026                                                                         <xsl:with-param name="axis">t</xsl:with-param>
6027                                                                         <xsl:with-param name="afterCodes">g</xsl:with-param>
6028                                                                 </xsl:call-template>
6029                                                         </xsl:with-param>
6030                                                 </xsl:call-template>
6031                                         </title>
6032                                         <xsl:call-template name="part"/>
6033                                 </titleInfo>
6034                                 <name type="personal">
6035                                         <namePart>
6036                                                 <xsl:call-template name="chopPunctuation">
6037                                                         <xsl:with-param name="chopString">
6038                                                                 <xsl:call-template name="specialSubfieldSelect">
6039                                                                         <xsl:with-param name="anyCodes">aq</xsl:with-param>
6040                                                                         <xsl:with-param name="axis">t</xsl:with-param>
6041                                                                         <xsl:with-param name="beforeCodes">g</xsl:with-param>
6042                                                                 </xsl:call-template>
6043                                                         </xsl:with-param>
6044                                                 </xsl:call-template>
6045                                         </namePart>
6046                                         <xsl:call-template name="termsOfAddress"/>
6047                                         <xsl:call-template name="nameDate"/>
6048                                         <xsl:call-template name="role"/>
6049                                 </name>
6050                                 <xsl:call-template name="relatedForm"/>
6051                         </relatedItem>
6052                 </xsl:for-each>
6053                 <xsl:for-each select="marc:datafield[@tag=810]">
6054                         <relatedItem type="series">
6055                                 <titleInfo>
6056                                         <title>
6057                                                 <xsl:call-template name="chopPunctuation">
6058                                                         <xsl:with-param name="chopString">
6059                                                                 <xsl:call-template name="specialSubfieldSelect">
6060                                                                         <xsl:with-param name="anyCodes">tfklmorsv</xsl:with-param>
6061                                                                         <xsl:with-param name="axis">t</xsl:with-param>
6062                                                                         <xsl:with-param name="afterCodes">dg</xsl:with-param>
6063                                                                 </xsl:call-template>
6064                                                         </xsl:with-param>
6065                                                 </xsl:call-template>
6066                                         </title>
6067                                         <xsl:call-template name="relatedPartNumName"/>
6068                                 </titleInfo>
6069                                 <name type="corporate">
6070                                         <xsl:for-each select="marc:subfield[@code='a']">
6071                                                 <namePart>
6072                                                         <xsl:value-of select="."/>
6073                                                 </namePart>
6074                                         </xsl:for-each>
6075                                         <xsl:for-each select="marc:subfield[@code='b']">
6076                                                 <namePart>
6077                                                         <xsl:value-of select="."/>
6078                                                 </namePart>
6079                                         </xsl:for-each>
6080                                         <namePart>
6081                                                 <xsl:call-template name="specialSubfieldSelect">
6082                                                         <xsl:with-param name="anyCodes">c</xsl:with-param>
6083                                                         <xsl:with-param name="axis">t</xsl:with-param>
6084                                                         <xsl:with-param name="beforeCodes">dgn</xsl:with-param>
6085                                                 </xsl:call-template>
6086                                         </namePart>
6087                                         <xsl:call-template name="role"/>
6088                                 </name>
6089                                 <xsl:call-template name="relatedForm"/>
6090                         </relatedItem>
6091                 </xsl:for-each>
6092                 <xsl:for-each select="marc:datafield[@tag=811]">
6093                         <relatedItem type="series">
6094                                 <titleInfo>
6095                                         <title>
6096                                                 <xsl:call-template name="chopPunctuation">
6097                                                         <xsl:with-param name="chopString">
6098                                                                 <xsl:call-template name="specialSubfieldSelect">
6099                                                                         <xsl:with-param name="anyCodes">tfklsv</xsl:with-param>
6100                                                                         <xsl:with-param name="axis">t</xsl:with-param>
6101                                                                         <xsl:with-param name="afterCodes">g</xsl:with-param>
6102                                                                 </xsl:call-template>
6103                                                         </xsl:with-param>
6104                                                 </xsl:call-template>
6105                                         </title>
6106                                         <xsl:call-template name="relatedPartNumName"/>
6107                                 </titleInfo>
6108                                 <name type="conference">
6109                                         <namePart>
6110                                                 <xsl:call-template name="specialSubfieldSelect">
6111                                                         <xsl:with-param name="anyCodes">aqdc</xsl:with-param>
6112                                                         <xsl:with-param name="axis">t</xsl:with-param>
6113                                                         <xsl:with-param name="beforeCodes">gn</xsl:with-param>
6114                                                 </xsl:call-template>
6115                                         </namePart>
6116                                         <xsl:call-template name="role"/>
6117                                 </name>
6118                                 <xsl:call-template name="relatedForm"/>
6119                         </relatedItem>
6120                 </xsl:for-each>
6121                 <xsl:for-each select="marc:datafield[@tag='830']">
6122                         <relatedItem type="series">
6123                                 <titleInfo>
6124                                         <title>
6125                                                 <xsl:call-template name="chopPunctuation">
6126                                                         <xsl:with-param name="chopString">
6127                                                                 <xsl:call-template name="subfieldSelect">
6128                                                                         <xsl:with-param name="codes">adfgklmorsv</xsl:with-param>
6129                                                                 </xsl:call-template>
6130                                                         </xsl:with-param>
6131                                                 </xsl:call-template>
6132                                         </title>
6133                                         <xsl:call-template name="part"/>
6134                                 </titleInfo>
6135                                 <xsl:call-template name="relatedForm"/>
6136                         </relatedItem>
6137                 </xsl:for-each>
6138                 <xsl:for-each select="marc:datafield[@tag='856'][@ind2='2']/marc:subfield[@code='q']">
6139                         <relatedItem>
6140                                 <internetMediaType>
6141                                         <xsl:value-of select="."/>
6142                                 </internetMediaType>
6143                         </relatedItem>
6144                 </xsl:for-each>
6145                 <xsl:for-each select="marc:datafield[@tag='020']">
6146                         <xsl:call-template name="isInvalid">
6147                                 <xsl:with-param name="type">isbn</xsl:with-param>
6148                         </xsl:call-template>
6149                         <xsl:if test="marc:subfield[@code='a']">
6150                                 <identifier type="isbn">
6151                                         <xsl:value-of select="marc:subfield[@code='a']"/>
6152                                 </identifier>
6153                         </xsl:if>
6154                 </xsl:for-each>
6155                 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='0']">
6156                         <xsl:call-template name="isInvalid">
6157                                 <xsl:with-param name="type">isrc</xsl:with-param>
6158                         </xsl:call-template>
6159                         <xsl:if test="marc:subfield[@code='a']">
6160                                 <identifier type="isrc">
6161                                         <xsl:value-of select="marc:subfield[@code='a']"/>
6162                                 </identifier>
6163                         </xsl:if>
6164                 </xsl:for-each>
6165                 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='2']">
6166                         <xsl:call-template name="isInvalid">
6167                                 <xsl:with-param name="type">ismn</xsl:with-param>
6168                         </xsl:call-template>
6169                         <xsl:if test="marc:subfield[@code='a']">
6170                                 <identifier type="ismn">
6171                                         <xsl:value-of select="marc:subfield[@code='a']"/>
6172                                 </identifier>
6173                         </xsl:if>
6174                 </xsl:for-each>
6175                 <xsl:for-each select="marc:datafield[@tag='024'][@ind1='4']">
6176                         <xsl:call-template name="isInvalid">
6177                                 <xsl:with-param name="type">sici</xsl:with-param>
6178                         </xsl:call-template>
6179                         <identifier type="sici">
6180                                 <xsl:call-template name="subfieldSelect">
6181                                         <xsl:with-param name="codes">ab</xsl:with-param>
6182                                 </xsl:call-template>
6183                         </identifier>
6184                 </xsl:for-each>
6185                 <xsl:for-each select="marc:datafield[@tag='022']">
6186                         <xsl:if test="marc:subfield[@code='a']">
6187                                 <xsl:call-template name="isInvalid">
6188                                         <xsl:with-param name="type">issn</xsl:with-param>
6189                                 </xsl:call-template>
6190                                 <identifier type="issn">
6191                                         <xsl:value-of select="marc:subfield[@code='a']"/>
6192                                 </identifier>
6193                         </xsl:if>
6194                         <xsl:if test="marc:subfield[@code='l']">
6195                                 <xsl:call-template name="isInvalid">
6196                                         <xsl:with-param name="type">issn-l</xsl:with-param>
6197                                 </xsl:call-template>
6198                                 <identifier type="issn-l">
6199                                         <xsl:value-of select="marc:subfield[@code='l']"/>
6200                                 </identifier>
6201                         </xsl:if>
6202                 </xsl:for-each>
6203
6204
6205
6206                 <xsl:for-each select="marc:datafield[@tag='010']">
6207                         <xsl:call-template name="isInvalid">
6208                                 <xsl:with-param name="type">lccn</xsl:with-param>
6209                         </xsl:call-template>
6210                         <identifier type="lccn">
6211                                 <xsl:value-of select="normalize-space(marc:subfield[@code='a'])"/>
6212                         </identifier>
6213                 </xsl:for-each>
6214                 <xsl:for-each select="marc:datafield[@tag='028']">
6215                         <identifier>
6216                                 <xsl:attribute name="type">
6217                                         <xsl:choose>
6218                                                 <xsl:when test="@ind1='0'">issue number</xsl:when>
6219                                                 <xsl:when test="@ind1='1'">matrix number</xsl:when>
6220                                                 <xsl:when test="@ind1='2'">music plate</xsl:when>
6221                                                 <xsl:when test="@ind1='3'">music publisher</xsl:when>
6222                                                 <xsl:when test="@ind1='4'">videorecording identifier</xsl:when>
6223                                         </xsl:choose>
6224                                 </xsl:attribute>
6225                                 <!--<xsl:call-template name="isInvalid"/>-->
6226                                 <!-- no $z in 028 -->
6227                                 <xsl:call-template name="subfieldSelect">
6228                                         <xsl:with-param name="codes">
6229                                                 <xsl:choose>
6230                                                         <xsl:when test="@ind1='0'">ba</xsl:when>
6231                                                         <xsl:otherwise>ab</xsl:otherwise>
6232                                                 </xsl:choose>
6233                                         </xsl:with-param>
6234                                 </xsl:call-template>
6235                         </identifier>
6236                 </xsl:for-each>
6237                 <xsl:for-each select="marc:datafield[@tag='037']">
6238                         <identifier type="stock number">
6239                                 <!--<xsl:call-template name="isInvalid"/>-->
6240                                 <!-- no $z in 037 -->
6241                                 <xsl:call-template name="subfieldSelect">
6242                                         <xsl:with-param name="codes">ab</xsl:with-param>
6243                                 </xsl:call-template>
6244                         </identifier>
6245                 </xsl:for-each>
6246                 <xsl:for-each select="marc:datafield[@tag='856'][marc:subfield[@code='u']]">
6247                         <identifier>
6248                                 <xsl:attribute name="type">
6249                                         <xsl:choose>
6250                                                 <xsl:when
6251                                                         test="starts-with(marc:subfield[@code='u'],'urn:doi') or starts-with(marc:subfield[@code='u'],'doi')"
6252                                                         >doi</xsl:when>
6253                                                 <xsl:when
6254                                                         test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov')"
6255                                                         >hdl</xsl:when>
6256                                                 <xsl:otherwise>uri</xsl:otherwise>
6257                                         </xsl:choose>
6258                                 </xsl:attribute>
6259                                 <xsl:choose>
6260                                         <xsl:when
6261                                                 test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl') or starts-with(marc:subfield[@code='u'],'http://hdl.loc.gov') ">
6262                                                 <xsl:value-of
6263                                                         select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"
6264                                                 />
6265                                         </xsl:when>
6266                                         <xsl:otherwise>
6267                                                 <xsl:value-of select="marc:subfield[@code='u']"/>
6268                                         </xsl:otherwise>
6269                                 </xsl:choose>
6270                         </identifier>
6271                         <xsl:if
6272                                 test="starts-with(marc:subfield[@code='u'],'urn:hdl') or starts-with(marc:subfield[@code='u'],'hdl')">
6273                                 <identifier type="hdl">
6274                                         <xsl:if test="marc:subfield[@code='y' or @code='3' or @code='z']">
6275                                                 <xsl:attribute name="displayLabel">
6276                                                         <xsl:call-template name="subfieldSelect">
6277                                                                 <xsl:with-param name="codes">y3z</xsl:with-param>
6278                                                         </xsl:call-template>
6279                                                 </xsl:attribute>
6280                                         </xsl:if>
6281                                         <xsl:value-of
6282                                                 select="concat('hdl:',substring-after(marc:subfield[@code='u'],'http://hdl.loc.gov/'))"
6283                                         />
6284                                 </identifier>
6285                         </xsl:if>
6286                 </xsl:for-each>
6287                 <xsl:for-each select="marc:datafield[@tag=024][@ind1=1]">
6288                         <identifier type="upc">
6289                                 <xsl:call-template name="isInvalid"/>
6290                                 <xsl:value-of select="marc:subfield[@code='a']"/>
6291                         </identifier>
6292                 </xsl:for-each>
6293
6294                 <!-- 1/04 fix added $y -->
6295
6296                 <!-- 1.21  tmee-->
6297                 <xsl:for-each select="marc:datafield[@tag=856][@ind2=1][marc:subfield[@code='u']]">
6298                         <relatedItem type="otherVersion">
6299                                 <location>
6300                                         <url>
6301                                                 <xsl:if test="marc:subfield[@code='y' or @code='3']">
6302                                                         <xsl:attribute name="displayLabel">
6303                                                                 <xsl:call-template name="subfieldSelect">
6304                                                                         <xsl:with-param name="codes">y3</xsl:with-param>
6305                                                                 </xsl:call-template>
6306                                                         </xsl:attribute>
6307                                                 </xsl:if>
6308                                                 <xsl:if test="marc:subfield[@code='z' ]">
6309                                                         <xsl:attribute name="note">
6310                                                                 <xsl:call-template name="subfieldSelect">
6311                                                                         <xsl:with-param name="codes">z</xsl:with-param>
6312                                                                 </xsl:call-template>
6313                                                         </xsl:attribute>
6314                                                 </xsl:if>
6315                                                 <xsl:value-of select="marc:subfield[@code='u']"/>
6316                                         </url>
6317                                 </location>
6318                         </relatedItem>
6319                 </xsl:for-each>
6320                 <xsl:for-each select="marc:datafield[@tag=856][@ind2=2][marc:subfield[@code='u']]">
6321                         <relatedItem>
6322                                 <location>
6323                                         <url>
6324                                                 <xsl:if test="marc:subfield[@code='y' or @code='3']">
6325                                                         <xsl:attribute name="displayLabel">
6326                                                                 <xsl:call-template name="subfieldSelect">
6327                                                                         <xsl:with-param name="codes">y3</xsl:with-param>
6328                                                                 </xsl:call-template>
6329                                                         </xsl:attribute>
6330                                                 </xsl:if>
6331                                                 <xsl:if test="marc:subfield[@code='z' ]">
6332                                                         <xsl:attribute name="note">
6333                                                                 <xsl:call-template name="subfieldSelect">
6334                                                                         <xsl:with-param name="codes">z</xsl:with-param>
6335                                                                 </xsl:call-template>
6336                                                         </xsl:attribute>
6337                                                 </xsl:if>
6338                                                 <xsl:value-of select="marc:subfield[@code='u']"/>
6339                                         </url>
6340                                 </location>
6341                         </relatedItem>
6342                 </xsl:for-each>
6343
6344                 <!-- 3.2 change tmee 856z  -->
6345
6346                 <!-- 1.24  tmee  -->
6347                 <xsl:for-each select="marc:datafield[@tag=852]">
6348                         <location>
6349                                 <xsl:if test="marc:subfield[@code='a' or @code='b' or @code='e']">
6350                                         <physicalLocation>
6351                                                 <xsl:call-template name="subfieldSelect">
6352                                                         <xsl:with-param name="codes">abe</xsl:with-param>
6353                                                 </xsl:call-template>
6354                                         </physicalLocation>
6355                                 </xsl:if>
6356
6357                                 <xsl:if test="marc:subfield[@code='u']">
6358                                         <physicalLocation>
6359                                                 <xsl:call-template name="uri"/>
6360                                                 <xsl:call-template name="subfieldSelect">
6361                                                         <xsl:with-param name="codes">u</xsl:with-param>
6362                                                 </xsl:call-template>
6363                                         </physicalLocation>
6364                                 </xsl:if>
6365
6366                                 <xsl:if
6367                                         test="marc:subfield[@code='h' or @code='i' or @code='j' or @code='k' or @code='l' or @code='m' or @code='t']">
6368                                         <shelfLocation>
6369                                                 <xsl:call-template name="subfieldSelect">
6370                                                         <xsl:with-param name="codes">hijklmt</xsl:with-param>
6371                                                 </xsl:call-template>
6372                                         </shelfLocation>
6373                                 </xsl:if>
6374                         </location>
6375                 </xsl:for-each>
6376
6377                 <xsl:for-each select="marc:datafield[@tag=506]">
6378                         <accessCondition type="restrictionOnAccess">
6379                                 <xsl:call-template name="subfieldSelect">
6380                                         <xsl:with-param name="codes">abcd35</xsl:with-param>
6381                                 </xsl:call-template>
6382                         </accessCondition>
6383                 </xsl:for-each>
6384                 <xsl:for-each select="marc:datafield[@tag=540]">
6385                         <accessCondition type="useAndReproduction">
6386                                 <xsl:call-template name="subfieldSelect">
6387                                         <xsl:with-param name="codes">abcde35</xsl:with-param>
6388                                 </xsl:call-template>
6389                         </accessCondition>
6390                 </xsl:for-each>
6391
6392                 <recordInfo>
6393                         <!-- 1.25  tmee-->
6394
6395
6396                         <xsl:for-each select="marc:leader[substring($leader,19,1)='a']">
6397                                 <descriptionStandard>aacr2</descriptionStandard>
6398                         </xsl:for-each>
6399
6400                         <xsl:for-each select="marc:datafield[@tag=040]">
6401                                 <xsl:if test="marc:subfield[@code='e']">
6402                                         <descriptionStandard>
6403                                                 <xsl:value-of select="marc:subfield[@code='e']"/>
6404                                         </descriptionStandard>
6405                                 </xsl:if>
6406                                 <recordContentSource authority="marcorg">
6407                                         <xsl:value-of select="marc:subfield[@code='a']"/>
6408                                 </recordContentSource>
6409                         </xsl:for-each>
6410                         <xsl:for-each select="marc:controlfield[@tag=008]">
6411                                 <recordCreationDate encoding="marc">
6412                                         <xsl:value-of select="substring(.,1,6)"/>
6413                                 </recordCreationDate>
6414                         </xsl:for-each>
6415
6416                         <xsl:for-each select="marc:controlfield[@tag=005]">
6417                                 <recordChangeDate encoding="iso8601">
6418                                         <xsl:value-of select="."/>
6419                                 </recordChangeDate>
6420                         </xsl:for-each>
6421                         <xsl:for-each select="marc:controlfield[@tag=001]">
6422                                 <recordIdentifier>
6423                                         <xsl:if test="../marc:controlfield[@tag=003]">
6424                                                 <xsl:attribute name="source">
6425                                                         <xsl:value-of select="../marc:controlfield[@tag=003]"/>
6426                                                 </xsl:attribute>
6427                                         </xsl:if>
6428                                         <xsl:value-of select="."/>
6429                                 </recordIdentifier>
6430                         </xsl:for-each>
6431                         <xsl:for-each select="marc:datafield[@tag=040]/marc:subfield[@code='b']">
6432                                 <languageOfCataloging>
6433                                         <languageTerm authority="iso639-2b" type="code">
6434                                                 <xsl:value-of select="."/>
6435                                         </languageTerm>
6436                                 </languageOfCataloging>
6437                         </xsl:for-each>
6438                 </recordInfo>
6439         </xsl:template>
6440         <xsl:template name="displayForm">
6441                 <xsl:for-each select="marc:subfield[@code='c']">
6442                         <displayForm>
6443                                 <xsl:value-of select="."/>
6444                         </displayForm>
6445                 </xsl:for-each>
6446         </xsl:template>
6447         <xsl:template name="affiliation">
6448                 <xsl:for-each select="marc:subfield[@code='u']">
6449                         <affiliation>
6450                                 <xsl:value-of select="."/>
6451                         </affiliation>
6452                 </xsl:for-each>
6453         </xsl:template>
6454         <xsl:template name="uri">
6455                 <xsl:for-each select="marc:subfield[@code='u']|marc:subfield[@code='0']">
6456                         <xsl:attribute name="xlink:href">
6457                                 <xsl:value-of select="."/>
6458                         </xsl:attribute>
6459                 </xsl:for-each>
6460         </xsl:template>
6461         <xsl:template name="role">
6462                 <xsl:for-each select="marc:subfield[@code='e']">
6463                         <role>
6464                                 <roleTerm type="text">
6465                                         <xsl:value-of select="."/>
6466                                 </roleTerm>
6467                         </role>
6468                 </xsl:for-each>
6469                 <xsl:for-each select="marc:subfield[@code='4']">
6470                         <role>
6471                                 <roleTerm authority="marcrelator" type="code">
6472                                         <xsl:value-of select="."/>
6473                                 </roleTerm>
6474                         </role>
6475                 </xsl:for-each>
6476         </xsl:template>
6477         <xsl:template name="part">
6478                 <xsl:variable name="partNumber">
6479                         <xsl:call-template name="specialSubfieldSelect">
6480                                 <xsl:with-param name="axis">n</xsl:with-param>
6481                                 <xsl:with-param name="anyCodes">n</xsl:with-param>
6482                                 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
6483                         </xsl:call-template>
6484                 </xsl:variable>
6485                 <xsl:variable name="partName">
6486                         <xsl:call-template name="specialSubfieldSelect">
6487                                 <xsl:with-param name="axis">p</xsl:with-param>
6488                                 <xsl:with-param name="anyCodes">p</xsl:with-param>
6489                                 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
6490                         </xsl:call-template>
6491                 </xsl:variable>
6492                 <xsl:if test="string-length(normalize-space($partNumber))">
6493                         <partNumber>
6494                                 <xsl:call-template name="chopPunctuation">
6495                                         <xsl:with-param name="chopString" select="$partNumber"/>
6496                                 </xsl:call-template>
6497                         </partNumber>
6498                 </xsl:if>
6499                 <xsl:if test="string-length(normalize-space($partName))">
6500                         <partName>
6501                                 <xsl:call-template name="chopPunctuation">
6502                                         <xsl:with-param name="chopString" select="$partName"/>
6503                                 </xsl:call-template>
6504                         </partName>
6505                 </xsl:if>
6506         </xsl:template>
6507         <xsl:template name="relatedPart">
6508                 <xsl:if test="@tag=773">
6509                         <xsl:for-each select="marc:subfield[@code='g']">
6510                                 <part>
6511                                         <text>
6512                                                 <xsl:value-of select="."/>
6513                                         </text>
6514                                 </part>
6515                         </xsl:for-each>
6516                         <xsl:for-each select="marc:subfield[@code='q']">
6517                                 <part>
6518                                         <xsl:call-template name="parsePart"/>
6519                                 </part>
6520                         </xsl:for-each>
6521                 </xsl:if>
6522         </xsl:template>
6523         <xsl:template name="relatedPartNumName">
6524                 <xsl:variable name="partNumber">
6525                         <xsl:call-template name="specialSubfieldSelect">
6526                                 <xsl:with-param name="axis">g</xsl:with-param>
6527                                 <xsl:with-param name="anyCodes">g</xsl:with-param>
6528                                 <xsl:with-param name="afterCodes">pst</xsl:with-param>
6529                         </xsl:call-template>
6530                 </xsl:variable>
6531                 <xsl:variable name="partName">
6532                         <xsl:call-template name="specialSubfieldSelect">
6533                                 <xsl:with-param name="axis">p</xsl:with-param>
6534                                 <xsl:with-param name="anyCodes">p</xsl:with-param>
6535                                 <xsl:with-param name="afterCodes">fgkdlmor</xsl:with-param>
6536                         </xsl:call-template>
6537                 </xsl:variable>
6538                 <xsl:if test="string-length(normalize-space($partNumber))">
6539                         <partNumber>
6540                                 <xsl:value-of select="$partNumber"/>
6541                         </partNumber>
6542                 </xsl:if>
6543                 <xsl:if test="string-length(normalize-space($partName))">
6544                         <partName>
6545                                 <xsl:value-of select="$partName"/>
6546                         </partName>
6547                 </xsl:if>
6548         </xsl:template>
6549         <xsl:template name="relatedName">
6550                 <xsl:for-each select="marc:subfield[@code='a']">
6551                         <name>
6552                                 <namePart>
6553                                         <xsl:value-of select="."/>
6554                                 </namePart>
6555                         </name>
6556                 </xsl:for-each>
6557         </xsl:template>
6558         <xsl:template name="relatedForm">
6559                 <xsl:for-each select="marc:subfield[@code='h']">
6560                         <physicalDescription>
6561                                 <form>
6562                                         <xsl:value-of select="."/>
6563                                 </form>
6564                         </physicalDescription>
6565                 </xsl:for-each>
6566         </xsl:template>
6567         <xsl:template name="relatedExtent">
6568                 <xsl:for-each select="marc:subfield[@code='h']">
6569                         <physicalDescription>
6570                                 <extent>
6571                                         <xsl:value-of select="."/>
6572                                 </extent>
6573                         </physicalDescription>
6574                 </xsl:for-each>
6575         </xsl:template>
6576         <xsl:template name="relatedNote">
6577                 <xsl:for-each select="marc:subfield[@code='n']">
6578                         <note>
6579                                 <xsl:value-of select="."/>
6580                         </note>
6581                 </xsl:for-each>
6582         </xsl:template>
6583         <xsl:template name="relatedSubject">
6584                 <xsl:for-each select="marc:subfield[@code='j']">
6585                         <subject>
6586                                 <temporal encoding="iso8601">
6587                                         <xsl:call-template name="chopPunctuation">
6588                                                 <xsl:with-param name="chopString" select="."/>
6589                                         </xsl:call-template>
6590                                 </temporal>
6591                         </subject>
6592                 </xsl:for-each>
6593         </xsl:template>
6594         <xsl:template name="relatedIdentifierISSN">
6595                 <xsl:for-each select="marc:subfield[@code='x']">
6596                         <identifier type="issn">
6597                                 <xsl:value-of select="."/>
6598                         </identifier>
6599                 </xsl:for-each>
6600         </xsl:template>
6601         <xsl:template name="relatedIdentifierLocal">
6602                 <xsl:for-each select="marc:subfield[@code='w']">
6603                         <identifier type="local">
6604                                 <xsl:value-of select="."/>
6605                         </identifier>
6606                 </xsl:for-each>
6607         </xsl:template>
6608         <xsl:template name="relatedIdentifier">
6609                 <xsl:for-each select="marc:subfield[@code='o']">
6610                         <identifier>
6611                                 <xsl:value-of select="."/>
6612                         </identifier>
6613                 </xsl:for-each>
6614         </xsl:template>
6615         <xsl:template name="relatedItem76X-78X">
6616                 <xsl:call-template name="displayLabel"/>
6617                 <xsl:call-template name="relatedTitle76X-78X"/>
6618                 <xsl:call-template name="relatedName"/>
6619                 <xsl:call-template name="relatedOriginInfo"/>
6620                 <xsl:call-template name="relatedLanguage"/>
6621                 <xsl:call-template name="relatedExtent"/>
6622                 <xsl:call-template name="relatedNote"/>
6623                 <xsl:call-template name="relatedSubject"/>
6624                 <xsl:call-template name="relatedIdentifier"/>
6625                 <xsl:call-template name="relatedIdentifierISSN"/>
6626                 <xsl:call-template name="relatedIdentifierLocal"/>
6627                 <xsl:call-template name="relatedPart"/>
6628         </xsl:template>
6629         <xsl:template name="subjectGeographicZ">
6630                 <geographic>
6631                         <xsl:call-template name="chopPunctuation">
6632                                 <xsl:with-param name="chopString" select="."/>
6633                         </xsl:call-template>
6634                 </geographic>
6635         </xsl:template>
6636         <xsl:template name="subjectTemporalY">
6637                 <temporal>
6638                         <xsl:call-template name="chopPunctuation">
6639                                 <xsl:with-param name="chopString" select="."/>
6640                         </xsl:call-template>
6641                 </temporal>
6642         </xsl:template>
6643         <xsl:template name="subjectTopic">
6644                 <topic>
6645                         <xsl:call-template name="chopPunctuation">
6646                                 <xsl:with-param name="chopString" select="."/>
6647                         </xsl:call-template>
6648                 </topic>
6649         </xsl:template>
6650         <!-- 3.2 change tmee 6xx $v genre -->
6651         <xsl:template name="subjectGenre">
6652                 <genre>
6653                         <xsl:call-template name="chopPunctuation">
6654                                 <xsl:with-param name="chopString" select="."/>
6655                         </xsl:call-template>
6656                 </genre>
6657         </xsl:template>
6658
6659         <xsl:template name="nameABCDN">
6660                 <xsl:for-each select="marc:subfield[@code='a']">
6661                         <namePart>
6662                                 <xsl:call-template name="chopPunctuation">
6663                                         <xsl:with-param name="chopString" select="."/>
6664                                 </xsl:call-template>
6665                         </namePart>
6666                 </xsl:for-each>
6667                 <xsl:for-each select="marc:subfield[@code='b']">
6668                         <namePart>
6669                                 <xsl:value-of select="."/>
6670                         </namePart>
6671                 </xsl:for-each>
6672                 <xsl:if
6673                         test="marc:subfield[@code='c'] or marc:subfield[@code='d'] or marc:subfield[@code='n']">
6674                         <namePart>
6675                                 <xsl:call-template name="subfieldSelect">
6676                                         <xsl:with-param name="codes">cdn</xsl:with-param>
6677                                 </xsl:call-template>
6678                         </namePart>
6679                 </xsl:if>
6680         </xsl:template>
6681         <xsl:template name="nameABCDQ">
6682                 <namePart>
6683                         <xsl:call-template name="chopPunctuation">
6684                                 <xsl:with-param name="chopString">
6685                                         <xsl:call-template name="subfieldSelect">
6686                                                 <xsl:with-param name="codes">aq</xsl:with-param>
6687                                         </xsl:call-template>
6688                                 </xsl:with-param>
6689                                 <xsl:with-param name="punctuation">
6690                                         <xsl:text>:,;/ </xsl:text>
6691                                 </xsl:with-param>
6692                         </xsl:call-template>
6693                 </namePart>
6694                 <xsl:call-template name="termsOfAddress"/>
6695                 <xsl:call-template name="nameDate"/>
6696         </xsl:template>
6697         <xsl:template name="nameACDEQ">
6698                 <namePart>
6699                         <xsl:call-template name="subfieldSelect">
6700                                 <xsl:with-param name="codes">acdeq</xsl:with-param>
6701                         </xsl:call-template>
6702                 </namePart>
6703         </xsl:template>
6704         <xsl:template name="constituentOrRelatedType">
6705                 <xsl:if test="@ind2=2">
6706                         <xsl:attribute name="type">constituent</xsl:attribute>
6707                 </xsl:if>
6708         </xsl:template>
6709         <xsl:template name="relatedTitle">
6710                 <xsl:for-each select="marc:subfield[@code='t']">
6711                         <titleInfo>
6712                                 <title>
6713                                         <xsl:call-template name="chopPunctuation">
6714                                                 <xsl:with-param name="chopString">
6715                                                         <xsl:value-of select="."/>
6716                                                 </xsl:with-param>
6717                                         </xsl:call-template>
6718                                 </title>
6719                         </titleInfo>
6720                 </xsl:for-each>
6721         </xsl:template>
6722         <xsl:template name="relatedTitle76X-78X">
6723                 <xsl:for-each select="marc:subfield[@code='t']">
6724                         <titleInfo>
6725                                 <title>
6726                                         <xsl:call-template name="chopPunctuation">
6727                                                 <xsl:with-param name="chopString">
6728                                                         <xsl:value-of select="."/>
6729                                                 </xsl:with-param>
6730                                         </xsl:call-template>
6731                                 </title>
6732                                 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
6733                                         <xsl:call-template name="relatedPartNumName"/>
6734                                 </xsl:if>
6735                         </titleInfo>
6736                 </xsl:for-each>
6737                 <xsl:for-each select="marc:subfield[@code='p']">
6738                         <titleInfo type="abbreviated">
6739                                 <title>
6740                                         <xsl:call-template name="chopPunctuation">
6741                                                 <xsl:with-param name="chopString">
6742                                                         <xsl:value-of select="."/>
6743                                                 </xsl:with-param>
6744                                         </xsl:call-template>
6745                                 </title>
6746                                 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
6747                                         <xsl:call-template name="relatedPartNumName"/>
6748                                 </xsl:if>
6749                         </titleInfo>
6750                 </xsl:for-each>
6751                 <xsl:for-each select="marc:subfield[@code='s']">
6752                         <titleInfo type="uniform">
6753                                 <title>
6754                                         <xsl:call-template name="chopPunctuation">
6755                                                 <xsl:with-param name="chopString">
6756                                                         <xsl:value-of select="."/>
6757                                                 </xsl:with-param>
6758                                         </xsl:call-template>
6759                                 </title>
6760                                 <xsl:if test="marc:datafield[@tag!=773]and marc:subfield[@code='g']">
6761                                         <xsl:call-template name="relatedPartNumName"/>
6762                                 </xsl:if>
6763                         </titleInfo>
6764                 </xsl:for-each>
6765         </xsl:template>
6766         <xsl:template name="relatedOriginInfo">
6767                 <xsl:if test="marc:subfield[@code='b' or @code='d'] or marc:subfield[@code='f']">
6768                         <originInfo>
6769                                 <xsl:if test="@tag=775">
6770                                         <xsl:for-each select="marc:subfield[@code='f']">
6771                                                 <place>
6772                                                         <placeTerm>
6773                                                                 <xsl:attribute name="type">code</xsl:attribute>
6774                                                                 <xsl:attribute name="authority">marcgac</xsl:attribute>
6775                                                                 <xsl:value-of select="."/>
6776                                                         </placeTerm>
6777                                                 </place>
6778                                         </xsl:for-each>
6779                                 </xsl:if>
6780                                 <xsl:for-each select="marc:subfield[@code='d']">
6781                                         <publisher>
6782                                                 <xsl:value-of select="."/>
6783                                         </publisher>
6784                                 </xsl:for-each>
6785                                 <xsl:for-each select="marc:subfield[@code='b']">
6786                                         <edition>
6787                                                 <xsl:value-of select="."/>
6788                                         </edition>
6789                                 </xsl:for-each>
6790                         </originInfo>
6791                 </xsl:if>
6792         </xsl:template>
6793         <xsl:template name="relatedLanguage">
6794                 <xsl:for-each select="marc:subfield[@code='e']">
6795                         <xsl:call-template name="getLanguage">
6796                                 <xsl:with-param name="langString">
6797                                         <xsl:value-of select="."/>
6798                                 </xsl:with-param>
6799                         </xsl:call-template>
6800                 </xsl:for-each>
6801         </xsl:template>
6802         <xsl:template name="nameDate">
6803                 <xsl:for-each select="marc:subfield[@code='d']">
6804                         <namePart type="date">
6805                                 <xsl:call-template name="chopPunctuation">
6806                                         <xsl:with-param name="chopString" select="."/>
6807                                 </xsl:call-template>
6808                         </namePart>
6809                 </xsl:for-each>
6810         </xsl:template>
6811         <xsl:template name="subjectAuthority">
6812                 <xsl:if test="@ind2!=4">
6813                         <xsl:if test="@ind2!=' '">
6814                                 <xsl:if test="@ind2!=8">
6815                                         <xsl:if test="@ind2!=9">
6816                                                 <xsl:attribute name="authority">
6817                                                         <xsl:choose>
6818                                                                 <xsl:when test="@ind2=0">lcsh</xsl:when>
6819                                                                 <xsl:when test="@ind2=1">lcshac</xsl:when>
6820                                                                 <xsl:when test="@ind2=2">mesh</xsl:when>
6821                                                                 <!-- 1/04 fix -->
6822                                                                 <xsl:when test="@ind2=3">nal</xsl:when>
6823                                                                 <xsl:when test="@ind2=5">csh</xsl:when>
6824                                                                 <xsl:when test="@ind2=6">rvm</xsl:when>
6825                                                                 <xsl:when test="@ind2=7">
6826                                                                         <xsl:value-of select="marc:subfield[@code='2']"/>
6827                                                                 </xsl:when>
6828                                                         </xsl:choose>
6829                                                 </xsl:attribute>
6830                                         </xsl:if>
6831                                 </xsl:if>
6832                         </xsl:if>
6833                 </xsl:if>
6834         </xsl:template>
6835         <xsl:template name="subjectAnyOrder">
6836                 <xsl:for-each select="marc:subfield[@code='v' or @code='x' or @code='y' or @code='z']">
6837                         <xsl:choose>
6838                                 <xsl:when test="@code='v'">
6839                                         <xsl:call-template name="subjectGenre"/>
6840                                 </xsl:when>
6841                                 <xsl:when test="@code='x'">
6842                                         <xsl:call-template name="subjectTopic"/>
6843                                 </xsl:when>
6844                                 <xsl:when test="@code='y'">
6845                                         <xsl:call-template name="subjectTemporalY"/>
6846                                 </xsl:when>
6847                                 <xsl:when test="@code='z'">
6848                                         <xsl:call-template name="subjectGeographicZ"/>
6849                                 </xsl:when>
6850                         </xsl:choose>
6851                 </xsl:for-each>
6852         </xsl:template>
6853         <xsl:template name="specialSubfieldSelect">
6854                 <xsl:param name="anyCodes"/>
6855                 <xsl:param name="axis"/>
6856                 <xsl:param name="beforeCodes"/>
6857                 <xsl:param name="afterCodes"/>
6858                 <xsl:variable name="str">
6859                         <xsl:for-each select="marc:subfield">
6860                                 <xsl:if
6861                                         test="contains($anyCodes, @code)      or (contains($beforeCodes,@code) and following-sibling::marc:subfield[@code=$axis])      or (contains($afterCodes,@code) and preceding-sibling::marc:subfield[@code=$axis])">
6862                                         <xsl:value-of select="text()"/>
6863                                         <xsl:text> </xsl:text>
6864                                 </xsl:if>
6865                         </xsl:for-each>
6866                 </xsl:variable>
6867                 <xsl:value-of select="substring($str,1,string-length($str)-1)"/>
6868         </xsl:template>
6869
6870         <!-- 3.2 change tmee 6xx $v genre -->
6871         <xsl:template match="marc:datafield[@tag=600]">
6872                 <subject>
6873                         <xsl:call-template name="subjectAuthority"/>
6874                         <name type="personal">
6875                                 <xsl:call-template name="termsOfAddress"/>
6876                                 <namePart>
6877                                         <xsl:call-template name="chopPunctuation">
6878                                                 <xsl:with-param name="chopString">
6879                                                         <xsl:call-template name="subfieldSelect">
6880                                                                 <xsl:with-param name="codes">aq</xsl:with-param>
6881                                                         </xsl:call-template>
6882                                                 </xsl:with-param>
6883                                         </xsl:call-template>
6884                                 </namePart>
6885                                 <xsl:call-template name="nameDate"/>
6886                                 <xsl:call-template name="affiliation"/>
6887                                 <xsl:call-template name="role"/>
6888                         </name>
6889                         <xsl:call-template name="subjectAnyOrder"/>
6890                 </subject>
6891         </xsl:template>
6892         <xsl:template match="marc:datafield[@tag=610]">
6893                 <subject>
6894                         <xsl:call-template name="subjectAuthority"/>
6895                         <name type="corporate">
6896                                 <xsl:for-each select="marc:subfield[@code='a']">
6897                                         <namePart>
6898                                                 <xsl:value-of select="."/>
6899                                         </namePart>
6900                                 </xsl:for-each>
6901                                 <xsl:for-each select="marc:subfield[@code='b']">
6902                                         <namePart>
6903                                                 <xsl:value-of select="."/>
6904                                         </namePart>
6905                                 </xsl:for-each>
6906                                 <xsl:if test="marc:subfield[@code='c' or @code='d' or @code='n' or @code='p']">
6907                                         <namePart>
6908                                                 <xsl:call-template name="subfieldSelect">
6909                                                         <xsl:with-param name="codes">cdnp</xsl:with-param>
6910                                                 </xsl:call-template>
6911                                         </namePart>
6912                                 </xsl:if>
6913                                 <xsl:call-template name="role"/>
6914                         </name>
6915                         <xsl:call-template name="subjectAnyOrder"/>
6916                 </subject>
6917         </xsl:template>
6918         <xsl:template match="marc:datafield[@tag=611]">
6919                 <subject>
6920                         <xsl:call-template name="subjectAuthority"/>
6921                         <name type="conference">
6922                                 <namePart>
6923                                         <xsl:call-template name="subfieldSelect">
6924                                                 <xsl:with-param name="codes">abcdeqnp</xsl:with-param>
6925                                         </xsl:call-template>
6926                                 </namePart>
6927                                 <xsl:for-each select="marc:subfield[@code='4']">
6928                                         <role>
6929                                                 <roleTerm authority="marcrelator" type="code">
6930                                                         <xsl:value-of select="."/>
6931                                                 </roleTerm>
6932                                         </role>
6933                                 </xsl:for-each>
6934                         </name>
6935                         <xsl:call-template name="subjectAnyOrder"/>
6936                 </subject>
6937         </xsl:template>
6938         <xsl:template match="marc:datafield[@tag=630]">
6939                 <subject>
6940                         <xsl:call-template name="subjectAuthority"/>
6941                         <titleInfo>
6942                                 <title>
6943                                         <xsl:call-template name="chopPunctuation">
6944                                                 <xsl:with-param name="chopString">
6945                                                         <xsl:call-template name="subfieldSelect">
6946                                                                 <xsl:with-param name="codes">adfhklor</xsl:with-param>
6947                                                         </xsl:call-template>
6948                                                 </xsl:with-param>
6949                                         </xsl:call-template>
6950                                 </title>
6951                                 <xsl:call-template name="part"/>
6952                         </titleInfo>
6953                         <xsl:call-template name="subjectAnyOrder"/>
6954                 </subject>
6955         </xsl:template>
6956         <!-- 1.27 648 tmee-->
6957         <xsl:template match="marc:datafield[@tag=648]">
6958                 <subject>
6959                         <xsl:if test="marc:subfield[@code=2]">
6960                                 <xsl:attribute name="authority">
6961                                         <xsl:value-of select="marc:subfield[@code=2]"/>
6962                                 </xsl:attribute>
6963                         </xsl:if>
6964                         <xsl:call-template name="uri"/>
6965
6966                         <xsl:call-template name="subjectAuthority"/>
6967                         <temporal>
6968                                 <xsl:call-template name="chopPunctuation">
6969                                         <xsl:with-param name="chopString">
6970                                                 <xsl:call-template name="subfieldSelect">
6971                                                         <xsl:with-param name="codes">abcd</xsl:with-param>
6972                                                 </xsl:call-template>
6973                                         </xsl:with-param>
6974                                 </xsl:call-template>
6975                         </temporal>
6976                         <xsl:call-template name="subjectAnyOrder"/>
6977
6978                 </subject>
6979         </xsl:template>
6980         <xsl:template match="marc:datafield[@tag=650]">
6981                 <subject>
6982                         <xsl:call-template name="subjectAuthority"/>
6983                         <topic>
6984                                 <xsl:call-template name="chopPunctuation">
6985                                         <xsl:with-param name="chopString">
6986                                                 <xsl:call-template name="subfieldSelect">
6987                                                         <xsl:with-param name="codes">abcd</xsl:with-param>
6988                                                 </xsl:call-template>
6989                                         </xsl:with-param>
6990                                 </xsl:call-template>
6991                         </topic>
6992                         <xsl:call-template name="subjectAnyOrder"/>
6993                 </subject>
6994         </xsl:template>
6995         <xsl:template match="marc:datafield[@tag=651]">
6996                 <subject>
6997                         <xsl:call-template name="subjectAuthority"/>
6998                         <xsl:for-each select="marc:subfield[@code='a']">
6999                                 <geographic>
7000                                         <xsl:call-template name="chopPunctuation">
7001                                                 <xsl:with-param name="chopString" select="."/>
7002                                         </xsl:call-template>
7003                                 </geographic>
7004                         </xsl:for-each>
7005                         <xsl:call-template name="subjectAnyOrder"/>
7006                 </subject>
7007         </xsl:template>
7008         <xsl:template match="marc:datafield[@tag=653]">
7009                 <subject>
7010                         <xsl:for-each select="marc:subfield[@code='a']">
7011                                 <topic>
7012                                         <xsl:value-of select="."/>
7013                                 </topic>
7014                         </xsl:for-each>
7015                 </subject>
7016         </xsl:template>
7017         <xsl:template match="marc:datafield[@tag=656]">
7018                 <subject>
7019                         <xsl:if test="marc:subfield[@code=2]">
7020                                 <xsl:attribute name="authority">
7021                                         <xsl:value-of select="marc:subfield[@code=2]"/>
7022                                 </xsl:attribute>
7023                         </xsl:if>
7024                         <occupation>
7025                                 <xsl:call-template name="chopPunctuation">
7026                                         <xsl:with-param name="chopString">
7027                                                 <xsl:value-of select="marc:subfield[@code='a']"/>
7028                                         </xsl:with-param>
7029                                 </xsl:call-template>
7030                         </occupation>
7031                 </subject>
7032         </xsl:template>
7033         <xsl:template name="termsOfAddress">
7034                 <xsl:if test="marc:subfield[@code='b' or @code='c']">
7035                         <namePart type="termsOfAddress">
7036                                 <xsl:call-template name="chopPunctuation">
7037                                         <xsl:with-param name="chopString">
7038                                                 <xsl:call-template name="subfieldSelect">
7039                                                         <xsl:with-param name="codes">bc</xsl:with-param>
7040                                                 </xsl:call-template>
7041                                         </xsl:with-param>
7042                                 </xsl:call-template>
7043                         </namePart>
7044                 </xsl:if>
7045         </xsl:template>
7046         <xsl:template name="displayLabel">
7047                 <xsl:if test="marc:subfield[@code='i']">
7048                         <xsl:attribute name="displayLabel">
7049                                 <xsl:value-of select="marc:subfield[@code='i']"/>
7050                         </xsl:attribute>
7051                 </xsl:if>
7052                 <xsl:if test="marc:subfield[@code='3']">
7053                         <xsl:attribute name="displayLabel">
7054                                 <xsl:value-of select="marc:subfield[@code='3']"/>
7055                         </xsl:attribute>
7056                 </xsl:if>
7057         </xsl:template>
7058         <xsl:template name="isInvalid">
7059                 <xsl:param name="type"/>
7060                 <xsl:if
7061                         test="marc:subfield[@code='z'] or marc:subfield[@code='y']  or marc:subfield[@code='m']">
7062                         <identifier>
7063                                 <xsl:attribute name="type">
7064                                         <xsl:value-of select="$type"/>
7065                                 </xsl:attribute>
7066                                 <xsl:attribute name="invalid">
7067                                         <xsl:text>yes</xsl:text>
7068                                 </xsl:attribute>
7069                                 <xsl:if test="marc:subfield[@code='z']">
7070                                         <xsl:value-of select="marc:subfield[@code='z']"/>
7071                                 </xsl:if>
7072                                 <xsl:if test="marc:subfield[@code='y']">
7073                                         <xsl:value-of select="marc:subfield[@code='y']"/>
7074                                 </xsl:if>
7075                                 <xsl:if test="marc:subfield[@code='m']">
7076                                         <xsl:value-of select="marc:subfield[@code='m']"/>
7077                                 </xsl:if>
7078                         </identifier>
7079                 </xsl:if>
7080         </xsl:template>
7081         <xsl:template name="subtitle">
7082                 <xsl:if test="marc:subfield[@code='b']">
7083                         <subTitle>
7084                                 <xsl:call-template name="chopPunctuation">
7085                                         <xsl:with-param name="chopString">
7086                                                 <xsl:value-of select="marc:subfield[@code='b']"/>
7087                                                 <!--<xsl:call-template name="subfieldSelect">
7088                                                         <xsl:with-param name="codes">b</xsl:with-param>                                                                 
7089                                                 </xsl:call-template>-->
7090                                         </xsl:with-param>
7091                                 </xsl:call-template>
7092                         </subTitle>
7093                 </xsl:if>
7094         </xsl:template>
7095         <xsl:template name="script">
7096                 <xsl:param name="scriptCode"/>
7097                 <xsl:attribute name="script">
7098                         <xsl:choose>
7099                                 <xsl:when test="$scriptCode='(3'">Arabic</xsl:when>
7100                                 <xsl:when test="$scriptCode='(B'">Latin</xsl:when>
7101                                 <xsl:when test="$scriptCode='$1'">Chinese, Japanese, Korean</xsl:when>
7102                                 <xsl:when test="$scriptCode='(N'">Cyrillic</xsl:when>
7103                                 <xsl:when test="$scriptCode='(2'">Hebrew</xsl:when>
7104                                 <xsl:when test="$scriptCode='(S'">Greek</xsl:when>
7105                         </xsl:choose>
7106                 </xsl:attribute>
7107         </xsl:template>
7108         <xsl:template name="parsePart">
7109                 <!-- assumes 773$q= 1:2:3<4
7110                      with up to 3 levels and one optional start page
7111                 -->
7112                 <xsl:variable name="level1">
7113                         <xsl:choose>
7114                                 <xsl:when test="contains(text(),':')">
7115                                         <!-- 1:2 -->
7116                                         <xsl:value-of select="substring-before(text(),':')"/>
7117                                 </xsl:when>
7118                                 <xsl:when test="not(contains(text(),':'))">
7119                                         <!-- 1 or 1<3 -->
7120                                         <xsl:if test="contains(text(),'&lt;')">
7121                                                 <!-- 1<3 -->
7122                                                 <xsl:value-of select="substring-before(text(),'&lt;')"/>
7123                                         </xsl:if>
7124                                         <xsl:if test="not(contains(text(),'&lt;'))">
7125                                                 <!-- 1 -->
7126                                                 <xsl:value-of select="text()"/>
7127                                         </xsl:if>
7128                                 </xsl:when>
7129                         </xsl:choose>
7130                 </xsl:variable>
7131                 <xsl:variable name="sici2">
7132                         <xsl:choose>
7133                                 <xsl:when test="starts-with(substring-after(text(),$level1),':')">
7134                                         <xsl:value-of select="substring(substring-after(text(),$level1),2)"/>
7135                                 </xsl:when>
7136                                 <xsl:otherwise>
7137                                         <xsl:value-of select="substring-after(text(),$level1)"/>
7138                                 </xsl:otherwise>
7139                         </xsl:choose>
7140                 </xsl:variable>
7141                 <xsl:variable name="level2">
7142                         <xsl:choose>
7143                                 <xsl:when test="contains($sici2,':')">
7144                                         <!--  2:3<4  -->
7145                                         <xsl:value-of select="substring-before($sici2,':')"/>
7146                                 </xsl:when>
7147                                 <xsl:when test="contains($sici2,'&lt;')">
7148                                         <!-- 1: 2<4 -->
7149                                         <xsl:value-of select="substring-before($sici2,'&lt;')"/>
7150                                 </xsl:when>
7151                                 <xsl:otherwise>
7152                                         <xsl:value-of select="$sici2"/>
7153                                         <!-- 1:2 -->
7154                                 </xsl:otherwise>
7155                         </xsl:choose>
7156                 </xsl:variable>
7157                 <xsl:variable name="sici3">
7158                         <xsl:choose>
7159                                 <xsl:when test="starts-with(substring-after($sici2,$level2),':')">
7160                                         <xsl:value-of select="substring(substring-after($sici2,$level2),2)"/>
7161                                 </xsl:when>
7162                                 <xsl:otherwise>
7163                                         <xsl:value-of select="substring-after($sici2,$level2)"/>
7164                                 </xsl:otherwise>
7165                         </xsl:choose>
7166                 </xsl:variable>
7167                 <xsl:variable name="level3">
7168                         <xsl:choose>
7169                                 <xsl:when test="contains($sici3,'&lt;')">
7170                                         <!-- 2<4 -->
7171                                         <xsl:value-of select="substring-before($sici3,'&lt;')"/>
7172                                 </xsl:when>
7173                                 <xsl:otherwise>
7174                                         <xsl:value-of select="$sici3"/>
7175                                         <!-- 3 -->
7176                                 </xsl:otherwise>
7177                         </xsl:choose>
7178                 </xsl:variable>
7179                 <xsl:variable name="page">
7180                         <xsl:if test="contains(text(),'&lt;')">
7181                                 <xsl:value-of select="substring-after(text(),'&lt;')"/>
7182                         </xsl:if>
7183                 </xsl:variable>
7184                 <xsl:if test="$level1">
7185                         <detail level="1">
7186                                 <number>
7187                                         <xsl:value-of select="$level1"/>
7188                                 </number>
7189                         </detail>
7190                 </xsl:if>
7191                 <xsl:if test="$level2">
7192                         <detail level="2">
7193                                 <number>
7194                                         <xsl:value-of select="$level2"/>
7195                                 </number>
7196                         </detail>
7197                 </xsl:if>
7198                 <xsl:if test="$level3">
7199                         <detail level="3">
7200                                 <number>
7201                                         <xsl:value-of select="$level3"/>
7202                                 </number>
7203                         </detail>
7204                 </xsl:if>
7205                 <xsl:if test="$page">
7206                         <extent unit="page">
7207                                 <start>
7208                                         <xsl:value-of select="$page"/>
7209                                 </start>
7210                         </extent>
7211                 </xsl:if>
7212         </xsl:template>
7213         <xsl:template name="getLanguage">
7214                 <xsl:param name="langString"/>
7215                 <xsl:param name="controlField008-35-37"/>
7216                 <xsl:variable name="length" select="string-length($langString)"/>
7217                 <xsl:choose>
7218                         <xsl:when test="$length=0"/>
7219                         <xsl:when test="$controlField008-35-37=substring($langString,1,3)">
7220                                 <xsl:call-template name="getLanguage">
7221                                         <xsl:with-param name="langString" select="substring($langString,4,$length)"/>
7222                                         <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"/>
7223                                 </xsl:call-template>
7224                         </xsl:when>
7225                         <xsl:otherwise>
7226                                 <language>
7227                                         <languageTerm authority="iso639-2b" type="code">
7228                                                 <xsl:value-of select="substring($langString,1,3)"/>
7229                                         </languageTerm>
7230                                 </language>
7231                                 <xsl:call-template name="getLanguage">
7232                                         <xsl:with-param name="langString" select="substring($langString,4,$length)"/>
7233                                         <xsl:with-param name="controlField008-35-37" select="$controlField008-35-37"/>
7234                                 </xsl:call-template>
7235                         </xsl:otherwise>
7236                 </xsl:choose>
7237         </xsl:template>
7238         <xsl:template name="isoLanguage">
7239                 <xsl:param name="currentLanguage"/>
7240                 <xsl:param name="usedLanguages"/>
7241                 <xsl:param name="remainingLanguages"/>
7242                 <xsl:choose>
7243                         <xsl:when test="string-length($currentLanguage)=0"/>
7244                         <xsl:when test="not(contains($usedLanguages, $currentLanguage))">
7245                                 <language>
7246                                         <xsl:if test="@code!='a'">
7247                                                 <xsl:attribute name="objectPart">
7248                                                         <xsl:choose>
7249                                                                 <xsl:when test="@code='b'">summary or subtitle</xsl:when>
7250                                                                 <xsl:when test="@code='d'">sung or spoken text</xsl:when>
7251                                                                 <xsl:when test="@code='e'">libretto</xsl:when>
7252                                                                 <xsl:when test="@code='f'">table of contents</xsl:when>
7253                                                                 <xsl:when test="@code='g'">accompanying material</xsl:when>
7254                                                                 <xsl:when test="@code='h'">translation</xsl:when>
7255                                                         </xsl:choose>
7256                                                 </xsl:attribute>
7257                                         </xsl:if>
7258                                         <languageTerm authority="iso639-2b" type="code">
7259                                                 <xsl:value-of select="$currentLanguage"/>
7260                                         </languageTerm>
7261                                 </language>
7262                                 <xsl:call-template name="isoLanguage">
7263                                         <xsl:with-param name="currentLanguage">
7264                                                 <xsl:value-of select="substring($remainingLanguages,1,3)"/>
7265                                         </xsl:with-param>
7266                                         <xsl:with-param name="usedLanguages">
7267                                                 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"/>
7268                                         </xsl:with-param>
7269                                         <xsl:with-param name="remainingLanguages">
7270                                                 <xsl:value-of
7271                                                         select="substring($remainingLanguages,4,string-length($remainingLanguages))"
7272                                                 />
7273                                         </xsl:with-param>
7274                                 </xsl:call-template>
7275                         </xsl:when>
7276                         <xsl:otherwise>
7277                                 <xsl:call-template name="isoLanguage">
7278                                         <xsl:with-param name="currentLanguage">
7279                                                 <xsl:value-of select="substring($remainingLanguages,1,3)"/>
7280                                         </xsl:with-param>
7281                                         <xsl:with-param name="usedLanguages">
7282                                                 <xsl:value-of select="concat($usedLanguages,$currentLanguage)"/>
7283                                         </xsl:with-param>
7284                                         <xsl:with-param name="remainingLanguages">
7285                                                 <xsl:value-of
7286                                                         select="substring($remainingLanguages,4,string-length($remainingLanguages))"
7287                                                 />
7288                                         </xsl:with-param>
7289                                 </xsl:call-template>
7290                         </xsl:otherwise>
7291                 </xsl:choose>
7292         </xsl:template>
7293         <xsl:template name="chopBrackets">
7294                 <xsl:param name="chopString"/>
7295                 <xsl:variable name="string">
7296                         <xsl:call-template name="chopPunctuation">
7297                                 <xsl:with-param name="chopString" select="$chopString"/>
7298                         </xsl:call-template>
7299                 </xsl:variable>
7300                 <xsl:if test="substring($string, 1,1)='['">
7301                         <xsl:value-of select="substring($string,2, string-length($string)-2)"/>
7302                 </xsl:if>
7303                 <xsl:if test="substring($string, 1,1)!='['">
7304                         <xsl:value-of select="$string"/>
7305                 </xsl:if>
7306         </xsl:template>
7307         <xsl:template name="rfcLanguages">
7308                 <xsl:param name="nodeNum"/>
7309                 <xsl:param name="usedLanguages"/>
7310                 <xsl:param name="controlField008-35-37"/>
7311                 <xsl:variable name="currentLanguage" select="."/>
7312                 <xsl:choose>
7313                         <xsl:when test="not($currentLanguage)"/>
7314                         <xsl:when
7315                                 test="$currentLanguage!=$controlField008-35-37 and $currentLanguage!='rfc3066'">
7316                                 <xsl:if test="not(contains($usedLanguages,$currentLanguage))">
7317                                         <language>
7318                                                 <xsl:if test="@code!='a'">
7319                                                         <xsl:attribute name="objectPart">
7320                                                                 <xsl:choose>
7321                                                                         <xsl:when test="@code='b'">summary or subtitle</xsl:when>
7322                                                                         <xsl:when test="@code='d'">sung or spoken text</xsl:when>
7323                                                                         <xsl:when test="@code='e'">libretto</xsl:when>
7324                                                                         <xsl:when test="@code='f'">table of contents</xsl:when>
7325                                                                         <xsl:when test="@code='g'">accompanying material</xsl:when>
7326                                                                         <xsl:when test="@code='h'">translation</xsl:when>
7327                                                                 </xsl:choose>
7328                                                         </xsl:attribute>
7329                                                 </xsl:if>
7330                                                 <languageTerm authority="rfc3066" type="code">
7331                                                         <xsl:value-of select="$currentLanguage"/>
7332                                                 </languageTerm>
7333                                         </language>
7334                                 </xsl:if>
7335                         </xsl:when>
7336                         <xsl:otherwise> </xsl:otherwise>
7337                 </xsl:choose>
7338         </xsl:template>
7339
7340     <xsl:template name="datafield">
7341                 <xsl:param name="tag"/>
7342                 <xsl:param name="ind1">
7343                         <xsl:text> </xsl:text>
7344                 </xsl:param>
7345                 <xsl:param name="ind2">
7346                         <xsl:text> </xsl:text>
7347                 </xsl:param>
7348                 <xsl:param name="subfields"/>
7349                 <xsl:element name="marc:datafield">
7350                         <xsl:attribute name="tag">
7351                                 <xsl:value-of select="$tag"/>
7352                         </xsl:attribute>
7353                         <xsl:attribute name="ind1">
7354                                 <xsl:value-of select="$ind1"/>
7355                         </xsl:attribute>
7356                         <xsl:attribute name="ind2">
7357                                 <xsl:value-of select="$ind2"/>
7358                         </xsl:attribute>
7359                         <xsl:copy-of select="$subfields"/>
7360                 </xsl:element>
7361         </xsl:template>
7362
7363         <xsl:template name="subfieldSelect">
7364                 <xsl:param name="codes">abcdefghijklmnopqrstuvwxyz</xsl:param>
7365                 <xsl:param name="delimeter">
7366                         <xsl:text> </xsl:text>
7367                 </xsl:param>
7368                 <xsl:variable name="str">
7369                         <xsl:for-each select="marc:subfield">
7370                                 <xsl:if test="contains($codes, @code)">
7371                                         <xsl:value-of select="text()"/>
7372                                         <xsl:value-of select="$delimeter"/>
7373                                 </xsl:if>
7374                         </xsl:for-each>
7375                 </xsl:variable>
7376                 <xsl:value-of select="substring($str,1,string-length($str)-string-length($delimeter))"/>
7377         </xsl:template>
7378
7379         <xsl:template name="buildSpaces">
7380                 <xsl:param name="spaces"/>
7381                 <xsl:param name="char">
7382                         <xsl:text> </xsl:text>
7383                 </xsl:param>
7384                 <xsl:if test="$spaces>0">
7385                         <xsl:value-of select="$char"/>
7386                         <xsl:call-template name="buildSpaces">
7387                                 <xsl:with-param name="spaces" select="$spaces - 1"/>
7388                                 <xsl:with-param name="char" select="$char"/>
7389                         </xsl:call-template>
7390                 </xsl:if>
7391         </xsl:template>
7392
7393         <xsl:template name="chopPunctuation">
7394                 <xsl:param name="chopString"/>
7395                 <xsl:param name="punctuation">
7396                         <xsl:text>.:,;/ </xsl:text>
7397                 </xsl:param>
7398                 <xsl:variable name="length" select="string-length($chopString)"/>
7399                 <xsl:choose>
7400                         <xsl:when test="$length=0"/>
7401                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
7402                                 <xsl:call-template name="chopPunctuation">
7403                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
7404                                         <xsl:with-param name="punctuation" select="$punctuation"/>
7405                                 </xsl:call-template>
7406                         </xsl:when>
7407                         <xsl:when test="not($chopString)"/>
7408                         <xsl:otherwise>
7409                                 <xsl:value-of select="$chopString"/>
7410                         </xsl:otherwise>
7411                 </xsl:choose>
7412         </xsl:template>
7413
7414         <xsl:template name="chopPunctuationFront">
7415                 <xsl:param name="chopString"/>
7416                 <xsl:variable name="length" select="string-length($chopString)"/>
7417                 <xsl:choose>
7418                         <xsl:when test="$length=0"/>
7419                         <xsl:when test="contains('.:,;/[ ', substring($chopString,1,1))">
7420                                 <xsl:call-template name="chopPunctuationFront">
7421                                         <xsl:with-param name="chopString" select="substring($chopString,2,$length - 1)"
7422                                         />
7423                                 </xsl:call-template>
7424                         </xsl:when>
7425                         <xsl:when test="not($chopString)"/>
7426                         <xsl:otherwise>
7427                                 <xsl:value-of select="$chopString"/>
7428                         </xsl:otherwise>
7429                 </xsl:choose>
7430         </xsl:template>
7431
7432         <xsl:template name="chopPunctuationBack">
7433                 <xsl:param name="chopString"/>
7434                 <xsl:param name="punctuation">
7435                         <xsl:text>.:,;/] </xsl:text>
7436                 </xsl:param>
7437                 <xsl:variable name="length" select="string-length($chopString)"/>
7438                 <xsl:choose>
7439                         <xsl:when test="$length=0"/>
7440                         <xsl:when test="contains($punctuation, substring($chopString,$length,1))">
7441                                 <xsl:call-template name="chopPunctuation">
7442                                         <xsl:with-param name="chopString" select="substring($chopString,1,$length - 1)"/>
7443                                         <xsl:with-param name="punctuation" select="$punctuation"/>
7444                                 </xsl:call-template>
7445                         </xsl:when>
7446                         <xsl:when test="not($chopString)"/>
7447                         <xsl:otherwise>
7448                                 <xsl:value-of select="$chopString"/>
7449                         </xsl:otherwise>
7450                 </xsl:choose>
7451         </xsl:template>
7452
7453         <!-- nate added 12/14/2007 for lccn.loc.gov: url encode ampersand, etc. -->
7454         <xsl:template name="url-encode">
7455
7456                 <xsl:param name="str"/>
7457
7458                 <xsl:if test="$str">
7459                         <xsl:variable name="first-char" select="substring($str,1,1)"/>
7460                         <xsl:choose>
7461                                 <xsl:when test="contains($safe,$first-char)">
7462                                         <xsl:value-of select="$first-char"/>
7463                                 </xsl:when>
7464                                 <xsl:otherwise>
7465                                         <xsl:variable name="codepoint">
7466                                                 <xsl:choose>
7467                                                         <xsl:when test="contains($ascii,$first-char)">
7468                                                                 <xsl:value-of
7469                                                                         select="string-length(substring-before($ascii,$first-char)) + 32"
7470                                                                 />
7471                                                         </xsl:when>
7472                                                         <xsl:when test="contains($latin1,$first-char)">
7473                                                                 <xsl:value-of
7474                                                                         select="string-length(substring-before($latin1,$first-char)) + 160"/>
7475                                                                 <!-- was 160 -->
7476                                                         </xsl:when>
7477                                                         <xsl:otherwise>
7478                                                                 <xsl:message terminate="no">Warning: string contains a character
7479                                                                         that is out of range! Substituting "?".</xsl:message>
7480                                                                 <xsl:text>63</xsl:text>
7481                                                         </xsl:otherwise>
7482                                                 </xsl:choose>
7483                                         </xsl:variable>
7484                                         <xsl:variable name="hex-digit1"
7485                                                 select="substring($hex,floor($codepoint div 16) + 1,1)"/>
7486                                         <xsl:variable name="hex-digit2" select="substring($hex,$codepoint mod 16 + 1,1)"/>
7487                                         <!-- <xsl:value-of select="concat('%',$hex-digit2)"/> -->
7488                                         <xsl:value-of select="concat('%',$hex-digit1,$hex-digit2)"/>
7489                                 </xsl:otherwise>
7490                         </xsl:choose>
7491                         <xsl:if test="string-length($str) &gt; 1">
7492                                 <xsl:call-template name="url-encode">
7493                                         <xsl:with-param name="str" select="substring($str,2)"/>
7494                                 </xsl:call-template>
7495                         </xsl:if>
7496                 </xsl:if>
7497         </xsl:template>
7498 </xsl:stylesheet>$$ WHERE name = 'mods33';
7499
7500 ALTER TABLE actor.usr ALTER COLUMN juvenile SET NOT NULL;
7501 ALTER TABLE actor.usr_address ALTER COLUMN pending SET NOT NULL;
7502 ALTER TABLE config.circ_matrix_matchpoint ALTER COLUMN duration_rule SET NOT NULL;
7503 ALTER TABLE config.circ_matrix_matchpoint ALTER COLUMN recurring_fine_rule SET NOT NULL;
7504 ALTER TABLE config.circ_matrix_matchpoint ALTER COLUMN max_fine_rule SET NOT NULL;
7505
7506 -- We're updating the IDL, so flush cached mods slim records to avoid field mismatches
7507 UPDATE metabib.metarecord SET mods = NULL;