1 --Upgrade Script for 2.1 to 2.2-alpha1
3 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('2.2-alpha1');
4 -- DROP objects that might have existed from a prior run of 0526
6 DROP TABLE IF EXISTS config.db_patch_dependencies;
7 ALTER TABLE config.upgrade_log DROP COLUMN applied_to;
8 DROP FUNCTION evergreen.upgrade_list_applied_deprecates(TEXT);
9 DROP FUNCTION evergreen.upgrade_list_applied_supersedes(TEXT);
12 INSERT INTO config.upgrade_log (version) VALUES ('0526'); --miker
14 CREATE TABLE config.db_patch_dependencies (
15 db_patch TEXT PRIMARY KEY,
20 CREATE OR REPLACE FUNCTION evergreen.array_overlap_check (/* field */) RETURNS TRIGGER AS $$
26 EXECUTE 'SELECT COUNT(*) FROM '|| TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME ||' WHERE '|| fld ||' && ($1).'|| fld INTO cnt USING NEW;
28 RAISE EXCEPTION 'Cannot insert duplicate array into field % of table %', fld, TG_TABLE_SCHEMA ||'.'|| TG_TABLE_NAME;
34 CREATE TRIGGER no_overlapping_sups
35 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
36 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('supersedes');
38 CREATE TRIGGER no_overlapping_deps
39 BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
40 FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
42 ALTER TABLE config.upgrade_log
43 ADD COLUMN applied_to TEXT;
45 -- Provide a named type for patching functions
46 CREATE TYPE evergreen.patch AS (patch TEXT);
48 -- List applied db patches that are deprecated by (and block the application of) my_db_patch
49 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecates ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
50 SELECT DISTINCT l.version
51 FROM config.upgrade_log l
52 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.deprecates)
56 -- List applied db patches that are superseded by (and block the application of) my_db_patch
57 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_supersedes ( my_db_patch TEXT ) RETURNS SETOF evergreen.patch AS $$
58 SELECT DISTINCT l.version
59 FROM config.upgrade_log l
60 JOIN config.db_patch_dependencies d ON (l.version::TEXT[] && d.supersedes)
64 -- List applied db patches that deprecates (and block the application of) my_db_patch
65 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS TEXT AS $$
67 FROM config.db_patch_dependencies
68 WHERE ARRAY[$1]::TEXT[] && deprecates
71 -- List applied db patches that supersedes (and block the application of) my_db_patch
72 CREATE OR REPLACE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS TEXT AS $$
74 FROM config.db_patch_dependencies
75 WHERE ARRAY[$1]::TEXT[] && supersedes
78 -- Make sure that no deprecated or superseded db patches are currently applied
79 CREATE OR REPLACE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
81 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
83 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
85 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
87 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
90 -- Raise an exception if there are, in fact, dep/sup confilct
91 CREATE OR REPLACE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
96 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
97 SELECT STRING_AGG(patch, ', ') INTO deprecates FROM evergreen.upgrade_list_applied_deprecates(my_db_patch);
98 SELECT STRING_AGG(patch, ', ') INTO supersedes FROM evergreen.upgrade_list_applied_supersedes(my_db_patch);
100 Upgrade script % can not be applied:
101 applied deprecated scripts %
102 applied superseded scripts %
106 ARRAY_AGG(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
107 ARRAY_AGG(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
108 evergreen.upgrade_list_applied_deprecated(my_db_patch),
109 evergreen.upgrade_list_applied_superseded(my_db_patch);
112 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
117 -- Evergreen DB patch 0536.schema.lazy_circ-barcode_lookup.sql
119 -- FIXME: insert description of change, if needed
122 -- check whether patch can be applied
123 INSERT INTO config.upgrade_log (version) VALUES ('0536');
125 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype) VALUES ( 'circ.staff_client.actor_on_checkout', 'Load patron from Checkout', 'When scanning barcodes into Checkout auto-detect if a new patron barcode is scanned and auto-load the new patron.', 'bool');
127 CREATE TABLE config.barcode_completion (
128 id SERIAL PRIMARY KEY,
129 active BOOL NOT NULL DEFAULT true,
130 org_unit INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
133 length INT NOT NULL DEFAULT 0,
135 padding_end BOOL NOT NULL DEFAULT false,
136 asset BOOL NOT NULL DEFAULT true,
137 actor BOOL NOT NULL DEFAULT true
140 CREATE TYPE evergreen.barcode_set AS (type TEXT, id BIGINT, barcode TEXT);
142 CREATE OR REPLACE FUNCTION evergreen.get_barcodes(select_ou INT, type TEXT, in_barcode TEXT) RETURNS SETOF evergreen.barcode_set AS $$
147 asset_barcodes TEXT[];
148 actor_barcodes TEXT[];
149 do_asset BOOL = false;
150 do_serial BOOL = false;
151 do_booking BOOL = false;
152 do_actor BOOL = false;
153 completion_set config.barcode_completion%ROWTYPE;
156 IF position('asset' in type) > 0 THEN
159 IF position('serial' in type) > 0 THEN
162 IF position('booking' in type) > 0 THEN
165 IF do_asset OR do_serial OR do_booking THEN
166 asset_barcodes = asset_barcodes || in_barcode;
168 IF position('actor' in type) > 0 THEN
170 actor_barcodes = actor_barcodes || in_barcode;
173 barcode_len := length(in_barcode);
175 FOR completion_set IN
176 SELECT * FROM config.barcode_completion
178 AND org_unit IN (SELECT aou.id FROM actor.org_unit_ancestors(select_ou) aou)
180 IF completion_set.prefix IS NULL THEN
181 completion_set.prefix := '';
183 IF completion_set.suffix IS NULL THEN
184 completion_set.suffix := '';
186 IF completion_set.length = 0 OR completion_set.padding IS NULL OR length(completion_set.padding) = 0 THEN
187 cur_barcode = completion_set.prefix || in_barcode || completion_set.suffix;
189 completion_len = completion_set.length - length(completion_set.prefix) - length(completion_set.suffix);
190 IF completion_len >= barcode_len THEN
191 IF completion_set.padding_end THEN
192 cur_barcode = rpad(in_barcode, completion_len, completion_set.padding);
194 cur_barcode = lpad(in_barcode, completion_len, completion_set.padding);
196 cur_barcode = completion_set.prefix || cur_barcode || completion_set.suffix;
199 IF completion_set.actor THEN
200 actor_barcodes = actor_barcodes || cur_barcode;
202 IF completion_set.asset THEN
203 asset_barcodes = asset_barcodes || cur_barcode;
207 IF do_asset AND do_serial THEN
208 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM ONLY asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
209 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
211 RETURN QUERY SELECT 'asset'::TEXT, id, barcode FROM asset.copy WHERE barcode = ANY(asset_barcodes) AND deleted = false;
213 RETURN QUERY SELECT 'serial'::TEXT, id, barcode FROM serial.unit WHERE barcode = ANY(asset_barcodes) AND deleted = false;
216 RETURN QUERY SELECT 'booking'::TEXT, id::BIGINT, barcode FROM booking.resource WHERE barcode = ANY(asset_barcodes);
219 RETURN QUERY SELECT 'actor'::TEXT, c.usr::BIGINT, c.barcode FROM actor.card c JOIN actor.usr u ON c.usr = u.id WHERE c.barcode = ANY(actor_barcodes) AND c.active AND NOT u.deleted ORDER BY usr;
225 COMMENT ON FUNCTION evergreen.get_barcodes(INT, TEXT, TEXT) IS $$
226 Given user input, find an appropriate barcode in the proper class.
228 Will add prefix/suffix information to do so, and return all results.
233 INSERT INTO config.upgrade_log (version) VALUES ('0537'); --miker
235 DROP FUNCTION evergreen.upgrade_deps_block_check(text,text);
236 DROP FUNCTION evergreen.upgrade_verify_no_dep_conflicts(text);
237 DROP FUNCTION evergreen.upgrade_list_applied_deprecated(text);
238 DROP FUNCTION evergreen.upgrade_list_applied_superseded(text);
240 -- List applied db patches that deprecates (and block the application of) my_db_patch
241 CREATE FUNCTION evergreen.upgrade_list_applied_deprecated ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
243 FROM config.db_patch_dependencies
244 WHERE ARRAY[$1]::TEXT[] && deprecates
247 -- List applied db patches that supersedes (and block the application of) my_db_patch
248 CREATE FUNCTION evergreen.upgrade_list_applied_superseded ( my_db_patch TEXT ) RETURNS SETOF TEXT AS $$
250 FROM config.db_patch_dependencies
251 WHERE ARRAY[$1]::TEXT[] && supersedes
254 -- Make sure that no deprecated or superseded db patches are currently applied
255 CREATE FUNCTION evergreen.upgrade_verify_no_dep_conflicts ( my_db_patch TEXT ) RETURNS BOOL AS $$
257 FROM (SELECT * FROM evergreen.upgrade_list_applied_deprecates( $1 )
259 SELECT * FROM evergreen.upgrade_list_applied_supersedes( $1 )
261 SELECT * FROM evergreen.upgrade_list_applied_deprecated( $1 )
263 SELECT * FROM evergreen.upgrade_list_applied_superseded( $1 ))x
266 -- Raise an exception if there are, in fact, dep/sup confilct
267 CREATE FUNCTION evergreen.upgrade_deps_block_check ( my_db_patch TEXT, my_applied_to TEXT ) RETURNS BOOL AS $$
269 IF NOT evergreen.upgrade_verify_no_dep_conflicts( my_db_patch ) THEN
271 Upgrade script % can not be applied:
272 applied deprecated scripts %
273 applied superseded scripts %
277 ARRAY_ACCUM(evergreen.upgrade_list_applied_deprecates(my_db_patch)),
278 ARRAY_ACCUM(evergreen.upgrade_list_applied_supersedes(my_db_patch)),
279 evergreen.upgrade_list_applied_deprecated(my_db_patch),
280 evergreen.upgrade_list_applied_superseded(my_db_patch);
283 INSERT INTO config.upgrade_log (version, applied_to) VALUES (my_db_patch, my_applied_to);
289 INSERT INTO config.upgrade_log (version) VALUES ('0544');
291 INSERT INTO config.usr_setting_type
292 ( name, opac_visible, label, description, datatype) VALUES
293 ( 'circ.collections.exempt',
295 oils_i18n_gettext('circ.collections.exempt', 'Collections: Exempt', 'cust', 'description'),
296 oils_i18n_gettext('circ.collections.exempt', 'User is exempt from collections tracking/processing', 'cust', 'description'),
302 SELECT evergreen.upgrade_deps_block_check('0545', :eg_version);
304 INSERT INTO permission.perm_list VALUES
305 (507, 'ABORT_TRANSIT_ON_LOST', oils_i18n_gettext(507, 'Allows a user to abort a transit on a copy with status of LOST', 'ppl', 'description')),
306 (508, 'ABORT_TRANSIT_ON_MISSING', oils_i18n_gettext(508, 'Allows a user to abort a transit on a copy with status of MISSING', 'ppl', 'description'));
308 --- stock Circulation Administrator group
310 INSERT INTO permission.grp_perm_map ( grp, perm, depth, grantable )
316 FROM permission.perm_list
317 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING');
319 -- Evergreen DB patch 0546.schema.sip_statcats.sql
322 -- check whether patch can be applied
323 SELECT evergreen.upgrade_deps_block_check('0546', :eg_version);
325 CREATE TABLE actor.stat_cat_sip_fields (
326 field CHAR(2) PRIMARY KEY,
328 one_only BOOL NOT NULL DEFAULT FALSE
330 COMMENT ON TABLE actor.stat_cat_sip_fields IS $$
331 Actor Statistical Category SIP Fields
333 Contains the list of valid SIP Field identifiers for
334 Statistical Categories.
336 ALTER TABLE actor.stat_cat
337 ADD COLUMN sip_field CHAR(2) REFERENCES actor.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
338 ADD COLUMN sip_format TEXT;
340 CREATE FUNCTION actor.stat_cat_check() RETURNS trigger AS $func$
342 sipfield actor.stat_cat_sip_fields%ROWTYPE;
345 IF NEW.sip_field IS NOT NULL THEN
346 SELECT INTO sipfield * FROM actor.stat_cat_sip_fields WHERE field = NEW.sip_field;
347 IF sipfield.one_only THEN
348 SELECT INTO use_count count(id) FROM actor.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
349 IF use_count > 0 THEN
350 RAISE EXCEPTION 'Sip field cannot be used twice';
356 $func$ LANGUAGE PLPGSQL;
358 CREATE TRIGGER actor_stat_cat_sip_update_trigger
359 BEFORE INSERT OR UPDATE ON actor.stat_cat FOR EACH ROW
360 EXECUTE PROCEDURE actor.stat_cat_check();
362 CREATE TABLE asset.stat_cat_sip_fields (
363 field CHAR(2) PRIMARY KEY,
365 one_only BOOL NOT NULL DEFAULT FALSE
367 COMMENT ON TABLE asset.stat_cat_sip_fields IS $$
368 Asset Statistical Category SIP Fields
370 Contains the list of valid SIP Field identifiers for
371 Statistical Categories.
374 ALTER TABLE asset.stat_cat
375 ADD COLUMN sip_field CHAR(2) REFERENCES asset.stat_cat_sip_fields(field) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
376 ADD COLUMN sip_format TEXT;
378 CREATE FUNCTION asset.stat_cat_check() RETURNS trigger AS $func$
380 sipfield asset.stat_cat_sip_fields%ROWTYPE;
383 IF NEW.sip_field IS NOT NULL THEN
384 SELECT INTO sipfield * FROM asset.stat_cat_sip_fields WHERE field = NEW.sip_field;
385 IF sipfield.one_only THEN
386 SELECT INTO use_count count(id) FROM asset.stat_cat WHERE sip_field = NEW.sip_field AND id != NEW.id;
387 IF use_count > 0 THEN
388 RAISE EXCEPTION 'Sip field cannot be used twice';
394 $func$ LANGUAGE PLPGSQL;
396 CREATE TRIGGER asset_stat_cat_sip_update_trigger
397 BEFORE INSERT OR UPDATE ON asset.stat_cat FOR EACH ROW
398 EXECUTE PROCEDURE asset.stat_cat_check();
402 SELECT evergreen.upgrade_deps_block_check('0548', :eg_version); -- dbwells
404 \qecho This redoes the original part 1 of 0547 which did not apply to rel_2_1,
405 \qecho and is being added for the sake of clarity
407 -- delete errant inserts from 0545 (group 4 is NOT the circulation admin group)
408 DELETE FROM permission.grp_perm_map WHERE grp = 4 AND perm IN (
409 SELECT id FROM permission.perm_list
410 WHERE code in ('ABORT_TRANSIT_ON_LOST', 'ABORT_TRANSIT_ON_MISSING')
413 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
415 pgt.id, perm.id, aout.depth, TRUE
417 permission.grp_tree pgt,
418 permission.perm_list perm,
419 actor.org_unit_type aout
421 pgt.name = 'Circulation Administrator' AND
422 aout.name = 'Consortium' AND
424 'ABORT_TRANSIT_ON_LOST',
425 'ABORT_TRANSIT_ON_MISSING'
428 FROM permission.grp_perm_map AS map
431 AND map.perm = perm.id
434 -- Evergreen DB patch XXXX.data.transit-checkin-interval.sql
436 -- New org unit setting "circ.transit.min_checkin_interval"
437 -- New TRANSIT_CHECKIN_INTERVAL_BLOCK.override permission
441 -- check whether patch can be applied
442 SELECT evergreen.upgrade_deps_block_check('0549', :eg_version);
444 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
445 'circ.transit.min_checkin_interval',
447 'circ.transit.min_checkin_interval',
448 'Circ: Minimum Transit Checkin Interval',
453 'circ.transit.min_checkin_interval',
454 'In-Transit items checked in this close to the transit start time will be prevented from checking in',
461 INSERT INTO permission.perm_list ( id, code, description ) VALUES (
463 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override',
466 'Allows a user to override the TRANSIT_CHECKIN_INTERVAL_BLOCK event',
472 -- add the perm to the default circ admin group
473 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
475 pgt.id, perm.id, aout.depth, TRUE
477 permission.grp_tree pgt,
478 permission.perm_list perm,
479 actor.org_unit_type aout
481 pgt.name = 'Circulation Administrator' AND
482 aout.name = 'System' AND
483 perm.code IN ( 'TRANSIT_CHECKIN_INTERVAL_BLOCK.override' );
486 -- check whether patch can be applied
487 SELECT evergreen.upgrade_deps_block_check('0550', :eg_version);
489 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
490 'org.patron_opt_boundary',
492 'org.patron_opt_boundary',
493 'Circ: Patron Opt-In Boundary',
498 'org.patron_opt_boundary',
499 'This determines at which depth above which patrons must be opted in, and below which patrons will be assumed to be opted in.',
506 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
507 'org.patron_opt_default',
509 'org.patron_opt_default',
510 'Circ: Patron Opt-In Default',
515 'org.patron_opt_default',
516 'This is the default depth at which a patron is opted in; it is calculated as an org unit relative to the current workstation.',
523 -- Evergreen DB patch 0562.schema.copy_active_date.sql
528 -- check whether patch can be applied
529 SELECT evergreen.upgrade_deps_block_check('0562', :eg_version);
531 ALTER TABLE asset.copy
532 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
534 ALTER TABLE auditor.asset_copy_history
535 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
537 ALTER TABLE auditor.serial_unit_history
538 ADD COLUMN active_date TIMESTAMP WITH TIME ZONE;
540 ALTER TABLE config.copy_status
541 ADD COLUMN copy_active BOOL NOT NULL DEFAULT FALSE;
543 ALTER TABLE config.circ_matrix_weights
544 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
546 ALTER TABLE config.hold_matrix_weights
547 ADD COLUMN item_age NUMERIC(6,2) NOT NULL DEFAULT 0.0;
549 -- The two defaults above were to stop erroring on NOT NULL
551 ALTER TABLE config.circ_matrix_weights
552 ALTER COLUMN item_age DROP DEFAULT;
554 ALTER TABLE config.hold_matrix_weights
555 ALTER COLUMN item_age DROP DEFAULT;
557 ALTER TABLE config.circ_matrix_matchpoint
558 ADD COLUMN item_age INTERVAL;
560 ALTER TABLE config.hold_matrix_matchpoint
561 ADD COLUMN item_age INTERVAL;
563 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
564 RETURNS TRIGGER AS $$
566 IF NEW.status <> OLD.status THEN
567 NEW.status_changed_time := now();
568 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
569 NEW.active_date := now();
576 CREATE OR REPLACE FUNCTION asset.acp_created()
577 RETURNS TRIGGER AS $$
579 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
580 NEW.active_date := now();
582 IF NEW.status_changed_time IS NULL THEN
583 NEW.status_changed_time := now();
589 CREATE TRIGGER acp_created_trig
590 BEFORE INSERT ON asset.copy
591 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
593 CREATE TRIGGER sunit_created_trig
594 BEFORE INSERT ON serial.unit
595 FOR EACH ROW EXECUTE PROCEDURE asset.acp_created();
597 CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
600 user_object actor.usr%ROWTYPE;
601 age_protect_object config.rule_age_hold_protect%ROWTYPE;
602 standing_penalty config.standing_penalty%ROWTYPE;
603 transit_range_ou_type actor.org_unit_type%ROWTYPE;
604 transit_source actor.org_unit%ROWTYPE;
605 item_object asset.copy%ROWTYPE;
606 item_cn_object asset.call_number%ROWTYPE;
607 ou_skip actor.org_unit_setting%ROWTYPE;
608 result action.matrix_test_result;
609 hold_test config.hold_matrix_matchpoint%ROWTYPE;
610 use_active_date TEXT;
611 age_protect_date TIMESTAMP WITH TIME ZONE;
613 hold_transit_prox INT;
614 frozen_hold_count INT;
615 context_org_list INT[];
618 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
619 SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
621 result.success := TRUE;
623 -- Fail if we couldn't find a user
624 IF user_object.id IS NULL THEN
625 result.fail_part := 'no_user';
626 result.success := FALSE;
632 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
634 -- Fail if we couldn't find a copy
635 IF item_object.id IS NULL THEN
636 result.fail_part := 'no_item';
637 result.success := FALSE;
643 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
644 result.matchpoint := matchpoint_id;
646 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
648 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
649 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
650 result.fail_part := 'circ.holds.target_skip_me';
651 result.success := FALSE;
657 -- Fail if user is barred
658 IF user_object.barred IS TRUE THEN
659 result.fail_part := 'actor.usr.barred';
660 result.success := FALSE;
666 -- Fail if we couldn't find any matchpoint (requires a default)
667 IF matchpoint_id IS NULL THEN
668 result.fail_part := 'no_matchpoint';
669 result.success := FALSE;
675 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
677 IF hold_test.holdable IS FALSE THEN
678 result.fail_part := 'config.hold_matrix_test.holdable';
679 result.success := FALSE;
684 IF hold_test.transit_range IS NOT NULL THEN
685 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
686 IF hold_test.distance_is_from_owner THEN
687 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;
689 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
692 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
695 result.fail_part := 'transit_range';
696 result.success := FALSE;
702 FOR standing_penalty IN
703 SELECT DISTINCT csp.*
704 FROM actor.usr_standing_penalty usp
705 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
706 WHERE usr = match_user
707 AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
708 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
709 AND csp.block_list LIKE '%HOLD%' LOOP
711 result.fail_part := standing_penalty.name;
712 result.success := FALSE;
717 IF hold_test.stop_blocked_user IS TRUE THEN
718 FOR standing_penalty IN
719 SELECT DISTINCT csp.*
720 FROM actor.usr_standing_penalty usp
721 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
722 WHERE usr = match_user
723 AND usp.org_unit IN ( SELECT * FROM explode_array(context_org_list) )
724 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
725 AND csp.block_list LIKE '%CIRC%' LOOP
727 result.fail_part := standing_penalty.name;
728 result.success := FALSE;
734 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
735 SELECT INTO hold_count COUNT(*)
736 FROM action.hold_request
737 WHERE usr = match_user
738 AND fulfillment_time IS NULL
739 AND cancel_time IS NULL
740 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
742 IF hold_count >= hold_test.max_holds THEN
743 result.fail_part := 'config.hold_matrix_test.max_holds';
744 result.success := FALSE;
750 IF item_object.age_protect IS NOT NULL THEN
751 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
752 IF hold_test.distance_is_from_owner THEN
753 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
755 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
757 IF use_active_date = 'true' THEN
758 age_protect_date := COALESCE(item_object.active_date, NOW());
760 age_protect_date := item_object.create_date;
762 IF age_protect_date + age_protect_object.age > NOW() THEN
763 IF hold_test.distance_is_from_owner THEN
764 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
765 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
767 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
770 IF hold_transit_prox > age_protect_object.prox THEN
771 result.fail_part := 'config.rule_age_hold_protect.prox';
772 result.success := FALSE;
785 $func$ LANGUAGE plpgsql;
787 CREATE OR REPLACE FUNCTION action.find_circ_matrix_matchpoint( context_ou INT, item_object asset.copy, user_object actor.usr, renewal BOOL ) RETURNS action.found_circ_matrix_matchpoint AS $func$
789 cn_object asset.call_number%ROWTYPE;
790 rec_descriptor metabib.rec_descriptor%ROWTYPE;
791 cur_matchpoint config.circ_matrix_matchpoint%ROWTYPE;
792 matchpoint config.circ_matrix_matchpoint%ROWTYPE;
793 weights config.circ_matrix_weights%ROWTYPE;
795 my_item_age INTERVAL;
796 denominator NUMERIC(6,2);
798 result action.found_circ_matrix_matchpoint;
801 result.success = false;
804 SELECT INTO cn_object * FROM asset.call_number WHERE id = item_object.call_number;
805 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = cn_object.record;
807 -- Pre-generate this so we only calc it once
808 IF user_object.dob IS NOT NULL THEN
809 SELECT INTO user_age age(user_object.dob);
813 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
815 -- Grab the closest set circ weight setting.
816 SELECT INTO weights cw.*
817 FROM config.weight_assoc wa
818 JOIN config.circ_matrix_weights cw ON (cw.id = wa.circ_weights)
819 JOIN actor.org_unit_ancestors_distance( context_ou ) d ON (wa.org_unit = d.id)
824 -- No weights? Bad admin! Defaults to handle that anyway.
825 IF weights.id IS NULL THEN
827 weights.org_unit := 10.0;
828 weights.circ_modifier := 5.0;
829 weights.marc_type := 4.0;
830 weights.marc_form := 3.0;
831 weights.marc_bib_level := 2.0;
832 weights.marc_vr_format := 2.0;
833 weights.copy_circ_lib := 8.0;
834 weights.copy_owning_lib := 8.0;
835 weights.user_home_ou := 8.0;
836 weights.ref_flag := 1.0;
837 weights.juvenile_flag := 6.0;
838 weights.is_renewal := 7.0;
839 weights.usr_age_lower_bound := 0.0;
840 weights.usr_age_upper_bound := 0.0;
841 weights.item_age := 0.0;
844 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
845 -- If you break your org tree with funky parenting this may be wrong
846 -- Note: This CTE is duplicated in the find_hold_matrix_matchpoint function, and it may be a good idea to split it off to a function
847 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
848 WITH all_distance(distance) AS (
849 SELECT depth AS distance FROM actor.org_unit_type
851 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
853 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
855 -- Loop over all the potential matchpoints
856 FOR cur_matchpoint IN
858 FROM config.circ_matrix_matchpoint m
859 /*LEFT*/ JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.grp = upgad.id
860 /*LEFT*/ JOIN actor.org_unit_ancestors_distance( context_ou ) ctoua ON m.org_unit = ctoua.id
861 LEFT JOIN actor.org_unit_ancestors_distance( cn_object.owning_lib ) cnoua ON m.copy_owning_lib = cnoua.id
862 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.copy_circ_lib = iooua.id
863 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
866 -- AND (m.grp IS NULL OR upgad.id IS NOT NULL) -- Optional Permission Group?
868 -- AND (m.org_unit IS NULL OR ctoua.id IS NOT NULL) -- Optional Org Unit?
869 AND (m.copy_owning_lib IS NULL OR cnoua.id IS NOT NULL)
870 AND (m.copy_circ_lib IS NULL OR iooua.id IS NOT NULL)
871 AND (m.user_home_ou IS NULL OR uhoua.id IS NOT NULL)
873 AND (m.is_renewal IS NULL OR m.is_renewal = renewal)
874 -- Static User Checks
875 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
876 AND (m.usr_age_lower_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_lower_bound < user_age))
877 AND (m.usr_age_upper_bound IS NULL OR (user_age IS NOT NULL AND m.usr_age_upper_bound > user_age))
878 -- Static Item Checks
879 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
880 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
881 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
882 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
883 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
884 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
885 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
888 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.grp - (upgad.distance/denominator)) ELSE 0.0 END +
890 CASE WHEN ctoua.distance IS NOT NULL THEN 2^(2*weights.org_unit - (ctoua.distance/denominator)) ELSE 0.0 END +
891 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.copy_owning_lib - (cnoua.distance/denominator)) ELSE 0.0 END +
892 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.copy_circ_lib - (iooua.distance/denominator)) ELSE 0.0 END +
893 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
894 -- Circ Type -- Note: 4^x is equiv to 2^(2*x)
895 CASE WHEN m.is_renewal IS NOT NULL THEN 4^weights.is_renewal ELSE 0.0 END +
896 -- Static User Checks
897 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
898 CASE WHEN m.usr_age_lower_bound IS NOT NULL THEN 4^weights.usr_age_lower_bound ELSE 0.0 END +
899 CASE WHEN m.usr_age_upper_bound IS NOT NULL THEN 4^weights.usr_age_upper_bound ELSE 0.0 END +
900 -- Static Item Checks
901 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
902 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
903 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
904 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
905 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
906 -- Item age has a slight adjustment to weight based on value.
907 -- This should ensure that a shorter age limit comes first when all else is equal.
908 -- NOTE: This assumes that intervals will normally be in days.
909 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 1 + 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
910 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
911 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
914 -- Record the full matching row list
915 row_list := row_list || cur_matchpoint.id;
917 -- No matchpoint yet?
918 IF matchpoint.id IS NULL THEN
919 -- Take the entire matchpoint as a starting point
920 matchpoint := cur_matchpoint;
921 CONTINUE; -- No need to look at this row any more.
924 -- Incomplete matchpoint?
925 IF matchpoint.circulate IS NULL THEN
926 matchpoint.circulate := cur_matchpoint.circulate;
928 IF matchpoint.duration_rule IS NULL THEN
929 matchpoint.duration_rule := cur_matchpoint.duration_rule;
931 IF matchpoint.recurring_fine_rule IS NULL THEN
932 matchpoint.recurring_fine_rule := cur_matchpoint.recurring_fine_rule;
934 IF matchpoint.max_fine_rule IS NULL THEN
935 matchpoint.max_fine_rule := cur_matchpoint.max_fine_rule;
937 IF matchpoint.hard_due_date IS NULL THEN
938 matchpoint.hard_due_date := cur_matchpoint.hard_due_date;
940 IF matchpoint.total_copy_hold_ratio IS NULL THEN
941 matchpoint.total_copy_hold_ratio := cur_matchpoint.total_copy_hold_ratio;
943 IF matchpoint.available_copy_hold_ratio IS NULL THEN
944 matchpoint.available_copy_hold_ratio := cur_matchpoint.available_copy_hold_ratio;
946 IF matchpoint.renewals IS NULL THEN
947 matchpoint.renewals := cur_matchpoint.renewals;
949 IF matchpoint.grace_period IS NULL THEN
950 matchpoint.grace_period := cur_matchpoint.grace_period;
954 -- Check required fields
955 IF matchpoint.circulate IS NOT NULL AND
956 matchpoint.duration_rule IS NOT NULL AND
957 matchpoint.recurring_fine_rule IS NOT NULL AND
958 matchpoint.max_fine_rule IS NOT NULL THEN
959 -- All there? We have a completed match.
960 result.success := true;
963 -- Include the assembled matchpoint, even if it isn't complete
964 result.matchpoint := matchpoint;
966 -- Include (for debugging) the full list of matching rows
967 result.buildrows := row_list;
969 -- Hand the result back to caller
972 $func$ LANGUAGE plpgsql;
974 CREATE OR REPLACE FUNCTION action.find_hold_matrix_matchpoint(pickup_ou integer, request_ou integer, match_item bigint, match_user integer, match_requestor integer)
978 requestor_object actor.usr%ROWTYPE;
979 user_object actor.usr%ROWTYPE;
980 item_object asset.copy%ROWTYPE;
981 item_cn_object asset.call_number%ROWTYPE;
982 my_item_age INTERVAL;
983 rec_descriptor metabib.rec_descriptor%ROWTYPE;
984 matchpoint config.hold_matrix_matchpoint%ROWTYPE;
985 weights config.hold_matrix_weights%ROWTYPE;
986 denominator NUMERIC(6,2);
988 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
989 SELECT INTO requestor_object * FROM actor.usr WHERE id = match_requestor;
990 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
991 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
992 SELECT INTO rec_descriptor * FROM metabib.rec_descriptor WHERE record = item_cn_object.record;
994 SELECT INTO my_item_age age(coalesce(item_object.active_date, now()));
996 -- The item's owner should probably be the one determining if the item is holdable
997 -- How to decide that is debatable. Decided to default to the circ library (where the item lives)
998 -- This flag will allow for setting it to the owning library (where the call number "lives")
999 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.weight_owner_not_circ' AND enabled;
1001 -- Grab the closest set circ weight setting.
1003 -- Default to circ library
1004 SELECT INTO weights hw.*
1005 FROM config.weight_assoc wa
1006 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
1007 JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) d ON (wa.org_unit = d.id)
1012 -- Flag is set, use owning library
1013 SELECT INTO weights hw.*
1014 FROM config.weight_assoc wa
1015 JOIN config.hold_matrix_weights hw ON (hw.id = wa.hold_weights)
1016 JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) d ON (wa.org_unit = d.id)
1022 -- No weights? Bad admin! Defaults to handle that anyway.
1023 IF weights.id IS NULL THEN
1024 weights.user_home_ou := 5.0;
1025 weights.request_ou := 5.0;
1026 weights.pickup_ou := 5.0;
1027 weights.item_owning_ou := 5.0;
1028 weights.item_circ_ou := 5.0;
1029 weights.usr_grp := 7.0;
1030 weights.requestor_grp := 8.0;
1031 weights.circ_modifier := 4.0;
1032 weights.marc_type := 3.0;
1033 weights.marc_form := 2.0;
1034 weights.marc_bib_level := 1.0;
1035 weights.marc_vr_format := 1.0;
1036 weights.juvenile_flag := 4.0;
1037 weights.ref_flag := 0.0;
1038 weights.item_age := 0.0;
1041 -- Determine the max (expected) depth (+1) of the org tree and max depth of the permisson tree
1042 -- If you break your org tree with funky parenting this may be wrong
1043 -- Note: This CTE is duplicated in the find_circ_matrix_matchpoint function, and it may be a good idea to split it off to a function
1044 -- We use one denominator for all tree-based checks for when permission groups and org units have the same weighting
1045 WITH all_distance(distance) AS (
1046 SELECT depth AS distance FROM actor.org_unit_type
1048 SELECT distance AS distance FROM permission.grp_ancestors_distance((SELECT id FROM permission.grp_tree WHERE parent IS NULL))
1050 SELECT INTO denominator MAX(distance) + 1 FROM all_distance;
1052 -- To ATTEMPT to make this work like it used to, make it reverse the user/requestor profile ids.
1053 -- This may be better implemented as part of the upgrade script?
1054 -- Set usr_grp = requestor_grp, requestor_grp = 1 or something when this flag is already set
1055 -- Then remove this flag, of course.
1056 PERFORM * FROM config.internal_flag WHERE name = 'circ.holds.usr_not_requestor' AND enabled;
1059 -- Note: This, to me, is REALLY hacky. I put it in anyway.
1060 -- If you can't tell, this is a single call swap on two variables.
1061 SELECT INTO user_object.profile, requestor_object.profile
1062 requestor_object.profile, user_object.profile;
1065 -- Select the winning matchpoint into the matchpoint variable for returning
1066 SELECT INTO matchpoint m.*
1067 FROM config.hold_matrix_matchpoint m
1068 /*LEFT*/ JOIN permission.grp_ancestors_distance( requestor_object.profile ) rpgad ON m.requestor_grp = rpgad.id
1069 LEFT JOIN permission.grp_ancestors_distance( user_object.profile ) upgad ON m.usr_grp = upgad.id
1070 LEFT JOIN actor.org_unit_ancestors_distance( pickup_ou ) puoua ON m.pickup_ou = puoua.id
1071 LEFT JOIN actor.org_unit_ancestors_distance( request_ou ) rqoua ON m.request_ou = rqoua.id
1072 LEFT JOIN actor.org_unit_ancestors_distance( item_cn_object.owning_lib ) cnoua ON m.item_owning_ou = cnoua.id
1073 LEFT JOIN actor.org_unit_ancestors_distance( item_object.circ_lib ) iooua ON m.item_circ_ou = iooua.id
1074 LEFT JOIN actor.org_unit_ancestors_distance( user_object.home_ou ) uhoua ON m.user_home_ou = uhoua.id
1076 -- Permission Groups
1077 -- AND (m.requestor_grp IS NULL OR upgad.id IS NOT NULL) -- Optional Requestor Group?
1078 AND (m.usr_grp IS NULL OR upgad.id IS NOT NULL)
1080 AND (m.pickup_ou IS NULL OR (puoua.id IS NOT NULL AND (puoua.distance = 0 OR NOT m.strict_ou_match)))
1081 AND (m.request_ou IS NULL OR (rqoua.id IS NOT NULL AND (rqoua.distance = 0 OR NOT m.strict_ou_match)))
1082 AND (m.item_owning_ou IS NULL OR (cnoua.id IS NOT NULL AND (cnoua.distance = 0 OR NOT m.strict_ou_match)))
1083 AND (m.item_circ_ou IS NULL OR (iooua.id IS NOT NULL AND (iooua.distance = 0 OR NOT m.strict_ou_match)))
1084 AND (m.user_home_ou IS NULL OR (uhoua.id IS NOT NULL AND (uhoua.distance = 0 OR NOT m.strict_ou_match)))
1085 -- Static User Checks
1086 AND (m.juvenile_flag IS NULL OR m.juvenile_flag = user_object.juvenile)
1087 -- Static Item Checks
1088 AND (m.circ_modifier IS NULL OR m.circ_modifier = item_object.circ_modifier)
1089 AND (m.marc_type IS NULL OR m.marc_type = COALESCE(item_object.circ_as_type, rec_descriptor.item_type))
1090 AND (m.marc_form IS NULL OR m.marc_form = rec_descriptor.item_form)
1091 AND (m.marc_bib_level IS NULL OR m.marc_bib_level = rec_descriptor.bib_level)
1092 AND (m.marc_vr_format IS NULL OR m.marc_vr_format = rec_descriptor.vr_format)
1093 AND (m.ref_flag IS NULL OR m.ref_flag = item_object.ref)
1094 AND (m.item_age IS NULL OR (my_item_age IS NOT NULL AND m.item_age > my_item_age))
1096 -- Permission Groups
1097 CASE WHEN rpgad.distance IS NOT NULL THEN 2^(2*weights.requestor_grp - (rpgad.distance/denominator)) ELSE 0.0 END +
1098 CASE WHEN upgad.distance IS NOT NULL THEN 2^(2*weights.usr_grp - (upgad.distance/denominator)) ELSE 0.0 END +
1100 CASE WHEN puoua.distance IS NOT NULL THEN 2^(2*weights.pickup_ou - (puoua.distance/denominator)) ELSE 0.0 END +
1101 CASE WHEN rqoua.distance IS NOT NULL THEN 2^(2*weights.request_ou - (rqoua.distance/denominator)) ELSE 0.0 END +
1102 CASE WHEN cnoua.distance IS NOT NULL THEN 2^(2*weights.item_owning_ou - (cnoua.distance/denominator)) ELSE 0.0 END +
1103 CASE WHEN iooua.distance IS NOT NULL THEN 2^(2*weights.item_circ_ou - (iooua.distance/denominator)) ELSE 0.0 END +
1104 CASE WHEN uhoua.distance IS NOT NULL THEN 2^(2*weights.user_home_ou - (uhoua.distance/denominator)) ELSE 0.0 END +
1105 -- Static User Checks -- Note: 4^x is equiv to 2^(2*x)
1106 CASE WHEN m.juvenile_flag IS NOT NULL THEN 4^weights.juvenile_flag ELSE 0.0 END +
1107 -- Static Item Checks
1108 CASE WHEN m.circ_modifier IS NOT NULL THEN 4^weights.circ_modifier ELSE 0.0 END +
1109 CASE WHEN m.marc_type IS NOT NULL THEN 4^weights.marc_type ELSE 0.0 END +
1110 CASE WHEN m.marc_form IS NOT NULL THEN 4^weights.marc_form ELSE 0.0 END +
1111 CASE WHEN m.marc_vr_format IS NOT NULL THEN 4^weights.marc_vr_format ELSE 0.0 END +
1112 CASE WHEN m.ref_flag IS NOT NULL THEN 4^weights.ref_flag ELSE 0.0 END +
1113 -- Item age has a slight adjustment to weight based on value.
1114 -- This should ensure that a shorter age limit comes first when all else is equal.
1115 -- NOTE: This assumes that intervals will normally be in days.
1116 CASE WHEN m.item_age IS NOT NULL THEN 4^weights.item_age - 86400/EXTRACT(EPOCH FROM m.item_age) ELSE 0.0 END DESC,
1117 -- Final sort on id, so that if two rules have the same sorting in the previous sort they have a defined order
1118 -- This prevents "we changed the table order by updating a rule, and we started getting different results"
1121 -- Return just the ID for now
1122 RETURN matchpoint.id;
1124 $func$ LANGUAGE 'plpgsql';
1126 DROP INDEX IF EXISTS config.ccmm_once_per_paramset;
1128 DROP INDEX IF EXISTS config.chmm_once_per_paramset;
1130 CREATE UNIQUE INDEX ccmm_once_per_paramset ON config.circ_matrix_matchpoint (org_unit, grp, COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level,''), COALESCE(marc_vr_format, ''), COALESCE(copy_circ_lib::TEXT, ''), COALESCE(copy_owning_lib::TEXT, ''), COALESCE(user_home_ou::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(is_renewal::TEXT, ''), COALESCE(usr_age_lower_bound::TEXT, ''), COALESCE(usr_age_upper_bound::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
1132 CREATE UNIQUE INDEX chmm_once_per_paramset ON config.hold_matrix_matchpoint (COALESCE(user_home_ou::TEXT, ''), COALESCE(request_ou::TEXT, ''), COALESCE(pickup_ou::TEXT, ''), COALESCE(item_owning_ou::TEXT, ''), COALESCE(item_circ_ou::TEXT, ''), COALESCE(usr_grp::TEXT, ''), COALESCE(requestor_grp::TEXT, ''), COALESCE(circ_modifier, ''), COALESCE(marc_type, ''), COALESCE(marc_form, ''), COALESCE(marc_bib_level, ''), COALESCE(marc_vr_format, ''), COALESCE(juvenile_flag::TEXT, ''), COALESCE(ref_flag::TEXT, ''), COALESCE(item_age::TEXT, '')) WHERE active;
1134 UPDATE config.copy_status SET copy_active = true WHERE id IN (0, 1, 7, 8, 10, 12, 15);
1136 INSERT into config.org_unit_setting_type
1137 ( name, label, description, datatype ) VALUES
1138 ( 'circ.holds.age_protect.active_date', 'Holds: Use Active Date for Age Protection', 'When calculating age protection rules use the active date instead of the creation date.', 'bool');
1140 -- Assume create date when item is in status we would update active date for anyway
1141 UPDATE asset.copy SET active_date = create_date WHERE status IN (SELECT id FROM config.copy_status WHERE copy_active = true);
1143 -- Assume create date for any item with circs
1144 UPDATE asset.copy SET active_date = create_date WHERE id IN (SELECT id FROM extend_reporter.full_circ_count WHERE circ_count > 0);
1146 -- Assume create date for status change time while we are at it. Because being created WAS a change in status.
1147 UPDATE asset.copy SET status_changed_time = create_date WHERE status_changed_time IS NULL;
1149 -- Evergreen DB patch 0564.data.delete_empty_volume.sql
1151 -- New org setting cat.volume.delete_on_empty
1154 -- check whether patch can be applied
1155 SELECT evergreen.upgrade_deps_block_check('0564', :eg_version);
1157 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
1159 'cat.volume.delete_on_empty',
1160 oils_i18n_gettext('cat.volume.delete_on_empty', 'Cat: Delete volume with last copy', 'coust', 'label'),
1161 oils_i18n_gettext('cat.volume.delete_on_empty', 'Automatically delete a volume when the last linked copy is deleted', 'coust', 'description'),
1166 -- Evergreen DB patch 0565.schema.action-trigger.event_definition.hold-cancel-no-target-notification.sql
1168 -- New action trigger event definition: Hold Cancelled (No Target) Email Notification
1171 -- check whether patch can be applied
1172 SELECT evergreen.upgrade_deps_block_check('0565', :eg_version);
1174 INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, delay_field, group_field, template)
1175 VALUES (38, FALSE, 1,
1176 'Hold Cancelled (No Target) Email Notification',
1177 'hold_request.cancel.expire_no_target',
1178 'HoldIsCancelled', 'SendEmail', '30 minutes', 'cancel_time', 'usr',
1181 [%- user = target.0.usr -%]
1182 To: [%- params.recipient_email || user.email %]
1183 From: [%- params.sender_email || default_sender %]
1184 Subject: Hold Request Cancelled
1186 Dear [% user.family_name %], [% user.first_given_name %]
1187 The following holds were cancelled because no items were found to fullfil the hold.
1189 [% FOR hold IN target %]
1190 Title: [% hold.bib_rec.bib_record.simple_record.title %]
1191 Author: [% hold.bib_rec.bib_record.simple_record.author %]
1192 Library: [% hold.pickup_lib.name %]
1193 Request Date: [% date.format(helpers.format_date(hold.rrequest_time), '%Y-%m-%d') %]
1198 INSERT INTO action_trigger.environment (event_def, path) VALUES
1201 (38, 'bib_rec.bib_record.simple_record');
1203 -- Evergreen DB patch XXXX.data.ou_setting_generate_overdue_on_lost.sql.sql
1205 -- check whether patch can be applied
1206 SELECT evergreen.upgrade_deps_block_check('0567', :eg_version);
1208 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
1209 'circ.lost.generate_overdue_on_checkin',
1211 'circ.lost.generate_overdue_on_checkin',
1212 'Circ: Lost Checkin Generates New Overdues',
1217 'circ.lost.generate_overdue_on_checkin',
1218 'Enabling this setting causes retroactive creation of not-yet-existing overdue fines on lost item checkin, up to the point of checkin time (or max fines is reached). This is different than "restore overdue on lost", because it only creates new overdue fines. Use both settings together to get the full complement of overdue fines for a lost item',
1225 -- Evergreen DB patch 0572.vandelay-record-matching-and-quality.sql
1229 -- check whether patch can be applied
1230 SELECT evergreen.upgrade_deps_block_check('0572', :eg_version);
1232 CREATE OR REPLACE FUNCTION evergreen.array_remove_item_by_value(inp ANYARRAY, el ANYELEMENT) RETURNS anyarray AS $$ SELECT ARRAY_ACCUM(x.e) FROM UNNEST( $1 ) x(e) WHERE x.e <> $2; $$ LANGUAGE SQL;
1234 CREATE TABLE vandelay.match_set (
1235 id SERIAL PRIMARY KEY,
1237 owner INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE,
1238 mtype TEXT NOT NULL DEFAULT 'biblio', -- 'biblio','authority','mfhd'?, others?
1239 CONSTRAINT name_once_per_owner_mtype UNIQUE (name, owner, mtype)
1242 -- Table to define match points, either FF via SVF or tag+subfield
1243 CREATE TABLE vandelay.match_set_point (
1244 id SERIAL PRIMARY KEY,
1245 match_set INT REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1246 parent INT REFERENCES vandelay.match_set_point (id),
1247 bool_op TEXT CHECK (bool_op IS NULL OR (bool_op IN ('AND','OR','NOT'))),
1248 svf TEXT REFERENCES config.record_attr_definition (name),
1251 negate BOOL DEFAULT FALSE,
1252 quality INT NOT NULL DEFAULT 1, -- higher is better
1253 CONSTRAINT vmsp_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1254 CONSTRAINT vmsp_need_a_tag_or_a_ff_or_a_bo CHECK (
1255 (tag IS NOT NULL AND svf IS NULL AND bool_op IS NULL) OR
1256 (tag IS NULL AND svf IS NOT NULL AND bool_op IS NULL) OR
1257 (tag IS NULL AND svf IS NULL AND bool_op IS NOT NULL)
1261 CREATE TABLE vandelay.match_set_quality (
1262 id SERIAL PRIMARY KEY,
1263 match_set INT NOT NULL REFERENCES vandelay.match_set (id) ON DELETE CASCADE,
1264 svf TEXT REFERENCES config.record_attr_definition,
1267 value TEXT NOT NULL,
1268 quality INT NOT NULL DEFAULT 1, -- higher is better
1269 CONSTRAINT vmsq_need_a_subfield_with_a_tag CHECK ((tag IS NOT NULL AND subfield IS NOT NULL) OR tag IS NULL),
1270 CONSTRAINT vmsq_need_a_tag_or_a_ff CHECK ((tag IS NOT NULL AND svf IS NULL) OR (tag IS NULL AND svf IS NOT NULL))
1272 CREATE UNIQUE INDEX vmsq_def_once_per_set ON vandelay.match_set_quality (match_set, COALESCE(tag,''), COALESCE(subfield,''), COALESCE(svf,''), value);
1276 ALTER TABLE vandelay.queue ADD COLUMN match_set INT REFERENCES vandelay.match_set (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
1277 ALTER TABLE vandelay.queued_record ADD COLUMN quality INT NOT NULL DEFAULT 0;
1278 ALTER TABLE vandelay.bib_attr_definition DROP COLUMN ident;
1280 CREATE TABLE vandelay.import_error (
1281 code TEXT PRIMARY KEY,
1282 description TEXT NOT NULL -- i18n
1285 ALTER TABLE vandelay.queued_bib_record
1286 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1287 ADD COLUMN error_detail TEXT;
1289 ALTER TABLE vandelay.bib_match
1290 DROP COLUMN field_type,
1291 DROP COLUMN matched_attr,
1292 ADD COLUMN quality INT NOT NULL DEFAULT 1,
1293 ADD COLUMN match_score INT NOT NULL DEFAULT 0;
1295 ALTER TABLE vandelay.import_item
1296 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
1297 ADD COLUMN error_detail TEXT,
1298 ADD COLUMN imported_as BIGINT REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
1299 ADD COLUMN import_time TIMESTAMP WITH TIME ZONE;
1301 ALTER TABLE vandelay.merge_profile ADD COLUMN lwm_ratio NUMERIC;
1303 CREATE OR REPLACE FUNCTION vandelay.marc21_record_type( marc TEXT ) RETURNS config.marc21_rec_type_map AS $func$
1310 retval config.marc21_rec_type_map%ROWTYPE;
1312 ldr := oils_xpath_string( '//*[local-name()="leader"]', marc );
1314 IF ldr IS NULL OR ldr = '' THEN
1315 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1319 SELECT * INTO tval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'Type' LIMIT 1; -- They're all the same
1320 SELECT * INTO bval_rec FROM config.marc21_ff_pos_map WHERE fixed_field = 'BLvl' LIMIT 1; -- They're all the same
1323 tval := SUBSTRING( ldr, tval_rec.start_pos + 1, tval_rec.length );
1324 bval := SUBSTRING( ldr, bval_rec.start_pos + 1, bval_rec.length );
1326 -- RAISE NOTICE 'type %, blvl %, ldr %', tval, bval, ldr;
1328 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE type_val LIKE '%' || tval || '%' AND blvl_val LIKE '%' || bval || '%';
1331 IF retval.code IS NULL THEN
1332 SELECT * INTO retval FROM config.marc21_rec_type_map WHERE code = 'BKS';
1337 $func$ LANGUAGE PLPGSQL;
1339 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_fixed_field( marc TEXT, ff TEXT ) RETURNS TEXT AS $func$
1346 rtype := (vandelay.marc21_record_type( marc )).code;
1347 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE fixed_field = ff AND rec_type = rtype ORDER BY tag DESC LOOP
1348 IF ff_pos.tag = 'ldr' THEN
1349 val := oils_xpath_string('//*[local-name()="leader"]', marc);
1350 IF val IS NOT NULL THEN
1351 val := SUBSTRING( val, ff_pos.start_pos + 1, ff_pos.length );
1355 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1356 val := SUBSTRING( tag_data.value, ff_pos.start_pos + 1, ff_pos.length );
1360 val := REPEAT( ff_pos.default_val, ff_pos.length );
1366 $func$ LANGUAGE PLPGSQL;
1368 CREATE OR REPLACE FUNCTION vandelay.marc21_extract_all_fixed_fields( marc TEXT ) RETURNS SETOF biblio.record_ff_map AS $func$
1373 output biblio.record_ff_map%ROWTYPE;
1375 rtype := (vandelay.marc21_record_type( marc )).code;
1377 FOR ff_pos IN SELECT * FROM config.marc21_ff_pos_map WHERE rec_type = rtype ORDER BY tag DESC LOOP
1378 output.ff_name := ff_pos.fixed_field;
1379 output.ff_value := NULL;
1381 IF ff_pos.tag = 'ldr' THEN
1382 output.ff_value := oils_xpath_string('//*[local-name()="leader"]', marc);
1383 IF output.ff_value IS NOT NULL THEN
1384 output.ff_value := SUBSTRING( output.ff_value, ff_pos.start_pos + 1, ff_pos.length );
1386 output.ff_value := NULL;
1389 FOR tag_data IN SELECT value FROM UNNEST( oils_xpath( '//*[@tag="' || UPPER(ff_pos.tag) || '"]/text()', marc ) ) x(value) LOOP
1390 output.ff_value := SUBSTRING( tag_data, ff_pos.start_pos + 1, ff_pos.length );
1391 IF output.ff_value IS NULL THEN output.ff_value := REPEAT( ff_pos.default_val, ff_pos.length ); END IF;
1393 output.ff_value := NULL;
1401 $func$ LANGUAGE PLPGSQL;
1403 CREATE OR REPLACE FUNCTION vandelay.marc21_physical_characteristics( marc TEXT) RETURNS SETOF biblio.marc21_physical_characteristics AS $func$
1407 ptype config.marc21_physical_characteristic_type_map%ROWTYPE;
1408 psf config.marc21_physical_characteristic_subfield_map%ROWTYPE;
1409 pval config.marc21_physical_characteristic_value_map%ROWTYPE;
1410 retval biblio.marc21_physical_characteristics%ROWTYPE;
1413 _007 := oils_xpath_string( '//*[@tag="007"]', marc );
1415 IF _007 IS NOT NULL AND _007 <> '' THEN
1416 SELECT * INTO ptype FROM config.marc21_physical_characteristic_type_map WHERE ptype_key = SUBSTRING( _007, 1, 1 );
1418 IF ptype.ptype_key IS NOT NULL THEN
1419 FOR psf IN SELECT * FROM config.marc21_physical_characteristic_subfield_map WHERE ptype_key = ptype.ptype_key LOOP
1420 SELECT * INTO pval FROM config.marc21_physical_characteristic_value_map WHERE ptype_subfield = psf.id AND value = SUBSTRING( _007, psf.start_pos + 1, psf.length );
1422 IF pval.id IS NOT NULL THEN
1425 retval.ptype := ptype.ptype_key;
1426 retval.subfield := psf.id;
1427 retval.value := pval.id;
1437 $func$ LANGUAGE PLPGSQL;
1439 CREATE TYPE vandelay.flat_marc AS ( tag CHAR(3), ind1 TEXT, ind2 TEXT, subfield TEXT, value TEXT );
1440 CREATE OR REPLACE FUNCTION vandelay.flay_marc ( TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1443 use MARC::File::XML (BinaryEncoding => 'UTF-8');
1447 MARC::Charset->assume_unicode(1);
1450 my $r = MARC::Record->new_from_xml( $xml );
1452 return_next( { tag => 'LDR', value => $r->leader } );
1454 for my $f ( $r->fields ) {
1455 if ($f->is_control_field) {
1456 return_next({ tag => $f->tag, value => $f->data });
1458 for my $s ($f->subfields) {
1461 ind1 => $f->indicator(1),
1462 ind2 => $f->indicator(2),
1463 subfield => $s->[0],
1467 if ( $f->tag eq '245' and $s->[0] eq 'a' ) {
1468 my $trim = $f->indicator(2) || 0;
1471 ind1 => $f->indicator(1),
1472 ind2 => $f->indicator(2),
1474 value => substr( $s->[1], $trim )
1483 $func$ LANGUAGE PLPERLU;
1485 CREATE OR REPLACE FUNCTION vandelay.flatten_marc ( marc TEXT ) RETURNS SETOF vandelay.flat_marc AS $func$
1487 output vandelay.flat_marc%ROWTYPE;
1490 FOR field IN SELECT * FROM vandelay.flay_marc( marc ) LOOP
1491 output.ind1 := field.ind1;
1492 output.ind2 := field.ind2;
1493 output.tag := field.tag;
1494 output.subfield := field.subfield;
1495 IF field.subfield IS NOT NULL AND field.tag NOT IN ('020','022','024') THEN -- exclude standard numbers and control fields
1496 output.value := naco_normalize(field.value, field.subfield);
1498 output.value := field.value;
1501 CONTINUE WHEN output.value IS NULL;
1506 $func$ LANGUAGE PLPGSQL;
1508 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT, attr_defs TEXT[]) RETURNS hstore AS $_$
1510 transformed_xml TEXT;
1513 xfrm config.xml_transform%ROWTYPE;
1515 new_attrs HSTORE := ''::HSTORE;
1516 attr_def config.record_attr_definition%ROWTYPE;
1519 FOR attr_def IN SELECT * FROM config.record_attr_definition WHERE name IN (SELECT * FROM UNNEST(attr_defs)) ORDER BY format LOOP
1521 IF attr_def.tag IS NOT NULL THEN -- tag (and optional subfield list) selection
1522 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(x.value), COALESCE(attr_def.joiner,' ')) INTO attr_value
1523 FROM vandelay.flatten_marc(xml) AS x
1524 WHERE x.tag LIKE attr_def.tag
1526 WHEN attr_def.sf_list IS NOT NULL
1527 THEN POSITION(x.subfield IN attr_def.sf_list) > 0
1534 ELSIF attr_def.fixed_field IS NOT NULL THEN -- a named fixed field, see config.marc21_ff_pos_map.fixed_field
1535 attr_value := vandelay.marc21_extract_fixed_field(xml, attr_def.fixed_field);
1537 ELSIF attr_def.xpath IS NOT NULL THEN -- and xpath expression
1539 SELECT INTO xfrm * FROM config.xml_transform WHERE name = attr_def.format;
1541 -- See if we can skip the XSLT ... it's expensive
1542 IF prev_xfrm IS NULL OR prev_xfrm <> xfrm.name THEN
1543 -- Can't skip the transform
1544 IF xfrm.xslt <> '---' THEN
1545 transformed_xml := oils_xslt_process(xml,xfrm.xslt);
1547 transformed_xml := xml;
1550 prev_xfrm := xfrm.name;
1553 IF xfrm.name IS NULL THEN
1554 -- just grab the marcxml (empty) transform
1555 SELECT INTO xfrm * FROM config.xml_transform WHERE xslt = '---' LIMIT 1;
1556 prev_xfrm := xfrm.name;
1559 attr_value := oils_xpath_string(attr_def.xpath, transformed_xml, COALESCE(attr_def.joiner,' '), ARRAY[ARRAY[xfrm.prefix, xfrm.namespace_uri]]);
1561 ELSIF attr_def.phys_char_sf IS NOT NULL THEN -- a named Physical Characteristic, see config.marc21_physical_characteristic_*_map
1562 SELECT m.value::TEXT INTO attr_value
1563 FROM vandelay.marc21_physical_characteristics(xml) v
1564 JOIN config.marc21_physical_characteristic_value_map m ON (m.id = v.value)
1565 WHERE v.subfield = attr_def.phys_char_sf
1566 LIMIT 1; -- Just in case ...
1570 -- apply index normalizers to attr_value
1572 SELECT n.func AS func,
1573 n.param_count AS param_count,
1575 FROM config.index_normalizer n
1576 JOIN config.record_attr_index_norm_map m ON (m.norm = n.id)
1577 WHERE attr = attr_def.name
1579 EXECUTE 'SELECT ' || normalizer.func || '(' ||
1580 quote_literal( attr_value ) ||
1582 WHEN normalizer.param_count > 0
1583 THEN ',' || REPLACE(REPLACE(BTRIM(normalizer.params,'[]'),E'\'',E'\\\''),E'"',E'\'')
1586 ')' INTO attr_value;
1590 -- Add the new value to the hstore
1591 new_attrs := new_attrs || hstore( attr_def.name, attr_value );
1597 $_$ LANGUAGE PLPGSQL;
1599 CREATE OR REPLACE FUNCTION vandelay.extract_rec_attrs ( xml TEXT ) RETURNS hstore AS $_$
1600 SELECT vandelay.extract_rec_attrs( $1, (SELECT ARRAY_ACCUM(name) FROM config.record_attr_definition));
1603 -- Everything between this comment and the beginning of the definition of
1604 -- vandelay.match_bib_record() is strictly in service of that function.
1605 CREATE TYPE vandelay.match_set_test_result AS (record BIGINT, quality INTEGER);
1607 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
1608 match_set_id INTEGER, record_xml TEXT
1609 ) RETURNS SETOF vandelay.match_set_test_result AS $$
1620 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
1621 svf_rstore := vandelay.extract_rec_attrs(record_xml);
1623 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
1624 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
1626 -- generate the where clause and return that directly (into wq), and as
1627 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
1628 wq := vandelay.get_expr_from_match_set(match_set_id);
1630 query_ := 'SELECT bre.id AS record, ';
1632 -- qrows table is for the quality bits we add to the SELECT clause
1633 SELECT ARRAY_TO_STRING(
1634 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
1635 ) INTO coal FROM _vandelay_tmp_qrows;
1637 -- our query string so far is the SELECT clause and the inital FROM.
1638 -- no JOINs yet nor the WHERE clause
1639 query_ := query_ || coal || ' AS quality ' || E'\n' ||
1640 'FROM biblio.record_entry bre ';
1642 -- jrows table is for the joins we must make (and the real text conditions)
1643 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
1644 FROM _vandelay_tmp_jrows;
1646 -- add those joins and the where clause to our query.
1647 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
1649 -- this will return rows of record,quality
1650 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
1654 DROP TABLE _vandelay_tmp_qrows;
1655 DROP TABLE _vandelay_tmp_jrows;
1659 $$ LANGUAGE PLPGSQL;
1661 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
1663 ) RETURNS HSTORE AS $$
1667 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
1671 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
1672 FROM vandelay.flatten_marc(record_xml)
1673 GROUP BY tag, subfield ORDER BY tag, subfield
1677 $$ LANGUAGE PLPGSQL;
1679 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set(
1680 match_set_id INTEGER
1681 ) RETURNS TEXT AS $$
1683 root vandelay.match_set_point;
1685 SELECT * INTO root FROM vandelay.match_set_point
1686 WHERE parent IS NULL AND match_set = match_set_id;
1688 RETURN vandelay.get_expr_from_match_set_point(root);
1690 $$ LANGUAGE PLPGSQL;
1692 CREATE OR REPLACE FUNCTION vandelay.get_expr_from_match_set_point(
1693 node vandelay.match_set_point
1694 ) RETURNS TEXT AS $$
1700 child vandelay.match_set_point;
1702 SELECT ARRAY_ACCUM(id) INTO children FROM vandelay.match_set_point
1703 WHERE parent = node.id;
1705 IF ARRAY_LENGTH(children, 1) > 0 THEN
1706 this_op := vandelay._get_expr_render_one(node);
1709 WHILE children[i] IS NOT NULL LOOP
1710 SELECT * INTO child FROM vandelay.match_set_point
1711 WHERE id = children[i];
1713 q := q || ' ' || this_op || ' ';
1716 q := q || vandelay.get_expr_from_match_set_point(child);
1720 ELSIF node.bool_op IS NULL THEN
1721 PERFORM vandelay._get_expr_push_qrow(node);
1722 PERFORM vandelay._get_expr_push_jrow(node);
1723 RETURN vandelay._get_expr_render_one(node);
1728 $$ LANGUAGE PLPGSQL;
1730 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_qrow(
1731 node vandelay.match_set_point
1732 ) RETURNS VOID AS $$
1735 INSERT INTO _vandelay_tmp_qrows (q) VALUES (node.id);
1737 $$ LANGUAGE PLPGSQL;
1739 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
1740 node vandelay.match_set_point
1741 ) RETURNS VOID AS $$
1754 IF node.tag IS NOT NULL THEN
1756 IF node.subfield IS NOT NULL THEN
1757 tagkey := tagkey || node.subfield;
1761 my_alias := 'n' || node.id::TEXT;
1763 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
1764 ' AS quality FROM metabib.';
1765 IF node.tag IS NOT NULL THEN
1766 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
1767 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
1769 IF node.subfield IS NOT NULL THEN
1770 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
1771 node.subfield || '''';
1773 jrow := jrow || ' AND (' || my_alias || '.value ' || op ||
1774 ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
1776 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
1777 my_alias || '.id = bre.id AND (' ||
1778 my_alias || '.attrs->''' || node.svf ||
1779 ''' ' || op || ' $2->''' || node.svf || '''))';
1781 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
1783 $$ LANGUAGE PLPGSQL;
1785 CREATE OR REPLACE FUNCTION vandelay._get_expr_render_one(
1786 node vandelay.match_set_point
1787 ) RETURNS TEXT AS $$
1791 IF node.bool_op IS NOT NULL THEN
1792 RETURN node.bool_op;
1794 RETURN '(n' || node.id::TEXT || '.id IS NOT NULL)';
1797 $$ LANGUAGE PLPGSQL;
1799 CREATE OR REPLACE FUNCTION vandelay.match_bib_record() RETURNS TRIGGER AS $func$
1801 incoming_existing_id TEXT;
1802 test_result vandelay.match_set_test_result%ROWTYPE;
1806 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
1810 DELETE FROM vandelay.bib_match WHERE queued_record = NEW.id;
1812 SELECT q.match_set INTO match_set FROM vandelay.bib_queue q WHERE q.id = NEW.queue;
1814 IF match_set IS NOT NULL THEN
1815 NEW.quality := vandelay.measure_record_quality( NEW.marc, match_set );
1818 -- Perfect matches on 901$c exit early with a match with high quality.
1819 incoming_existing_id :=
1820 oils_xpath_string('//*[@tag="901"]/*[@code="c"][1]', NEW.marc);
1822 IF incoming_existing_id IS NOT NULL AND incoming_existing_id != '' THEN
1823 SELECT id INTO tmp_rec FROM biblio.record_entry WHERE id = incoming_existing_id::bigint;
1824 IF tmp_rec IS NOT NULL THEN
1825 INSERT INTO vandelay.bib_match (queued_record, eg_record, match_score, quality)
1830 -- note: no match_set means quality==0
1831 vandelay.measure_record_quality( b.marc, match_set )
1832 FROM biblio.record_entry b
1833 WHERE id = incoming_existing_id::bigint;
1837 IF match_set IS NULL THEN
1841 FOR test_result IN SELECT * FROM
1842 vandelay.match_set_test_marcxml(match_set, NEW.marc) LOOP
1844 INSERT INTO vandelay.bib_match ( queued_record, eg_record, match_score, quality )
1848 test_result.quality,
1849 vandelay.measure_record_quality( b.marc, match_set )
1850 FROM biblio.record_entry b
1851 WHERE id = test_result.record;
1857 $func$ LANGUAGE PLPGSQL;
1859 CREATE OR REPLACE FUNCTION vandelay.measure_record_quality ( xml TEXT, match_set_id INT ) RETURNS INT AS $_$
1863 test vandelay.match_set_quality%ROWTYPE;
1866 FOR test IN SELECT * FROM vandelay.match_set_quality WHERE match_set = match_set_id LOOP
1867 IF test.tag IS NOT NULL THEN
1868 FOR rvalue IN SELECT value FROM vandelay.flatten_marc( xml ) WHERE tag = test.tag AND subfield = test.subfield LOOP
1869 IF test.value = rvalue THEN
1870 out_q := out_q + test.quality;
1874 IF test.value = vandelay.extract_rec_attrs(xml, ARRAY[test.svf]) -> test.svf THEN
1875 out_q := out_q + test.quality;
1882 $_$ LANGUAGE PLPGSQL;
1885 CREATE OR REPLACE FUNCTION vandelay.overlay_bib_record ( import_id BIGINT, eg_id BIGINT, merge_profile_id INT ) RETURNS BOOL AS $$
1887 merge_profile vandelay.merge_profile%ROWTYPE;
1888 dyn_profile vandelay.compile_profile%ROWTYPE;
1898 SELECT q.marc INTO v_marc
1899 FROM vandelay.queued_record q
1900 JOIN vandelay.bib_match m ON (m.queued_record = q.id AND q.id = import_id)
1903 IF v_marc IS NULL THEN
1904 -- RAISE NOTICE 'no marc for vandelay or bib record';
1908 IF vandelay.template_overlay_bib_record( v_marc, eg_id, merge_profile_id) THEN
1909 UPDATE vandelay.queued_bib_record
1910 SET imported_as = eg_id,
1912 WHERE id = import_id;
1914 editor_string := (oils_xpath('//*[@tag="905"]/*[@code="u"]/text()',v_marc))[1];
1916 IF editor_string IS NOT NULL AND editor_string <> '' THEN
1917 SELECT usr INTO editor_id FROM actor.card WHERE barcode = editor_string;
1919 IF editor_id IS NULL THEN
1920 SELECT id INTO editor_id FROM actor.usr WHERE usrname = editor_string;
1923 IF editor_id IS NOT NULL THEN
1924 UPDATE biblio.record_entry SET editor = editor_id WHERE id = eg_id;
1931 -- RAISE NOTICE 'update of biblio.record_entry failed';
1936 $$ LANGUAGE PLPGSQL;
1939 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1942 lwm_ratio_value NUMERIC;
1945 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1947 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1950 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1954 SELECT m.eg_record INTO eg_id
1955 FROM vandelay.bib_match m
1956 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1957 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1958 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1959 WHERE m.queued_record = import_id
1960 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1961 ORDER BY m.match_score DESC, -- required match score
1962 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1963 m.id -- when in doubt, use the first match
1966 IF eg_id IS NULL THEN
1967 -- RAISE NOTICE 'incoming record is not of high enough quality';
1971 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
1973 $$ LANGUAGE PLPGSQL;
1975 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_record_with_best ( import_id BIGINT, merge_profile_id INT, lwm_ratio_value_p NUMERIC ) RETURNS BOOL AS $$
1978 lwm_ratio_value NUMERIC;
1981 lwm_ratio_value := COALESCE(lwm_ratio_value_p, 0.0);
1983 PERFORM * FROM vandelay.queued_bib_record WHERE import_time IS NOT NULL AND id = import_id;
1986 -- RAISE NOTICE 'already imported, cannot auto-overlay'
1990 SELECT m.eg_record INTO eg_id
1991 FROM vandelay.bib_match m
1992 JOIN vandelay.queued_bib_record qr ON (m.queued_record = qr.id)
1993 JOIN vandelay.bib_queue q ON (qr.queue = q.id)
1994 JOIN biblio.record_entry r ON (r.id = m.eg_record)
1995 WHERE m.queued_record = import_id
1996 AND qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC >= lwm_ratio_value
1997 ORDER BY m.match_score DESC, -- required match score
1998 qr.quality::NUMERIC / COALESCE(NULLIF(m.quality,0),1)::NUMERIC DESC, -- quality tie breaker
1999 m.id -- when in doubt, use the first match
2002 IF eg_id IS NULL THEN
2003 -- RAISE NOTICE 'incoming record is not of high enough quality';
2007 RETURN vandelay.overlay_bib_record( import_id, eg_id, merge_profile_id );
2009 $$ LANGUAGE PLPGSQL;
2012 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( queue_id BIGINT, merge_profile_id INT, lwm_ratio_value NUMERIC ) RETURNS SETOF BIGINT AS $$
2014 queued_record vandelay.queued_bib_record%ROWTYPE;
2017 FOR queued_record IN SELECT * FROM vandelay.queued_bib_record WHERE queue = queue_id AND import_time IS NULL LOOP
2019 IF vandelay.auto_overlay_bib_record_with_best( queued_record.id, merge_profile_id, lwm_ratio_value ) THEN
2020 RETURN NEXT queued_record.id;
2028 $$ LANGUAGE PLPGSQL;
2030 CREATE OR REPLACE FUNCTION vandelay.auto_overlay_bib_queue_with_best ( import_id BIGINT, merge_profile_id INT ) RETURNS SETOF BIGINT AS $$
2031 SELECT vandelay.auto_overlay_bib_queue_with_best( $1, $2, p.lwm_ratio ) FROM vandelay.merge_profile p WHERE id = $2;
2034 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_marc ( ) RETURNS TRIGGER AS $$
2040 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
2044 FOR adef IN SELECT * FROM vandelay.bib_attr_definition LOOP
2046 SELECT extract_marc_field('vandelay.queued_bib_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_bib_record WHERE id = NEW.id;
2047 IF (value IS NOT NULL AND value <> '') THEN
2048 INSERT INTO vandelay.queued_bib_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
2055 $$ LANGUAGE PLPGSQL;
2057 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
2060 item_data vandelay.import_item%ROWTYPE;
2063 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
2067 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
2069 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
2070 INSERT INTO vandelay.import_item (
2094 item_data.definition,
2095 item_data.owning_lib,
2097 item_data.call_number,
2098 item_data.copy_number,
2101 item_data.circulate,
2103 item_data.deposit_amount,
2108 item_data.circ_modifier,
2109 item_data.circ_as_type,
2110 item_data.alert_message,
2112 item_data.priv_note,
2113 item_data.opac_visible
2119 $func$ LANGUAGE PLPGSQL;
2121 CREATE OR REPLACE FUNCTION vandelay.cleanup_bib_marc ( ) RETURNS TRIGGER AS $$
2123 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
2127 DELETE FROM vandelay.queued_bib_record_attr WHERE record = OLD.id;
2128 DELETE FROM vandelay.import_item WHERE record = OLD.id;
2130 IF TG_OP = 'UPDATE' THEN
2135 $$ LANGUAGE PLPGSQL;
2139 DROP TRIGGER zz_match_bibs_trigger ON vandelay.queued_bib_record;
2140 CREATE TRIGGER zz_match_bibs_trigger
2141 BEFORE INSERT OR UPDATE ON vandelay.queued_bib_record
2142 FOR EACH ROW EXECUTE PROCEDURE vandelay.match_bib_record();
2144 CREATE OR REPLACE FUNCTION vandelay.ingest_authority_marc ( ) RETURNS TRIGGER AS $$
2150 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
2154 FOR adef IN SELECT * FROM vandelay.authority_attr_definition LOOP
2156 SELECT extract_marc_field('vandelay.queued_authority_record', id, adef.xpath, adef.remove) INTO value FROM vandelay.queued_authority_record WHERE id = NEW.id;
2157 IF (value IS NOT NULL AND value <> '') THEN
2158 INSERT INTO vandelay.queued_authority_record_attr (record, field, attr_value) VALUES (NEW.id, adef.id, value);
2165 $$ LANGUAGE PLPGSQL;
2167 ALTER TABLE vandelay.authority_attr_definition DROP COLUMN ident;
2168 ALTER TABLE vandelay.queued_authority_record
2169 ADD COLUMN import_error TEXT REFERENCES vandelay.import_error (code) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED,
2170 ADD COLUMN error_detail TEXT;
2172 ALTER TABLE vandelay.authority_match DROP COLUMN matched_attr;
2174 CREATE OR REPLACE FUNCTION vandelay.cleanup_authority_marc ( ) RETURNS TRIGGER AS $$
2176 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
2180 DELETE FROM vandelay.queued_authority_record_attr WHERE record = OLD.id;
2181 IF TG_OP = 'UPDATE' THEN
2186 $$ LANGUAGE PLPGSQL;
2188 CREATE OR REPLACE FUNCTION authority.flatten_marc ( rid BIGINT ) RETURNS SETOF authority.full_rec AS $func$
2190 auth authority.record_entry%ROWTYPE;
2191 output authority.full_rec%ROWTYPE;
2194 SELECT INTO auth * FROM authority.record_entry WHERE id = rid;
2196 FOR field IN SELECT * FROM vandelay.flatten_marc( auth.marc ) LOOP
2197 output.record := rid;
2198 output.ind1 := field.ind1;
2199 output.ind2 := field.ind2;
2200 output.tag := field.tag;
2201 output.subfield := field.subfield;
2202 output.value := field.value;
2207 $func$ LANGUAGE PLPGSQL;
2209 CREATE OR REPLACE FUNCTION biblio.flatten_marc ( rid BIGINT ) RETURNS SETOF metabib.full_rec AS $func$
2211 bib biblio.record_entry%ROWTYPE;
2212 output metabib.full_rec%ROWTYPE;
2215 SELECT INTO bib * FROM biblio.record_entry WHERE id = rid;
2217 FOR field IN SELECT * FROM vandelay.flatten_marc( bib.marc ) LOOP
2218 output.record := rid;
2219 output.ind1 := field.ind1;
2220 output.ind2 := field.ind2;
2221 output.tag := field.tag;
2222 output.subfield := field.subfield;
2223 output.value := field.value;
2228 $func$ LANGUAGE PLPGSQL;
2230 -----------------------------------------------
2231 -- Seed data for import errors
2232 -----------------------------------------------
2234 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'general.unknown', oils_i18n_gettext('general.unknown', 'Import or Overlay failed', 'vie', 'description') );
2235 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.duplicate.barcode', oils_i18n_gettext('import.item.duplicate.barcode', 'Import failed due to barcode collision', 'vie', 'description') );
2236 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.circ_modifier', oils_i18n_gettext('import.item.invalid.circ_modifier', 'Import failed due to invalid circulation modifier', 'vie', 'description') );
2237 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.item.invalid.location', oils_i18n_gettext('import.item.invalid.location', 'Import failed due to invalid copy location', 'vie', 'description') );
2238 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.sysid', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2239 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.duplicate.tcn', oils_i18n_gettext('import.duplicate.sysid', 'Import failed due to system id collision', 'vie', 'description') );
2240 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.missing.sysid', oils_i18n_gettext('overlay.missing.sysid', 'Overlay failed due to missing system id', 'vie', 'description') );
2241 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.auth.duplicate.acn', oils_i18n_gettext('import.auth.duplicate.acn', 'Import failed due to Accession Number collision', 'vie', 'description') );
2242 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'import.xml.malformed', oils_i18n_gettext('import.xml.malformed', 'Malformed record cause Import failure', 'vie', 'description') );
2243 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.xml.malformed', oils_i18n_gettext('overlay.xml.malformed', 'Malformed record cause Overlay failure', 'vie', 'description') );
2244 INSERT INTO vandelay.import_error ( code, description ) VALUES ( 'overlay.record.quality', oils_i18n_gettext('overlay.record.quality', 'New record had insufficient quality', 'vie', 'description') );
2247 ----------------------------------------------------------------
2248 -- Seed data for queued record/item exports
2249 ----------------------------------------------------------------
2251 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
2252 'vandelay.queued_bib_record.print',
2255 'vandelay.queued_bib_record.print',
2256 'Print output has been requested for records in an Importer Bib Queue.',
2263 'vandelay.queued_bib_record.csv',
2266 'vandelay.queued_bib_record.csv',
2267 'CSV output has been requested for records in an Importer Bib Queue.',
2274 'vandelay.queued_bib_record.email',
2277 'vandelay.queued_bib_record.email',
2278 'An email has been requested for records in an Importer Bib Queue.',
2285 'vandelay.queued_auth_record.print',
2288 'vandelay.queued_auth_record.print',
2289 'Print output has been requested for records in an Importer Authority Queue.',
2296 'vandelay.queued_auth_record.csv',
2299 'vandelay.queued_auth_record.csv',
2300 'CSV output has been requested for records in an Importer Authority Queue.',
2307 'vandelay.queued_auth_record.email',
2310 'vandelay.queued_auth_record.email',
2311 'An email has been requested for records in an Importer Authority Queue.',
2318 'vandelay.import_items.print',
2321 'vandelay.import_items.print',
2322 'Print output has been requested for Import Items from records in an Importer Bib Queue.',
2329 'vandelay.import_items.csv',
2332 'vandelay.import_items.csv',
2333 'CSV output has been requested for Import Items from records in an Importer Bib Queue.',
2340 'vandelay.import_items.email',
2343 'vandelay.import_items.email',
2344 'An email has been requested for Import Items from records in an Importer Bib Queue.',
2352 INSERT INTO action_trigger.event_definition (
2367 'Print Output for Queued Bib Records',
2368 'vandelay.queued_bib_record.print',
2376 Queue ID: [% target.0.queue.id %]
2377 Queue Name: [% target.0.queue.name %]
2378 Queue Type: [% target.0.queue.queue_type %]
2379 Complete? [% target.0.queue.complete %]
2381 [% FOR vqbr IN target %]
2383 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2384 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2385 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2386 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2387 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2388 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2389 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2390 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2391 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2392 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2393 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2394 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2395 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2396 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2397 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2405 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2410 INSERT INTO action_trigger.event_definition (
2425 'CSV Output for Queued Bib Records',
2426 'vandelay.queued_bib_record.csv',
2433 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode"
2434 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]"
2440 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2445 INSERT INTO action_trigger.event_definition (
2460 'Email Output for Queued Bib Records',
2461 'vandelay.queued_bib_record.email',
2468 [%- SET user = target.0.queue.owner -%]
2469 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2470 From: [%- params.sender_email || default_sender %]
2471 Subject: Bibs from Import Queue
2473 Queue ID: [% target.0.queue.id %]
2474 Queue Name: [% target.0.queue.name %]
2475 Queue Type: [% target.0.queue.queue_type %]
2476 Complete? [% target.0.queue.complete %]
2478 [% FOR vqbr IN target %]
2480 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
2481 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
2482 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
2483 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
2484 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
2485 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
2486 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
2487 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
2488 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
2489 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
2490 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
2491 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
2492 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
2493 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
2494 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
2502 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2505 ,( 41, 'queue.owner')
2508 INSERT INTO action_trigger.event_definition (
2523 'Print Output for Queued Authority Records',
2524 'vandelay.queued_auth_record.print',
2532 Queue ID: [% target.0.queue.id %]
2533 Queue Name: [% target.0.queue.name %]
2534 Queue Type: [% target.0.queue.queue_type %]
2535 Complete? [% target.0.queue.complete %]
2537 [% FOR vqar IN target %]
2539 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2547 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2552 INSERT INTO action_trigger.event_definition (
2567 'CSV Output for Queued Authority Records',
2568 'vandelay.queued_auth_record.csv',
2576 [% FOR vqar IN target %]"[% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) | replace('"', '""') %]"
2582 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2587 INSERT INTO action_trigger.event_definition (
2602 'Email Output for Queued Authority Records',
2603 'vandelay.queued_auth_record.email',
2610 [%- SET user = target.0.queue.owner -%]
2611 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2612 From: [%- params.sender_email || default_sender %]
2613 Subject: Authorities from Import Queue
2615 Queue ID: [% target.0.queue.id %]
2616 Queue Name: [% target.0.queue.name %]
2617 Queue Type: [% target.0.queue.queue_type %]
2618 Complete? [% target.0.queue.complete %]
2620 [% FOR vqar IN target %]
2622 Record Identifier | [% helpers.get_queued_auth_attr('rec_identifier',vqar.attributes) %]
2630 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2633 ,( 44, 'queue.owner')
2636 INSERT INTO action_trigger.event_definition (
2651 'Print Output for Import Items from Queued Bib Records',
2652 'vandelay.import_items.print',
2655 'record.queue.owner',
2660 Queue ID: [% target.0.record.queue.id %]
2661 Queue Name: [% target.0.record.queue.name %]
2662 Queue Type: [% target.0.record.queue.queue_type %]
2663 Complete? [% target.0.record.queue.complete %]
2665 [% FOR vii IN target %]
2667 Import Item ID | [% vii.id %]
2668 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2669 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2670 Attribute Definition | [% vii.definition %]
2671 Import Error | [% vii.import_error %]
2672 Import Error Detail | [% vii.error_detail %]
2673 Owning Library | [% vii.owning_lib %]
2674 Circulating Library | [% vii.circ_lib %]
2675 Call Number | [% vii.call_number %]
2676 Copy Number | [% vii.copy_number %]
2677 Status | [% vii.status.name %]
2678 Shelving Location | [% vii.location.name %]
2679 Circulate | [% vii.circulate %]
2680 Deposit | [% vii.deposit %]
2681 Deposit Amount | [% vii.deposit_amount %]
2682 Reference | [% vii.ref %]
2683 Holdable | [% vii.holdable %]
2684 Price | [% vii.price %]
2685 Barcode | [% vii.barcode %]
2686 Circulation Modifier | [% vii.circ_modifier %]
2687 Circulate As MARC Type | [% vii.circ_as_type %]
2688 Alert Message | [% vii.alert_message %]
2689 Public Note | [% vii.pub_note %]
2690 Private Note | [% vii.priv_note %]
2691 OPAC Visible | [% vii.opac_visible %]
2699 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2701 ,( 45, 'record.attributes')
2702 ,( 45, 'record.queue')
2703 ,( 45, 'record.queue.owner')
2706 INSERT INTO action_trigger.event_definition (
2721 'CSV Output for Import Items from Queued Bib Records',
2722 'vandelay.import_items.csv',
2725 'record.queue.owner',
2729 "Import Item ID","Title of work","ISBN","Attribute Definition","Import Error","Import Error Detail","Owning Library","Circulating Library","Call Number","Copy Number","Status","Shelving Location","Circulate","Deposit","Deposit Amount","Reference","Holdable","Price","Barcode","Circulation Modifier","Circulate As MARC Type","Alert Message","Public Note","Private Note","OPAC Visible"
2730 [% FOR vii IN target %]"[% vii.id | replace('"', '""') %]","[% helpers.get_queued_bib_attr('title',vii.record.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vii.record.attributes) | replace('"', '""') %]","[% vii.definition | replace('"', '""') %]","[% vii.import_error | replace('"', '""') %]","[% vii.error_detail | replace('"', '""') %]","[% vii.owning_lib | replace('"', '""') %]","[% vii.circ_lib | replace('"', '""') %]","[% vii.call_number | replace('"', '""') %]","[% vii.copy_number | replace('"', '""') %]","[% vii.status.name | replace('"', '""') %]","[% vii.location.name | replace('"', '""') %]","[% vii.circulate | replace('"', '""') %]","[% vii.deposit | replace('"', '""') %]","[% vii.deposit_amount | replace('"', '""') %]","[% vii.ref | replace('"', '""') %]","[% vii.holdable | replace('"', '""') %]","[% vii.price | replace('"', '""') %]","[% vii.barcode | replace('"', '""') %]","[% vii.circ_modifier | replace('"', '""') %]","[% vii.circ_as_type | replace('"', '""') %]","[% vii.alert_message | replace('"', '""') %]","[% vii.pub_note | replace('"', '""') %]","[% vii.priv_note | replace('"', '""') %]","[% vii.opac_visible | replace('"', '""') %]"
2736 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2738 ,( 46, 'record.attributes')
2739 ,( 46, 'record.queue')
2740 ,( 46, 'record.queue.owner')
2743 INSERT INTO action_trigger.event_definition (
2758 'Email Output for Import Items from Queued Bib Records',
2759 'vandelay.import_items.email',
2762 'record.queue.owner',
2766 [%- SET user = target.0.record.queue.owner -%]
2767 To: [%- params.recipient_email || user.email || 'root@localhost' %]
2768 From: [%- params.sender_email || default_sender %]
2769 Subject: Import Items from Import Queue
2771 Queue ID: [% target.0.record.queue.id %]
2772 Queue Name: [% target.0.record.queue.name %]
2773 Queue Type: [% target.0.record.queue.queue_type %]
2774 Complete? [% target.0.record.queue.complete %]
2776 [% FOR vii IN target %]
2778 Import Item ID | [% vii.id %]
2779 Title of work | [% helpers.get_queued_bib_attr('title',vii.record.attributes) %]
2780 ISBN | [% helpers.get_queued_bib_attr('isbn',vii.record.attributes) %]
2781 Attribute Definition | [% vii.definition %]
2782 Import Error | [% vii.import_error %]
2783 Import Error Detail | [% vii.error_detail %]
2784 Owning Library | [% vii.owning_lib %]
2785 Circulating Library | [% vii.circ_lib %]
2786 Call Number | [% vii.call_number %]
2787 Copy Number | [% vii.copy_number %]
2788 Status | [% vii.status.name %]
2789 Shelving Location | [% vii.location.name %]
2790 Circulate | [% vii.circulate %]
2791 Deposit | [% vii.deposit %]
2792 Deposit Amount | [% vii.deposit_amount %]
2793 Reference | [% vii.ref %]
2794 Holdable | [% vii.holdable %]
2795 Price | [% vii.price %]
2796 Barcode | [% vii.barcode %]
2797 Circulation Modifier | [% vii.circ_modifier %]
2798 Circulate As MARC Type | [% vii.circ_as_type %]
2799 Alert Message | [% vii.alert_message %]
2800 Public Note | [% vii.pub_note %]
2801 Private Note | [% vii.priv_note %]
2802 OPAC Visible | [% vii.opac_visible %]
2809 INSERT INTO action_trigger.environment ( event_def, path) VALUES (
2811 ,( 47, 'record.attributes')
2812 ,( 47, 'record.queue')
2813 ,( 47, 'record.queue.owner')
2818 SELECT evergreen.upgrade_deps_block_check('0574', :eg_version);
2820 UPDATE action_trigger.event_definition SET template =
2824 table { border-collapse: collapse; }
2825 td { padding: 5px; border-bottom: 1px solid #888; }
2826 th { font-weight: bold; }
2829 # Sort the holds into copy-location buckets
2830 # In the main print loop, sort each bucket by callnumber before printing
2831 SET holds_list = [];
2833 SET current_location = target.0.current_copy.location.id;
2835 IF current_location != hold.current_copy.location.id;
2836 SET current_location = hold.current_copy.location.id;
2837 holds_list.push(loc_data);
2842 'callnumber' => hold.current_copy.call_number.label
2844 loc_data.push(hold_data);
2846 holds_list.push(loc_data)
2853 <th>Shelving Location</th>
2854 <th>Call Number</th>
2855 <th>Barcode/Part</th>
2860 [% FOR loc_data IN holds_list %]
2861 [% FOR hold_data IN loc_data.sort('callnumber') %]
2863 SET hold = hold_data.hold;
2864 SET copy_data = helpers.get_copy_bib_basics(hold.current_copy.id);
2867 <td>[% copy_data.title | truncate %]</td>
2868 <td>[% copy_data.author | truncate %]</td>
2869 <td>[% hold.current_copy.location.name %]</td>
2870 <td>[% hold.current_copy.call_number.label %]</td>
2871 <td>[% hold.current_copy.barcode %]
2872 [% FOR part IN hold.current_copy.parts %]
2873 [% part.part.label %]
2876 <td>[% hold.usr.card.barcode %]</td>
2885 INSERT INTO action_trigger.environment (
2889 (35, 'current_copy.parts'),
2890 (35, 'current_copy.parts.part')
2894 -- Evergreen DB patch XXXX.schema.authority-control-sets.sql
2896 -- Schema upgrade to add Authority Control Set functionality
2900 -- check whether patch can be applied
2901 SELECT evergreen.upgrade_deps_block_check('0575', :eg_version);
2903 CREATE TABLE authority.control_set (
2904 id SERIAL PRIMARY KEY,
2905 name TEXT NOT NULL UNIQUE, -- i18n
2906 description TEXT -- i18n
2909 CREATE TABLE authority.control_set_authority_field (
2910 id SERIAL PRIMARY KEY,
2911 main_entry INT REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2912 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2913 tag CHAR(3) NOT NULL,
2914 sf_list TEXT NOT NULL,
2915 name TEXT NOT NULL, -- i18n
2916 description TEXT -- i18n
2919 CREATE TABLE authority.control_set_bib_field (
2920 id SERIAL PRIMARY KEY,
2921 authority_field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2922 tag CHAR(3) NOT NULL
2925 CREATE TABLE authority.thesaurus (
2926 code TEXT PRIMARY KEY, -- MARC21 thesaurus code
2927 control_set INT NOT NULL REFERENCES authority.control_set (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2928 name TEXT NOT NULL UNIQUE, -- i18n
2929 description TEXT -- i18n
2932 CREATE TABLE authority.browse_axis (
2933 code TEXT PRIMARY KEY,
2934 name TEXT UNIQUE NOT NULL, -- i18n
2935 sorter TEXT REFERENCES config.record_attr_definition (name) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2939 CREATE TABLE authority.browse_axis_authority_field_map (
2940 id SERIAL PRIMARY KEY,
2941 axis TEXT NOT NULL REFERENCES authority.browse_axis (code) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
2942 field INT NOT NULL REFERENCES authority.control_set_authority_field (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
2945 ALTER TABLE authority.record_entry ADD COLUMN control_set INT REFERENCES authority.control_set (id) ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
2946 ALTER TABLE authority.rec_descriptor DROP COLUMN char_encoding, ADD COLUMN encoding_level TEXT, ADD COLUMN thesaurus TEXT;
2948 CREATE INDEX authority_full_rec_value_index ON authority.full_rec (value);
2949 CREATE OR REPLACE RULE protect_authority_rec_delete AS ON DELETE TO authority.record_entry DO INSTEAD (UPDATE authority.record_entry SET deleted = TRUE WHERE OLD.id = authority.record_entry.id; DELETE FROM authority.full_rec WHERE record = OLD.id);
2951 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
2953 acsaf authority.control_set_authority_field%ROWTYPE;
2961 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
2962 IF thes_code IS NULL THEN
2966 SELECT control_set INTO cset FROM authority.thesaurus WHERE code = thes_code;
2972 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
2973 tag_used := acsaf.tag;
2974 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
2975 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
2976 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
2977 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
2980 EXIT WHEN heading_text <> '';
2983 IF thes_code = 'z' THEN
2984 thes_code := oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml);
2987 IF heading_text <> '' THEN
2988 IF no_thesaurus IS TRUE THEN
2989 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
2991 heading_text := tag_used || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
2994 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
2997 RETURN heading_text;
2999 $func$ LANGUAGE PLPGSQL IMMUTABLE;
3001 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
3002 SELECT authority.normalize_heading($1, TRUE);
3003 $func$ LANGUAGE SQL IMMUTABLE;
3005 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
3006 SELECT authority.normalize_heading($1, FALSE);
3007 $func$ LANGUAGE SQL IMMUTABLE;
3009 CREATE OR REPLACE VIEW authority.tracing_links AS
3010 SELECT main.record AS record,
3012 main.tag AS main_tag,
3013 oils_xpath_string('//*[@tag="'||main.tag||'"]/*[local-name()="subfield"]', are.marc) AS main_value,
3014 substr(link.value,1,1) AS relationship,
3015 substr(link.value,2,1) AS use_restriction,
3016 substr(link.value,3,1) AS deprecation,
3017 substr(link.value,4,1) AS display_restriction,
3019 link.tag AS link_tag,
3020 oils_xpath_string('//*[@tag="'||link.tag||'"]/*[local-name()="subfield"]', are.marc) AS link_value,
3021 authority.normalize_heading(are.marc) AS normalized_main_value
3022 FROM authority.full_rec main
3023 JOIN authority.record_entry are ON (main.record = are.id)
3024 JOIN authority.control_set_authority_field main_entry
3025 ON (main_entry.tag = main.tag
3026 AND main_entry.main_entry IS NULL
3027 AND main.subfield = 'a' )
3028 JOIN authority.control_set_authority_field sub_entry
3029 ON (main_entry.id = sub_entry.main_entry)
3030 JOIN authority.full_rec link
3031 ON (link.record = main.record
3032 AND link.tag = sub_entry.tag
3033 AND link.subfield = 'w' );
3035 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
3038 main_entry authority.control_set_authority_field%ROWTYPE;
3039 bib_field authority.control_set_bib_field%ROWTYPE;
3040 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
3041 replace_data XML[] DEFAULT '{}'::XML[];
3042 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
3045 IF auth_id IS NULL THEN
3049 -- Default to the LoC controll set
3050 SELECT COALESCE(control_set,1) INTO cset FROM authority.record_entry WHERE id = auth_id;
3052 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
3053 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
3054 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
3055 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
3056 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
3057 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
3065 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
3066 XMLELEMENT( name leader, '00881nam a2200193 4500'),
3070 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
3073 XMLATTRIBUTES('r' AS code),
3074 ARRAY_TO_STRING(replace_rules,',')
3079 $f$ STABLE LANGUAGE PLPGSQL;
3081 CREATE OR REPLACE FUNCTION authority.generate_overlay_template ( BIGINT ) RETURNS TEXT AS $func$
3082 SELECT authority.generate_overlay_template( marc ) FROM authority.record_entry WHERE id = $1;
3083 $func$ LANGUAGE SQL;
3085 CREATE OR REPLACE FUNCTION vandelay.add_field ( target_xml TEXT, source_xml TEXT, field TEXT, force_add INT ) RETURNS TEXT AS $_$
3088 use MARC::File::XML (BinaryEncoding => 'UTF-8');
3092 MARC::Charset->assume_unicode(1);
3094 my $target_xml = shift;
3095 my $source_xml = shift;
3096 my $field_spec = shift;
3097 my $force_add = shift || 0;
3099 my $target_r = MARC::Record->new_from_xml( $target_xml );
3100 my $source_r = MARC::Record->new_from_xml( $source_xml );
3102 return $target_xml unless ($target_r && $source_r);
3104 my @field_list = split(',', $field_spec);
3107 for my $f (@field_list) {
3108 $f =~ s/^\s*//; $f =~ s/\s*$//;
3109 if ($f =~ /^(.{3})(\w*)(?:\[([^]]*)\])?$/) {
3115 $match =~ s/^\s*//; $match =~ s/\s*$//;
3116 $fields{$field} = { sf => [ split('', $sf) ] };
3118 my ($msf,$mre) = split('~', $match);
3119 if (length($msf) > 0 and length($mre) > 0) {
3120 $msf =~ s/^\s*//; $msf =~ s/\s*$//;
3121 $mre =~ s/^\s*//; $mre =~ s/\s*$//;
3122 $fields{$field}{match} = { sf => $msf, re => qr/$mre/ };
3128 for my $f ( keys %fields) {
3129 if ( @{$fields{$f}{sf}} ) {
3130 for my $from_field ($source_r->field( $f )) {
3131 my @tos = $target_r->field( $f );
3133 next if (exists($fields{$f}{match}) and !$force_add);
3134 my @new_fields = map { $_->clone } $source_r->field( $f );
3135 $target_r->insert_fields_ordered( @new_fields );
3137 for my $to_field (@tos) {
3138 if (exists($fields{$f}{match})) {
3139 next unless (grep { $_ =~ $fields{$f}{match}{re} } $to_field->subfield($fields{$f}{match}{sf}));
3141 my @new_sf = map { ($_ => $from_field->subfield($_)) } grep { defined($from_field->subfield($_)) } @{$fields{$f}{sf}};
3142 $to_field->add_subfields( @new_sf );
3147 my @new_fields = map { $_->clone } $source_r->field( $f );
3148 $target_r->insert_fields_ordered( @new_fields );
3152 $target_xml = $target_r->as_xml_record;
3153 $target_xml =~ s/^<\?.+?\?>$//mo;
3154 $target_xml =~ s/\n//sgo;
3155 $target_xml =~ s/>\s+</></sgo;
3159 $_$ LANGUAGE PLPERLU;
3162 CREATE INDEX by_heading ON authority.record_entry (authority.simple_normalize_heading(marc)) WHERE deleted IS FALSE or deleted = FALSE;
3164 INSERT INTO config.metabib_field ( id, field_class, name, label, format, xpath, search_field, facet_field) VALUES
3165 (28, 'identifier', 'authority_id', oils_i18n_gettext(28, 'Authority Record ID', 'cmf', 'label'), 'marcxml', '//marc:datafield/marc:subfield[@code="0"]', FALSE, TRUE);
3167 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('AUT','z',' ');
3168 INSERT INTO config.marc21_rec_type_map (code, type_val, blvl_val) VALUES ('MFHD','uvxy',' ');
3170 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('ELvl', 'ldr', 'AUT', 17, 1, ' ');
3171 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('Subj', '008', 'AUT', 11, 1, '|');
3172 INSERT INTO config.marc21_ff_pos_map (fixed_field, tag, rec_type,start_pos, length, default_val) VALUES ('RecStat', 'ldr', 'AUT', 5, 1, 'n');
3174 INSERT INTO config.metabib_field_index_norm_map (field,norm,pos)
3178 FROM config.metabib_field m,
3179 config.index_normalizer i
3180 WHERE i.func = 'remove_paren_substring'
3183 SELECT SETVAL('authority.control_set_id_seq'::TEXT, 100);
3184 SELECT SETVAL('authority.control_set_authority_field_id_seq'::TEXT, 1000);
3185 SELECT SETVAL('authority.control_set_bib_field_id_seq'::TEXT, 1000);
3187 INSERT INTO authority.control_set (id, name, description) VALUES (
3189 oils_i18n_gettext('1','LoC','acs','name'),
3190 oils_i18n_gettext('1','Library of Congress standard authority record control semantics','acs','description')
3193 INSERT INTO authority.control_set_authority_field (id, control_set, main_entry, tag, sf_list, name) VALUES
3196 (1, 1, NULL, '100', 'abcdefklmnopqrstvxyz', oils_i18n_gettext('1','Heading -- Personal Name','acsaf','name')),
3197 (2, 1, NULL, '110', 'abcdefgklmnoprstvxyz', oils_i18n_gettext('2','Heading -- Corporate Name','acsaf','name')),
3198 (3, 1, NULL, '111', 'acdefgklnpqstvxyz', oils_i18n_gettext('3','Heading -- Meeting Name','acsaf','name')),
3199 (4, 1, NULL, '130', 'adfgklmnoprstvxyz', oils_i18n_gettext('4','Heading -- Uniform Title','acsaf','name')),
3200 (5, 1, NULL, '150', 'abvxyz', oils_i18n_gettext('5','Heading -- Topical Term','acsaf','name')),
3201 (6, 1, NULL, '151', 'avxyz', oils_i18n_gettext('6','Heading -- Geographic Name','acsaf','name')),
3202 (7, 1, NULL, '155', 'avxyz', oils_i18n_gettext('7','Heading -- Genre/Form Term','acsaf','name')),
3203 (8, 1, NULL, '180', 'vxyz', oils_i18n_gettext('8','Heading -- General Subdivision','acsaf','name')),
3204 (9, 1, NULL, '181', 'vxyz', oils_i18n_gettext('9','Heading -- Geographic Subdivision','acsaf','name')),
3205 (10, 1, NULL, '182', 'vxyz', oils_i18n_gettext('10','Heading -- Chronological Subdivision','acsaf','name')),
3206 (11, 1, NULL, '185', 'vxyz', oils_i18n_gettext('11','Heading -- Form Subdivision','acsaf','name')),
3207 (12, 1, NULL, '148', 'avxyz', oils_i18n_gettext('12','Heading -- Chronological Term','acsaf','name')),
3209 -- See Also From tracings
3210 (21, 1, 1, '500', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('21','See Also From Tracing -- Personal Name','acsaf','name')),
3211 (22, 1, 2, '510', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('22','See Also From Tracing -- Corporate Name','acsaf','name')),
3212 (23, 1, 3, '511', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('23','See Also From Tracing -- Meeting Name','acsaf','name')),
3213 (24, 1, 4, '530', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('24','See Also From Tracing -- Uniform Title','acsaf','name')),
3214 (25, 1, 5, '550', 'abivwxyz4', oils_i18n_gettext('25','See Also From Tracing -- Topical Term','acsaf','name')),
3215 (26, 1, 6, '551', 'aivwxyz4', oils_i18n_gettext('26','See Also From Tracing -- Geographic Name','acsaf','name')),
3216 (27, 1, 7, '555', 'aivwxyz4', oils_i18n_gettext('27','See Also From Tracing -- Genre/Form Term','acsaf','name')),
3217 (28, 1, 8, '580', 'ivwxyz4', oils_i18n_gettext('28','See Also From Tracing -- General Subdivision','acsaf','name')),
3218 (29, 1, 9, '581', 'ivwxyz4', oils_i18n_gettext('29','See Also From Tracing -- Geographic Subdivision','acsaf','name')),
3219 (30, 1, 10, '582', 'ivwxyz4', oils_i18n_gettext('30','See Also From Tracing -- Chronological Subdivision','acsaf','name')),
3220 (31, 1, 11, '585', 'ivwxyz4', oils_i18n_gettext('31','See Also From Tracing -- Form Subdivision','acsaf','name')),
3221 (32, 1, 12, '548', 'aivwxyz4', oils_i18n_gettext('32','See Also From Tracing -- Chronological Term','acsaf','name')),
3224 (41, 1, 1, '700', 'abcdefghjklmnopqrstvwxyz25', oils_i18n_gettext('41','Established Heading Linking Entry -- Personal Name','acsaf','name')),
3225 (42, 1, 2, '710', 'abcdefghklmnoprstvwxyz25', oils_i18n_gettext('42','Established Heading Linking Entry -- Corporate Name','acsaf','name')),
3226 (43, 1, 3, '711', 'acdefghklnpqstvwxyz25', oils_i18n_gettext('43','Established Heading Linking Entry -- Meeting Name','acsaf','name')),
3227 (44, 1, 4, '730', 'adfghklmnoprstvwxyz25', oils_i18n_gettext('44','Established Heading Linking Entry -- Uniform Title','acsaf','name')),
3228 (45, 1, 5, '750', 'abvwxyz25', oils_i18n_gettext('45','Established Heading Linking Entry -- Topical Term','acsaf','name')),
3229 (46, 1, 6, '751', 'avwxyz25', oils_i18n_gettext('46','Established Heading Linking Entry -- Geographic Name','acsaf','name')),
3230 (47, 1, 7, '755', 'avwxyz25', oils_i18n_gettext('47','Established Heading Linking Entry -- Genre/Form Term','acsaf','name')),
3231 (48, 1, 8, '780', 'vwxyz25', oils_i18n_gettext('48','Subdivision Linking Entry -- General Subdivision','acsaf','name')),
3232 (49, 1, 9, '781', 'vwxyz25', oils_i18n_gettext('49','Subdivision Linking Entry -- Geographic Subdivision','acsaf','name')),
3233 (50, 1, 10, '782', 'vwxyz25', oils_i18n_gettext('50','Subdivision Linking Entry -- Chronological Subdivision','acsaf','name')),
3234 (51, 1, 11, '785', 'vwxyz25', oils_i18n_gettext('51','Subdivision Linking Entry -- Form Subdivision','acsaf','name')),
3235 (52, 1, 12, '748', 'avwxyz25', oils_i18n_gettext('52','Established Heading Linking Entry -- Chronological Term','acsaf','name')),
3237 -- See From tracings
3238 (61, 1, 1, '400', 'abcdefiklmnopqrstvwxyz4', oils_i18n_gettext('61','See Also Tracing -- Personal Name','acsaf','name')),
3239 (62, 1, 2, '410', 'abcdefgiklmnoprstvwxyz4', oils_i18n_gettext('62','See Also Tracing -- Corporate Name','acsaf','name')),
3240 (63, 1, 3, '411', 'acdefgiklnpqstvwxyz4', oils_i18n_gettext('63','See Also Tracing -- Meeting Name','acsaf','name')),
3241 (64, 1, 4, '430', 'adfgiklmnoprstvwxyz4', oils_i18n_gettext('64','See Also Tracing -- Uniform Title','acsaf','name')),
3242 (65, 1, 5, '450', 'abivwxyz4', oils_i18n_gettext('65','See Also Tracing -- Topical Term','acsaf','name')),
3243 (66, 1, 6, '451', 'aivwxyz4', oils_i18n_gettext('66','See Also Tracing -- Geographic Name','acsaf','name')),
3244 (67, 1, 7, '455', 'aivwxyz4', oils_i18n_gettext('67','See Also Tracing -- Genre/Form Term','acsaf','name')),
3245 (68, 1, 8, '480', 'ivwxyz4', oils_i18n_gettext('68','See Also Tracing -- General Subdivision','acsaf','name')),
3246 (69, 1, 9, '481', 'ivwxyz4', oils_i18n_gettext('69','See Also Tracing -- Geographic Subdivision','acsaf','name')),
3247 (70, 1, 10, '482', 'ivwxyz4', oils_i18n_gettext('70','See Also Tracing -- Chronological Subdivision','acsaf','name')),
3248 (71, 1, 11, '485', 'ivwxyz4', oils_i18n_gettext('71','See Also Tracing -- Form Subdivision','acsaf','name')),
3249 (72, 1, 12, '448', 'aivwxyz4', oils_i18n_gettext('72','See Also Tracing -- Chronological Term','acsaf','name'));
3251 INSERT INTO authority.browse_axis (code,name,description,sorter) VALUES
3252 ('title','Title','Title axis','titlesort'),
3253 ('author','Author','Author axis','titlesort'),
3254 ('subject','Subject','Subject axis','titlesort'),
3255 ('topic','Topic','Topic Subject axis','titlesort');
3257 INSERT INTO authority.browse_axis_authority_field_map (axis,field) VALUES
3268 INSERT INTO authority.control_set_bib_field (tag, authority_field)
3269 SELECT '100', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3271 SELECT '600', id FROM authority.control_set_authority_field WHERE tag IN ('100','180','181','182','185')
3273 SELECT '700', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3275 SELECT '800', id FROM authority.control_set_authority_field WHERE tag IN ('100')
3278 SELECT '110', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3280 SELECT '610', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3282 SELECT '710', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3284 SELECT '810', id FROM authority.control_set_authority_field WHERE tag IN ('110')
3287 SELECT '111', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3289 SELECT '611', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3291 SELECT '711', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3293 SELECT '811', id FROM authority.control_set_authority_field WHERE tag IN ('111')
3296 SELECT '130', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3298 SELECT '240', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3300 SELECT '630', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3302 SELECT '730', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3304 SELECT '830', id FROM authority.control_set_authority_field WHERE tag IN ('130')
3307 SELECT '648', id FROM authority.control_set_authority_field WHERE tag IN ('148')
3310 SELECT '650', id FROM authority.control_set_authority_field WHERE tag IN ('150','180','181','182','185')
3312 SELECT '651', id FROM authority.control_set_authority_field WHERE tag IN ('151','180','181','182','185')
3314 SELECT '655', id FROM authority.control_set_authority_field WHERE tag IN ('155','180','181','182','185')
3317 INSERT INTO authority.thesaurus (code, name, control_set) VALUES
3318 ('a', oils_i18n_gettext('a','Library of Congress Subject Headings','at','name'), 1),
3319 ('b', oils_i18n_gettext('b',$$LC subject headings for children's literature$$,'at','name'), 1), -- silly vim '
3320 ('c', oils_i18n_gettext('c','Medical Subject Headings','at','name'), 1),
3321 ('d', oils_i18n_gettext('d','National Agricultural Library subject authority file','at','name'), 1),
3322 ('k', oils_i18n_gettext('k','Canadian Subject Headings','at','name'), 1),
3323 ('n', oils_i18n_gettext('n','Not applicable','at','name'), 1),
3324 ('r', oils_i18n_gettext('r','Art and Architecture Thesaurus','at','name'), 1),
3325 ('s', oils_i18n_gettext('s','Sears List of Subject Headings','at','name'), 1),
3326 ('v', oils_i18n_gettext('v','Repertoire de vedettes-matiere','at','name'), 1),
3327 ('z', oils_i18n_gettext('z','Other','at','name'), 1),
3328 ('|', oils_i18n_gettext('|','No attempt to code','at','name'), 1);
3330 CREATE OR REPLACE FUNCTION authority.map_thesaurus_to_control_set () RETURNS TRIGGER AS $func$
3332 IF NEW.control_set IS NULL THEN
3333 SELECT control_set INTO NEW.control_set
3334 FROM authority.thesaurus
3335 WHERE vandelay.marc21_extract_fixed_field(NEW.marc,'Subj') = code;
3340 $func$ LANGUAGE PLPGSQL;
3342 CREATE TRIGGER map_thesaurus_to_control_set BEFORE INSERT OR UPDATE ON authority.record_entry FOR EACH ROW EXECUTE PROCEDURE authority.map_thesaurus_to_control_set ();
3344 CREATE OR REPLACE FUNCTION authority.reingest_authority_rec_descriptor( auth_id BIGINT ) RETURNS VOID AS $func$
3346 DELETE FROM authority.rec_descriptor WHERE record = auth_id;
3347 INSERT INTO authority.rec_descriptor (record, record_status, encoding_level, thesaurus)
3349 vandelay.marc21_extract_fixed_field(marc,'RecStat'),
3350 vandelay.marc21_extract_fixed_field(marc,'ELvl'),
3351 vandelay.marc21_extract_fixed_field(marc,'Subj')
3352 FROM authority.record_entry
3356 $func$ LANGUAGE PLPGSQL;
3358 CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
3361 IF NEW.deleted IS TRUE THEN -- If this authority is deleted
3362 DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
3363 DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
3364 -- Should remove matching $0 from controlled fields at the same time?
3365 RETURN NEW; -- and we're done
3368 IF TG_OP = 'UPDATE' THEN -- re-ingest?
3369 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
3371 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
3374 -- Propagate these updates to any linked bib records
3375 PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
3378 -- Flatten and insert the afr data
3379 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
3381 PERFORM authority.reingest_authority_full_rec(NEW.id);
3382 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
3384 PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
3390 $func$ LANGUAGE PLPGSQL;
3393 -- Evergreen DB patch 0577.schema.vandelay-item-import-copy-loc-ancestors.sql
3395 -- Ingest items copy location inheritance
3398 -- check whether patch can be applied
3399 SELECT evergreen.upgrade_deps_block_check('0577', :eg_version); -- berick
3401 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3412 deposit_amount TEXT;
3425 tmp_attr_set RECORD;
3426 attr_set vandelay.import_item%ROWTYPE;
3432 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3436 attr_set.definition := attr_def.id;
3438 -- Build the combined XPath
3442 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3443 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3444 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3449 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3450 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3451 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3456 WHEN attr_def.call_number IS NULL THEN 'null()'
3457 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3458 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3463 WHEN attr_def.copy_number IS NULL THEN 'null()'
3464 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3465 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3470 WHEN attr_def.status IS NULL THEN 'null()'
3471 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3472 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3477 WHEN attr_def.location IS NULL THEN 'null()'
3478 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3479 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3484 WHEN attr_def.circulate IS NULL THEN 'null()'
3485 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3486 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3491 WHEN attr_def.deposit IS NULL THEN 'null()'
3492 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3493 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3498 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3499 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3500 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
3505 WHEN attr_def.ref IS NULL THEN 'null()'
3506 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
3507 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
3512 WHEN attr_def.holdable IS NULL THEN 'null()'
3513 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
3514 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
3519 WHEN attr_def.price IS NULL THEN 'null()'
3520 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
3521 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
3526 WHEN attr_def.barcode IS NULL THEN 'null()'
3527 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
3528 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
3533 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
3534 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
3535 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
3540 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
3541 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
3542 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
3547 WHEN attr_def.alert_message IS NULL THEN 'null()'
3548 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
3549 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
3554 WHEN attr_def.opac_visible IS NULL THEN 'null()'
3555 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
3556 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
3561 WHEN attr_def.pub_note IS NULL THEN 'null()'
3562 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
3563 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
3567 WHEN attr_def.priv_note IS NULL THEN 'null()'
3568 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
3569 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
3574 owning_lib || '|' ||
3576 call_number || '|' ||
3577 copy_number || '|' ||
3582 deposit_amount || '|' ||
3587 circ_modifier || '|' ||
3588 circ_as_type || '|' ||
3589 alert_message || '|' ||
3594 -- RAISE NOTICE 'XPath: %', xpath;
3598 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
3599 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
3600 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
3601 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
3604 tmp_attr_set.pr = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
3605 tmp_attr_set.dep_amount = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
3607 tmp_attr_set.pr := NULLIF( tmp_attr_set.pr, '' );
3608 tmp_attr_set.dep_amount := NULLIF( tmp_attr_set.dep_amount, '' );
3610 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
3611 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
3612 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
3615 -- search up the org unit tree for a matching copy location
3617 WITH RECURSIVE anscestor_depth AS (
3621 FROM actor.org_unit ou
3622 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3623 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
3628 FROM actor.org_unit ou
3629 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
3630 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
3631 ) SELECT cpl.id INTO attr_set.location
3632 FROM anscestor_depth a
3633 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
3634 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
3635 ORDER BY a.depth DESC
3638 attr_set.circulate :=
3639 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
3640 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
3643 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
3644 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
3646 attr_set.holdable :=
3647 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
3648 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
3650 attr_set.opac_visible :=
3651 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
3652 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
3655 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
3656 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
3658 attr_set.copy_number := tmp_attr_set.cnum::INT; -- INT,
3659 attr_set.deposit_amount := tmp_attr_set.dep_amount::NUMERIC(6,2); -- NUMERIC(6,2),
3660 attr_set.price := tmp_attr_set.pr::NUMERIC(8,2); -- NUMERIC(8,2),
3662 attr_set.call_number := tmp_attr_set.cn; -- TEXT
3663 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
3664 attr_set.circ_modifier := tmp_attr_set.circ_mod; -- TEXT,
3665 attr_set.circ_as_type := tmp_attr_set.circ_as; -- TEXT,
3666 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3667 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
3668 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
3669 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
3671 RETURN NEXT attr_set;
3680 $$ LANGUAGE PLPGSQL;
3683 -- Evergreen DB patch XXXX.data.org-setting-ui.circ.billing.uncheck_bills_and_unfocus_payment_box.sql
3685 -- New org setting ui.circ.billing.uncheck_bills_and_unfocus_payment_box
3688 -- check whether patch can be applied
3689 SELECT evergreen.upgrade_deps_block_check('0584', :eg_version);
3691 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3693 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3695 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3696 'GUI: Uncheck bills by default in the patron billing interface',
3701 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box',
3702 'Uncheck bills by default in the patron billing interface,'
3703 || ' and focus on the Uncheck All button instead of the'
3704 || ' Payment Received field.',
3712 -- check whether patch can be applied
3713 SELECT evergreen.upgrade_deps_block_check('0585', :eg_version);
3715 INSERT into config.org_unit_setting_type
3716 ( name, label, description, datatype ) VALUES
3717 ( 'circ.checkout_fills_related_hold_exact_match_only',
3718 'Checkout Fills Related Hold On Valid Copy Only',
3719 'When filling related holds on checkout only match on items that are valid for opportunistic capture for the hold. Without this set a Title or Volume hold could match when the item is not holdable. With this set only holdable items will match.',
3723 -- check whether patch can be applied
3724 SELECT evergreen.upgrade_deps_block_check('0586', :eg_version);
3726 INSERT INTO permission.perm_list (id, code, description) VALUES (
3731 'Allows a user to authenticate and get a long-lived session (length configured in opensrf.xml)',
3737 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
3739 pgt.id, perm.id, aout.depth, FALSE
3741 permission.grp_tree pgt,
3742 permission.perm_list perm,
3743 actor.org_unit_type aout
3745 pgt.name = 'Users' AND
3746 aout.name = 'Consortium' AND
3747 perm.code = 'PERSISTENT_LOGIN';
3750 \qecho If this transaction succeeded, your users (staff and patrons) now have
3751 \qecho the PERSISTENT_LOGIN permission by default.
3755 -- Evergreen DB patch XXXX.data.org-setting-circ.offline.skip_foo_if_newer_status_changed_time.sql
3757 -- New org setting circ.offline.skip_checkout_if_newer_status_changed_time
3758 -- New org setting circ.offline.skip_renew_if_newer_status_changed_time
3759 -- New org setting circ.offline.skip_checkin_if_newer_status_changed_time
3762 -- check whether patch can be applied
3763 SELECT evergreen.upgrade_deps_block_check('0593', :eg_version);
3765 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3767 'circ.offline.skip_checkout_if_newer_status_changed_time',
3769 'circ.offline.skip_checkout_if_newer_status_changed_time',
3770 'Offline: Skip offline checkout if newer item Status Changed Time.',
3775 'circ.offline.skip_checkout_if_newer_status_changed_time',
3776 'Skip offline checkout transaction (raise exception when'
3777 || ' processing) if item Status Changed Time is newer than the'
3778 || ' recorded transaction time. WARNING: The Reshelving to'
3779 || ' Available status rollover will trigger this.',
3785 'circ.offline.skip_renew_if_newer_status_changed_time',
3787 'circ.offline.skip_renew_if_newer_status_changed_time',
3788 'Offline: Skip offline renewal if newer item Status Changed Time.',
3793 'circ.offline.skip_renew_if_newer_status_changed_time',
3794 'Skip offline renewal transaction (raise exception when'
3795 || ' processing) if item Status Changed Time is newer than the'
3796 || ' recorded transaction time. WARNING: The Reshelving to'
3797 || ' Available status rollover will trigger this.',
3803 'circ.offline.skip_checkin_if_newer_status_changed_time',
3805 'circ.offline.skip_checkin_if_newer_status_changed_time',
3806 'Offline: Skip offline checkin if newer item Status Changed Time.',
3811 'circ.offline.skip_checkin_if_newer_status_changed_time',
3812 'Skip offline checkin transaction (raise exception when'
3813 || ' processing) if item Status Changed Time is newer than the'
3814 || ' recorded transaction time. WARNING: The Reshelving to'
3815 || ' Available status rollover will trigger this.',
3822 -- Evergreen DB patch YYYY.schema.acp_status_date_changed.sql
3824 -- Change trigger which updates copy status_changed_time to ignore the
3825 -- Reshelving->Available status rollover
3827 -- FIXME: 0039.schema.acp_status_date_changed.sql defines this the first time
3828 -- around, but along with the column itself, etc. And it gets modified with
3829 -- 0562.schema.copy_active_date.sql. Not sure how to use the supercedes /
3830 -- deprecate stuff for upgrade scripts, if it's even applicable when a given
3831 -- upgrade script is doing so much.
3833 -- check whether patch can be applied
3834 SELECT evergreen.upgrade_deps_block_check('0594', :eg_version);
3836 CREATE OR REPLACE FUNCTION asset.acp_status_changed()
3837 RETURNS TRIGGER AS $$
3839 IF NEW.status <> OLD.status AND NOT (NEW.status = 0 AND OLD.status = 7) THEN
3840 NEW.status_changed_time := now();
3841 IF NEW.active_date IS NULL AND NEW.status IN (SELECT id FROM config.copy_status WHERE copy_active = true) THEN
3842 NEW.active_date := now();
3847 $$ LANGUAGE plpgsql;
3849 -- Evergreen DB patch 0595.data.org-setting-ui.patron_search.result_cap.sql
3851 -- New org setting ui.patron_search.result_cap
3854 -- check whether patch can be applied
3855 SELECT evergreen.upgrade_deps_block_check('0595', :eg_version);
3857 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype )
3859 'ui.patron_search.result_cap',
3861 'ui.patron_search.result_cap',
3862 'GUI: Cap results in Patron Search at this number.',
3867 'ui.patron_search.result_cap',
3868 'So for example, if you search for John Doe, normally you would get'
3869 || ' at most 50 results. This setting allows you to raise or lower'
3877 -- Evergreen DB patch 0596.schema.vandelay-item-import-error-detail.sql
3879 -- check whether patch can be applied
3880 SELECT evergreen.upgrade_deps_block_check('0596', :eg_version);
3882 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3883 'import.item.invalid.status', oils_i18n_gettext('import.item.invalid.status', 'Invalid value for "status"', 'vie', 'description') );
3884 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3885 'import.item.invalid.price', oils_i18n_gettext('import.item.invalid.price', 'Invalid value for "price"', 'vie', 'description') );
3886 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3887 'import.item.invalid.deposit_amount', oils_i18n_gettext('import.item.invalid.deposit_amount', 'Invalid value for "deposit_amount"', 'vie', 'description') );
3888 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3889 'import.item.invalid.owning_lib', oils_i18n_gettext('import.item.invalid.owning_lib', 'Invalid value for "owning_lib"', 'vie', 'description') );
3890 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3891 'import.item.invalid.circ_lib', oils_i18n_gettext('import.item.invalid.circ_lib', 'Invalid value for "circ_lib"', 'vie', 'description') );
3892 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3893 'import.item.invalid.copy_number', oils_i18n_gettext('import.item.invalid.copy_number', 'Invalid value for "copy_number"', 'vie', 'description') );
3894 INSERT INTO vandelay.import_error ( code, description ) VALUES (
3895 'import.item.invalid.circ_as_type', oils_i18n_gettext('import.item.invalid.circ_as_type', 'Invalid value for "circ_as_type"', 'vie', 'description') );
3897 CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
3908 deposit_amount TEXT;
3921 tmp_attr_set RECORD;
3922 attr_set vandelay.import_item%ROWTYPE;
3929 SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
3933 attr_set.definition := attr_def.id;
3935 -- Build the combined XPath
3939 WHEN attr_def.owning_lib IS NULL THEN 'null()'
3940 WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
3941 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
3946 WHEN attr_def.circ_lib IS NULL THEN 'null()'
3947 WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
3948 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
3953 WHEN attr_def.call_number IS NULL THEN 'null()'
3954 WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
3955 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
3960 WHEN attr_def.copy_number IS NULL THEN 'null()'
3961 WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
3962 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
3967 WHEN attr_def.status IS NULL THEN 'null()'
3968 WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
3969 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
3974 WHEN attr_def.location IS NULL THEN 'null()'
3975 WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
3976 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
3981 WHEN attr_def.circulate IS NULL THEN 'null()'
3982 WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
3983 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
3988 WHEN attr_def.deposit IS NULL THEN 'null()'
3989 WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
3990 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
3995 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
3996 WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
3997 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
4002 WHEN attr_def.ref IS NULL THEN 'null()'
4003 WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
4004 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
4009 WHEN attr_def.holdable IS NULL THEN 'null()'
4010 WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
4011 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
4016 WHEN attr_def.price IS NULL THEN 'null()'
4017 WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
4018 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
4023 WHEN attr_def.barcode IS NULL THEN 'null()'
4024 WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
4025 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
4030 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
4031 WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
4032 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
4037 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
4038 WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
4039 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
4044 WHEN attr_def.alert_message IS NULL THEN 'null()'
4045 WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
4046 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
4051 WHEN attr_def.opac_visible IS NULL THEN 'null()'
4052 WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
4053 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
4058 WHEN attr_def.pub_note IS NULL THEN 'null()'
4059 WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
4060 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
4064 WHEN attr_def.priv_note IS NULL THEN 'null()'
4065 WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
4066 ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
4071 owning_lib || '|' ||
4073 call_number || '|' ||
4074 copy_number || '|' ||
4079 deposit_amount || '|' ||
4084 circ_modifier || '|' ||
4085 circ_as_type || '|' ||
4086 alert_message || '|' ||
4093 FROM oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
4094 AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
4095 dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
4096 circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, opac_vis TEXT )
4099 attr_set.import_error := NULL;
4100 attr_set.error_detail := NULL;
4101 attr_set.deposit_amount := NULL;
4102 attr_set.copy_number := NULL;
4103 attr_set.price := NULL;
4105 IF tmp_attr_set.pr != '' THEN
4106 tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
4107 IF tmp_str = '' THEN
4108 attr_set.import_error := 'import.item.invalid.price';
4109 attr_set.error_detail := tmp_attr_set.pr; -- original value
4110 RETURN NEXT attr_set; CONTINUE;
4112 attr_set.price := tmp_str::NUMERIC(8,2);
4115 IF tmp_attr_set.dep_amount != '' THEN
4116 tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
4117 IF tmp_str = '' THEN
4118 attr_set.import_error := 'import.item.invalid.deposit_amount';
4119 attr_set.error_detail := tmp_attr_set.dep_amount;
4120 RETURN NEXT attr_set; CONTINUE;
4122 attr_set.deposit_amount := tmp_str::NUMERIC(8,2);
4125 IF tmp_attr_set.cnum != '' THEN
4126 tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
4127 IF tmp_str = '' THEN
4128 attr_set.import_error := 'import.item.invalid.copy_number';
4129 attr_set.error_detail := tmp_attr_set.cnum;
4130 RETURN NEXT attr_set; CONTINUE;
4132 attr_set.copy_number := tmp_str::INT;
4135 IF tmp_attr_set.ol != '' THEN
4136 SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
4138 attr_set.import_error := 'import.item.invalid.owning_lib';
4139 attr_set.error_detail := tmp_attr_set.ol;
4140 RETURN NEXT attr_set; CONTINUE;
4144 IF tmp_attr_set.clib != '' THEN
4145 SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
4147 attr_set.import_error := 'import.item.invalid.circ_lib';
4148 attr_set.error_detail := tmp_attr_set.clib;
4149 RETURN NEXT attr_set; CONTINUE;
4153 IF tmp_attr_set.cs != '' THEN
4154 SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
4156 attr_set.import_error := 'import.item.invalid.status';
4157 attr_set.error_detail := tmp_attr_set.cs;
4158 RETURN NEXT attr_set; CONTINUE;
4162 IF tmp_attr_set.circ_mod != '' THEN
4163 SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
4165 attr_set.import_error := 'import.item.invalid.circ_modifier';
4166 attr_set.error_detail := tmp_attr_set.circ_mod;
4167 RETURN NEXT attr_set; CONTINUE;
4171 IF tmp_attr_set.circ_as != '' THEN
4172 SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
4174 attr_set.import_error := 'import.item.invalid.circ_as_type';
4175 attr_set.error_detail := tmp_attr_set.circ_as;
4176 RETURN NEXT attr_set; CONTINUE;
4180 IF tmp_attr_set.cl != '' THEN
4182 -- search up the org unit tree for a matching copy location
4183 WITH RECURSIVE anscestor_depth AS (
4187 FROM actor.org_unit ou
4188 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
4189 WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
4194 FROM actor.org_unit ou
4195 JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
4196 JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
4197 ) SELECT cpl.id INTO attr_set.location
4198 FROM anscestor_depth a
4199 JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
4200 WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
4201 ORDER BY a.depth DESC
4205 attr_set.import_error := 'import.item.invalid.location';
4206 attr_set.error_detail := tmp_attr_set.cs;
4207 RETURN NEXT attr_set; CONTINUE;
4211 attr_set.circulate :=
4212 LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
4213 OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
4216 LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
4217 OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
4219 attr_set.holdable :=
4220 LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
4221 OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
4223 attr_set.opac_visible :=
4224 LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
4225 OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
4228 LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
4229 OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
4231 attr_set.call_number := tmp_attr_set.cn; -- TEXT
4232 attr_set.barcode := tmp_attr_set.bc; -- TEXT,
4233 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4234 attr_set.pub_note := tmp_attr_set.note; -- TEXT,
4235 attr_set.priv_note := tmp_attr_set.pnote; -- TEXT,
4236 attr_set.alert_message := tmp_attr_set.amessage; -- TEXT,
4238 RETURN NEXT attr_set;
4247 $$ LANGUAGE PLPGSQL;
4249 CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
4252 item_data vandelay.import_item%ROWTYPE;
4255 IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
4259 SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
4261 FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
4262 INSERT INTO vandelay.import_item (
4288 item_data.definition,
4289 item_data.owning_lib,
4291 item_data.call_number,
4292 item_data.copy_number,
4295 item_data.circulate,
4297 item_data.deposit_amount,
4302 item_data.circ_modifier,
4303 item_data.circ_as_type,
4304 item_data.alert_message,
4306 item_data.priv_note,
4307 item_data.opac_visible,
4308 item_data.import_error,
4309 item_data.error_detail
4315 $func$ LANGUAGE PLPGSQL;
4317 -- Evergreen DB patch XXXX.schema.vandelay.bib_match_isxn_caseless.sql
4320 -- check whether patch can be applied
4321 SELECT evergreen.upgrade_deps_block_check('0597', :eg_version);
4323 CREATE INDEX metabib_full_rec_isxn_caseless_idx
4324 ON metabib.real_full_rec (LOWER(value))
4325 WHERE tag IN ('020', '022', '024');
4328 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4330 ) RETURNS HSTORE AS $$
4334 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4340 CASE WHEN tag IN ('020', '022', '024') THEN -- caseless
4341 ARRAY_ACCUM(LOWER(value))::TEXT
4343 ARRAY_ACCUM(value)::TEXT
4345 FROM vandelay.flatten_marc(record_xml)
4346 GROUP BY tag, subfield ORDER BY tag, subfield
4350 $$ LANGUAGE PLPGSQL;
4352 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4353 node vandelay.match_set_point
4354 ) RETURNS VOID AS $$
4362 -- remember $1 is tags_rstore, and $2 is svf_rstore
4372 IF node.tag IS NOT NULL THEN
4373 caseless := (node.tag IN ('020', '022', '024'));
4375 IF node.subfield IS NOT NULL THEN
4376 tagkey := tagkey || node.subfield;
4380 my_alias := 'n' || node.id::TEXT;
4382 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4383 ' AS quality FROM metabib.';
4384 IF node.tag IS NOT NULL THEN
4385 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4386 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4388 IF node.subfield IS NOT NULL THEN
4389 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4390 node.subfield || '''';
4392 jrow := jrow || ' AND (';
4395 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4397 jrow := jrow || my_alias || '.value ' || op;
4400 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4402 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4403 my_alias || '.id = bre.id AND (' ||
4404 my_alias || '.attrs->''' || node.svf ||
4405 ''' ' || op || ' $2->''' || node.svf || '''))';
4407 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4409 $$ LANGUAGE PLPGSQL;
4411 -- Evergreen DB patch 0598.schema.vandelay_one_match_per.sql
4415 -- check whether patch can be applied
4416 SELECT evergreen.upgrade_deps_block_check('0598', :eg_version);
4418 CREATE OR REPLACE FUNCTION vandelay.match_set_test_marcxml(
4419 match_set_id INTEGER, record_xml TEXT
4420 ) RETURNS SETOF vandelay.match_set_test_result AS $$
4431 tags_rstore := vandelay.flatten_marc_hstore(record_xml);
4432 svf_rstore := vandelay.extract_rec_attrs(record_xml);
4434 CREATE TEMPORARY TABLE _vandelay_tmp_qrows (q INTEGER);
4435 CREATE TEMPORARY TABLE _vandelay_tmp_jrows (j TEXT);
4437 -- generate the where clause and return that directly (into wq), and as
4438 -- a side-effect, populate the _vandelay_tmp_[qj]rows tables.
4439 wq := vandelay.get_expr_from_match_set(match_set_id);
4441 query_ := 'SELECT DISTINCT(bre.id) AS record, ';
4443 -- qrows table is for the quality bits we add to the SELECT clause
4444 SELECT ARRAY_TO_STRING(
4445 ARRAY_ACCUM('COALESCE(n' || q::TEXT || '.quality, 0)'), ' + '
4446 ) INTO coal FROM _vandelay_tmp_qrows;
4448 -- our query string so far is the SELECT clause and the inital FROM.
4449 -- no JOINs yet nor the WHERE clause
4450 query_ := query_ || coal || ' AS quality ' || E'\n' ||
4451 'FROM biblio.record_entry bre ';
4453 -- jrows table is for the joins we must make (and the real text conditions)
4454 SELECT ARRAY_TO_STRING(ARRAY_ACCUM(j), E'\n') INTO joins
4455 FROM _vandelay_tmp_jrows;
4457 -- add those joins and the where clause to our query.
4458 query_ := query_ || joins || E'\n' || 'WHERE ' || wq || ' AND not bre.deleted';
4460 -- this will return rows of record,quality
4461 FOR rec IN EXECUTE query_ USING tags_rstore, svf_rstore LOOP
4465 DROP TABLE _vandelay_tmp_qrows;
4466 DROP TABLE _vandelay_tmp_jrows;
4470 $$ LANGUAGE PLPGSQL;
4472 -- Evergreen DB patch 0606.schema.czs_use_perm_column.sql
4474 -- This adds a column to config.z3950_source called use_perm.
4475 -- The idea is that if a permission is set for a given source,
4476 -- then staff will need the referenced permission to use that
4480 -- check whether patch can be applied
4481 SELECT evergreen.upgrade_deps_block_check('0606', :eg_version);
4483 ALTER TABLE config.z3950_source
4484 ADD COLUMN use_perm INT REFERENCES permission.perm_list (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
4486 COMMENT ON COLUMN config.z3950_source.use_perm IS $$
4487 If set, this permission is required for the source to be listed in the staff
4488 client Z39.50 interface. Similar to permission.grp_tree.application_perm.
4491 -- Evergreen DB patch 0608.data.vandelay-export-error-match-info.sql
4496 -- check whether patch can be applied
4497 SELECT evergreen.upgrade_deps_block_check('0608', :eg_version);
4499 -- Add vqbr.import_error, vqbr.error_detail, and vqbr.matches.size to queue print output
4501 UPDATE action_trigger.event_definition SET template = $$
4504 Queue ID: [% target.0.queue.id %]
4505 Queue Name: [% target.0.queue.name %]
4506 Queue Type: [% target.0.queue.queue_type %]
4507 Complete? [% target.0.queue.complete %]
4509 [% FOR vqbr IN target %]
4511 Title of work | [% helpers.get_queued_bib_attr('title',vqbr.attributes) %]
4512 Author of work | [% helpers.get_queued_bib_attr('author',vqbr.attributes) %]
4513 Language of work | [% helpers.get_queued_bib_attr('language',vqbr.attributes) %]
4514 Pagination | [% helpers.get_queued_bib_attr('pagination',vqbr.attributes) %]
4515 ISBN | [% helpers.get_queued_bib_attr('isbn',vqbr.attributes) %]
4516 ISSN | [% helpers.get_queued_bib_attr('issn',vqbr.attributes) %]
4517 Price | [% helpers.get_queued_bib_attr('price',vqbr.attributes) %]
4518 Accession Number | [% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) %]
4519 TCN Value | [% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) %]
4520 TCN Source | [% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) %]
4521 Internal ID | [% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) %]
4522 Publisher | [% helpers.get_queued_bib_attr('publisher',vqbr.attributes) %]
4523 Publication Date | [% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) %]
4524 Edition | [% helpers.get_queued_bib_attr('edition',vqbr.attributes) %]
4525 Item Barcode | [% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) %]
4526 Import Error | [% vqbr.import_error %]
4527 Error Detail | [% vqbr.error_detail %]
4528 Match Count | [% vqbr.matches.size %]
4536 -- Do the same for the CVS version
4538 UPDATE action_trigger.event_definition SET template = $$
4540 "Title of work","Author of work","Language of work","Pagination","ISBN","ISSN","Price","Accession Number","TCN Value","TCN Source","Internal ID","Publisher","Publication Date","Edition","Item Barcode","Import Error","Error Detail","Match Count"
4541 [% FOR vqbr IN target %]"[% helpers.get_queued_bib_attr('title',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('author',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('language',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pagination',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('isbn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('issn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('price',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('rec_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_tcn_source',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('eg_identifier',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('publisher',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('pubdate',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('edition',vqbr.attributes) | replace('"', '""') %]","[% helpers.get_queued_bib_attr('item_barcode',vqbr.attributes) | replace('"', '""') %]","[% vqbr.import_error | replace('"', '""') %]","[% vqbr.error_detail | replace('"', '""') %]","[% vqbr.matches.size %]"
4546 -- Add matches to the env for both
4547 INSERT INTO action_trigger.environment (event_def, path) VALUES (39, 'matches');
4548 INSERT INTO action_trigger.environment (event_def, path) VALUES (40, 'matches');
4551 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4553 -- check whether patch can be applied
4554 SELECT evergreen.upgrade_deps_block_check('0609', :eg_version);
4556 ALTER TABLE acq.lineitem_detail
4557 ADD COLUMN receiver INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED;
4560 -- Evergreen DB patch XXXX.data.acq-copy-creator-from-receiver.sql
4562 -- check whether patch can be applied
4563 SELECT evergreen.upgrade_deps_block_check('0610', :eg_version);
4565 INSERT INTO config.org_unit_setting_type ( name, label, description, datatype ) VALUES (
4566 'acq.copy_creator_uses_receiver',
4568 'acq.copy_creator_uses_receiver',
4569 'Acq: Set copy creator as receiver',
4574 'acq.copy_creator_uses_receiver',
4575 'When receiving a copy in acquisitions, set the copy "creator" to be the staff that received the copy',
4582 -- Evergreen DB patch 0611.data.magic_macros.sql
4584 -- check whether patch can be applied
4585 SELECT evergreen.upgrade_deps_block_check('0611', :eg_version);
4587 INSERT into config.org_unit_setting_type
4588 ( name, label, description, datatype ) VALUES
4590 'circ.staff_client.receipt.header_text',
4592 'circ.staff_client.receipt.header_text',
4593 'Receipt Template: Content of header_text include',
4598 'circ.staff_client.receipt.header_text',
4599 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(header_text)%',
4606 'circ.staff_client.receipt.footer_text',
4608 'circ.staff_client.receipt.footer_text',
4609 'Receipt Template: Content of footer_text include',
4614 'circ.staff_client.receipt.footer_text',
4615 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(footer_text)%',
4622 'circ.staff_client.receipt.notice_text',
4624 'circ.staff_client.receipt.notice_text',
4625 'Receipt Template: Content of notice_text include',
4630 'circ.staff_client.receipt.notice_text',
4631 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(notice_text)%',
4638 'circ.staff_client.receipt.alert_text',
4640 'circ.staff_client.receipt.alert_text',
4641 'Receipt Template: Content of alert_text include',
4646 'circ.staff_client.receipt.alert_text',
4647 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(alert_text)%',
4654 'circ.staff_client.receipt.event_text',
4656 'circ.staff_client.receipt.event_text',
4657 'Receipt Template: Content of event_text include',
4662 'circ.staff_client.receipt.event_text',
4663 'Text/HTML/Macros to be inserted into receipt templates in place of %INCLUDE(event_text)%',
4670 -- Evergreen DB patch 0612.schema.authority_overlay_protection.sql
4674 -- check whether patch can be applied
4675 SELECT evergreen.upgrade_deps_block_check('0612', :eg_version);
4677 -- FIXME: add/check SQL statements to perform the upgrade
4679 -- Function to generate an ephemeral overlay template from an authority record
4680 CREATE OR REPLACE FUNCTION authority.generate_overlay_template (source_xml TEXT) RETURNS TEXT AS $f$
4683 main_entry authority.control_set_authority_field%ROWTYPE;
4684 bib_field authority.control_set_bib_field%ROWTYPE;
4685 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', source_xml)::INT;
4686 replace_data XML[] DEFAULT '{}'::XML[];
4687 replace_rules TEXT[] DEFAULT '{}'::TEXT[];
4690 IF auth_id IS NULL THEN
4694 -- Default to the LoC controll set
4695 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
4697 -- if none, make a best guess
4698 IF cset IS NULL THEN
4699 SELECT control_set INTO cset
4700 FROM authority.control_set_authority_field
4702 SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marc::XML)::TEXT[])
4703 FROM authority.record_entry
4709 -- if STILL none, no-op change
4710 IF cset IS NULL THEN
4713 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4714 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4717 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4720 XMLATTRIBUTES('d' AS code),
4727 FOR main_entry IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
4728 auth_field := XPATH('//*[@tag="'||main_entry.tag||'"][1]',source_xml::XML);
4729 IF ARRAY_LENGTH(auth_field,1) > 0 THEN
4730 FOR bib_field IN SELECT * FROM authority.control_set_bib_field WHERE authority_field = main_entry.id LOOP
4731 replace_data := replace_data || XMLELEMENT( name datafield, XMLATTRIBUTES(bib_field.tag AS tag), XPATH('//*[local-name()="subfield"]',auth_field[1])::XML[]);
4732 replace_rules := replace_rules || ( bib_field.tag || main_entry.sf_list || E'[0~\\)' || auth_id || '$]' );
4740 XMLATTRIBUTES('http://www.loc.gov/MARC21/slim' AS xmlns),
4741 XMLELEMENT( name leader, '00881nam a2200193 4500'),
4745 XMLATTRIBUTES( '905' AS tag, ' ' AS ind1, ' ' AS ind2),
4748 XMLATTRIBUTES('r' AS code),
4749 ARRAY_TO_STRING(replace_rules,',')
4754 $f$ STABLE LANGUAGE PLPGSQL;
4758 -- Evergreen DB patch 0613.schema.vandelay_isxn_normalization.sql
4762 -- check whether patch can be applied
4763 SELECT evergreen.upgrade_deps_block_check('0613', :eg_version);
4765 CREATE OR REPLACE FUNCTION vandelay.flatten_marc_hstore(
4767 ) RETURNS HSTORE AS $func$
4771 ARRAY_ACCUM(tag || (COALESCE(subfield, ''))),
4775 SELECT tag, subfield, ARRAY_ACCUM(value)::TEXT AS value
4778 CASE WHEN tag = '020' THEN -- caseless -- isbn
4779 LOWER((REGEXP_MATCHES(value,$$^(\S{10,17})$$))[1] || '%')
4780 WHEN tag = '022' THEN -- caseless -- issn
4781 LOWER((REGEXP_MATCHES(value,$$^(\S{4}[- ]?\S{4})$$))[1] || '%')
4782 WHEN tag = '024' THEN -- caseless -- upc (other)
4787 FROM vandelay.flatten_marc(record_xml)) x
4788 GROUP BY tag, subfield ORDER BY tag, subfield
4792 $func$ LANGUAGE PLPGSQL;
4794 CREATE OR REPLACE FUNCTION vandelay._get_expr_push_jrow(
4795 node vandelay.match_set_point
4796 ) RETURNS VOID AS $$
4804 -- remember $1 is tags_rstore, and $2 is svf_rstore
4808 IF node.tag IS NOT NULL THEN
4809 caseless := (node.tag IN ('020', '022', '024'));
4811 IF node.subfield IS NOT NULL THEN
4812 tagkey := tagkey || node.subfield;
4830 my_alias := 'n' || node.id::TEXT;
4832 jrow := 'LEFT JOIN (SELECT *, ' || node.quality ||
4833 ' AS quality FROM metabib.';
4834 IF node.tag IS NOT NULL THEN
4835 jrow := jrow || 'full_rec) ' || my_alias || ' ON (' ||
4836 my_alias || '.record = bre.id AND ' || my_alias || '.tag = ''' ||
4838 IF node.subfield IS NOT NULL THEN
4839 jrow := jrow || ' AND ' || my_alias || '.subfield = ''' ||
4840 node.subfield || '''';
4842 jrow := jrow || ' AND (';
4845 jrow := jrow || 'LOWER(' || my_alias || '.value) ' || op;
4847 jrow := jrow || my_alias || '.value ' || op;
4850 jrow := jrow || ' ANY(($1->''' || tagkey || ''')::TEXT[])))';
4852 jrow := jrow || 'record_attr) ' || my_alias || ' ON (' ||
4853 my_alias || '.id = bre.id AND (' ||
4854 my_alias || '.attrs->''' || node.svf ||
4855 ''' ' || op || ' $2->''' || node.svf || '''))';
4857 INSERT INTO _vandelay_tmp_jrows (j) VALUES (jrow);
4859 $$ LANGUAGE PLPGSQL;
4863 -- Evergreen DB patch XXXX.schema.generic-mapping-index-normalizer.sql
4866 -- check whether patch can be applied
4867 SELECT evergreen.upgrade_deps_block_check('0615', :eg_version);
4869 -- evergreen.generic_map_normalizer
4871 CREATE OR REPLACE FUNCTION evergreen.generic_map_normalizer ( TEXT, TEXT ) RETURNS TEXT AS $f$
4875 my $default = $string;
4878 while (/^\s*?(.*?)\s*?=>\s*?(\S+)\s*/) {
4882 $map{$2} = [split(/\s*,\s*/, $1)];
4887 for my $key ( keys %map ) {
4888 return $key if (grep { $_ eq $string } @{ $map{$key} });
4893 $f$ LANGUAGE PLPERLU;
4895 -- evergreen.generic_map_normalizer
4897 INSERT INTO config.index_normalizer (name, description, func, param_count) VALUES (
4898 'Generic Mapping Normalizer',
4899 'Map values or sets of values to new values',
4900 'generic_map_normalizer',
4905 SELECT evergreen.upgrade_deps_block_check('0616', :eg_version);
4907 CREATE OR REPLACE FUNCTION actor.org_unit_prox_update () RETURNS TRIGGER as $$
4911 IF TG_OP = 'DELETE' THEN
4913 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4917 IF TG_OP = 'UPDATE' THEN
4919 IF NEW.parent_ou <> OLD.parent_ou THEN
4921 DELETE FROM actor.org_unit_proximity WHERE (from_org = OLD.id or to_org= OLD.id);
4922 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4923 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4924 FROM actor.org_unit l, actor.org_unit r
4925 WHERE (l.id = NEW.id or r.id = NEW.id);
4931 IF TG_OP = 'INSERT' THEN
4933 INSERT INTO actor.org_unit_proximity (from_org, to_org, prox)
4934 SELECT l.id, r.id, actor.org_unit_proximity(l.id,r.id)
4935 FROM actor.org_unit l, actor.org_unit r
4936 WHERE (l.id = NEW.id or r.id = NEW.id);
4943 $$ LANGUAGE plpgsql;
4946 CREATE TRIGGER proximity_update_tgr AFTER INSERT OR UPDATE OR DELETE ON actor.org_unit FOR EACH ROW EXECUTE PROCEDURE actor.org_unit_prox_update ();
4949 SELECT evergreen.upgrade_deps_block_check('0617', :eg_version);
4951 -- add notify columns to booking.reservation
4952 ALTER TABLE booking.reservation
4953 ADD COLUMN email_notify BOOLEAN NOT NULL DEFAULT FALSE;
4955 -- create the hook and validator
4956 INSERT INTO action_trigger.hook (key, core_type, description, passive)
4957 VALUES ('reservation.available', 'bresv', 'A reservation is available for pickup', false);
4958 INSERT INTO action_trigger.validator (module, description)
4959 VALUES ('ReservationIsAvailable','Checked that a reserved resource is available for checkout');
4961 -- create org unit setting to toggle checkbox display
4962 INSERT INTO config.org_unit_setting_type (name, label, description, datatype)
4963 VALUES ('booking.allow_email_notify', 'booking.allow_email_notify', 'Permit email notification when a reservation is ready for pickup.', 'bool');
4966 SELECT evergreen.upgrade_deps_block_check('0618', :eg_version);
4968 UPDATE config.org_unit_setting_type SET description = E'The Regular Expression for validation on the day_phone field in patron registration. Note: The first capture group will be used for the "last 4 digits of phone number" feature, if enabled. Ex: "[2-9]\\d{2}-\\d{3}-(\\d{4})( x\\d+)?" will ignore the extension on a NANP number.' WHERE name = 'ui.patron.edit.au.day_phone.regex';
4970 UPDATE config.org_unit_setting_type SET description = 'The Regular Expression for validation on phone fields in patron registration. Applies to all phone fields without their own setting. NOTE: See description of the day_phone regex for important information about capture groups with it.' WHERE name = 'ui.patron.edit.phone.regex';
4972 UPDATE config.org_unit_setting_type SET description = oils_i18n_gettext('patron.password.use_phone', 'By default, use the last 4 alphanumeric characters of the patrons phone number as the default password when creating new users. The exact characters used may be configured via the "GUI: Regex for day_phone field on patron registration" setting.', 'coust', 'description') WHERE name = 'patron.password.use_phone';
4974 -- Evergreen DB patch 0619.schema.au_last_update_time.sql
4976 -- check whether patch can be applied
4977 SELECT evergreen.upgrade_deps_block_check('0619', :eg_version);
4979 -- Add new column last_update_time to actor.usr, with trigger to maintain it
4980 -- Add corresponding new column to auditor.actor_usr_history
4982 ALTER TABLE actor.usr
4983 ADD COLUMN last_update_time TIMESTAMPTZ;
4985 ALTER TABLE auditor.actor_usr_history
4986 ADD COLUMN last_update_time TIMESTAMPTZ;
4988 CREATE OR REPLACE FUNCTION actor.au_updated()
4989 RETURNS TRIGGER AS $$
4991 NEW.last_update_time := now();
4994 $$ LANGUAGE plpgsql;
4996 CREATE TRIGGER au_update_trig
4997 BEFORE INSERT OR UPDATE ON actor.usr
4998 FOR EACH ROW EXECUTE PROCEDURE actor.au_updated();
5000 -- Evergreen DB patch XXXX.data.opac_payment_history_age_limit.sql
5003 SELECT evergreen.upgrade_deps_block_check('0621', :eg_version);
5005 INSERT into config.org_unit_setting_type (name, label, description, datatype)
5007 'opac.payment_history_age_limit',
5008 oils_i18n_gettext('opac.payment_history_age_limit',
5009 'OPAC: Payment History Age Limit', 'coust', 'label'),
5010 oils_i18n_gettext('opac.payment_history_age_limit',
5011 'The OPAC should not display payments by patrons that are older than any interval defined here.', 'coust', 'label'),
5015 -- Updates config.org_unit_setting_type to remove the old tag prefixes for once
5016 -- groups have been added.
5019 SELECT evergreen.upgrade_deps_block_check('0622', :eg_version);
5021 INSERT INTO config.settings_group (name, label) VALUES
5022 ('sys', oils_i18n_gettext('config.settings_group.system', 'System', 'coust', 'label')),
5023 ('gui', oils_i18n_gettext('config.settings_group.gui', 'GUI', 'coust', 'label')),
5024 ('lib', oils_i18n_gettext('config.settings_group.lib', 'Library', 'coust', 'label')),
5025 ('sec', oils_i18n_gettext('config.settings_group.sec', 'Security', 'coust', 'label')),
5026 ('cat', oils_i18n_gettext('config.settings_group.cat', 'Cataloging', 'coust', 'label')),
5027 ('holds', oils_i18n_gettext('config.settings_group.holds', 'Holds', 'coust', 'label')),
5028 ('circ', oils_i18n_gettext('config.settings_group.circulation', 'Circulation', 'coust', 'label')),
5029 ('self', oils_i18n_gettext('config.settings_group.self', 'Self Check', 'coust', 'label')),
5030 ('opac', oils_i18n_gettext('config.settings_group.opac', 'OPAC', 'coust', 'label')),
5031 ('prog', oils_i18n_gettext('config.settings_group.program', 'Program', 'coust', 'label')),
5032 ('glob', oils_i18n_gettext('config.settings_group.global', 'Global', 'coust', 'label')),
5033 ('finance', oils_i18n_gettext('config.settings_group.finances', 'Finanaces', 'coust', 'label')),
5034 ('credit', oils_i18n_gettext('config.settings_group.ccp', 'Credit Card Processing', 'coust', 'label')),
5035 ('serial', oils_i18n_gettext('config.settings_group.serial', 'Serials', 'coust', 'label')),
5036 ('recall', oils_i18n_gettext('config.settings_group.recall', 'Recalls', 'coust', 'label')),
5037 ('booking', oils_i18n_gettext('config.settings_group.booking', 'Booking', 'coust', 'label')),
5038 ('offline', oils_i18n_gettext('config.settings_group.offline', 'Offline', 'coust', 'label')),
5039 ('receipt_template', oils_i18n_gettext('config.settings_group.receipt_template', 'Receipt Template', 'coust', 'label'));
5041 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Set copy creator as receiver' WHERE name = 'acq.copy_creator_uses_receiver';
5042 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_circ_modifier';
5043 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.default_copy_location';
5044 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.block';
5045 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'acq.fund.balance_limit.warn';
5046 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.holds.allow_holds_from_purchase_request';
5047 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_barcode_prefix';
5048 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'acq.tmp_callnumber_prefix';
5049 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.opac_timeout';
5050 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.persistent_login_interval';
5051 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'auth.staff_timeout';
5052 UPDATE config.org_unit_setting_type SET grp = 'booking' WHERE name = 'booking.allow_email_notify';
5053 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'cat.bib.alert_on_empty';
5054 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete bib if all copies are deleted via Acquisitions lineitem cancellation.' WHERE name = 'cat.bib.delete_on_no_copy_via_acq_lineitem_cancel';
5055 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'cat.bib.keep_on_empty';
5056 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default Classification Scheme' WHERE name = 'cat.default_classification_scheme';
5057 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (fast add)' WHERE name = 'cat.default_copy_status_fast';
5058 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Default copy status (normal)' WHERE name = 'cat.default_copy_status_normal';
5059 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'cat.default_item_price';
5060 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font family' WHERE name = 'cat.label.font.family';
5061 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font size' WHERE name = 'cat.label.font.size';
5062 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine and pocket label font weight' WHERE name = 'cat.label.font.weight';
5063 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Defines the control number identifier used in 003 and 035 fields.' WHERE name = 'cat.marc_control_number_identifier';
5064 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label maximum lines' WHERE name = 'cat.spine.line.height';
5065 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label left margin' WHERE name = 'cat.spine.line.margin';
5066 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Spine label line width' WHERE name = 'cat.spine.line.width';
5067 UPDATE config.org_unit_setting_type SET grp = 'cat', label='Delete volume with last copy' WHERE name = 'cat.volume.delete_on_empty';
5068 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Toggle off the patron summary sidebar after first view.' WHERE name = 'circ.auto_hide_patron_summary';
5069 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Block Renewal of Items Needed for Holds' WHERE name = 'circ.block_renews_for_holds';
5070 UPDATE config.org_unit_setting_type SET grp = 'booking', label='Elbow room' WHERE name = 'circ.booking_reservation.default_elbow_room';
5071 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_lost_on_zero';
5072 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.charge_on_damaged';
5073 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_auto_renew_age';
5074 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold';
5075 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.checkout_fills_related_hold_exact_match_only';
5076 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_never_checked_out.mark_missing';
5077 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.claim_return.copy_status';
5078 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.damaged.void_ovedue';
5079 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.damaged_item_processing_fee';
5080 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Do not include outstanding Claims Returned circulations in lump sum tallies in Patron Display.' WHERE name = 'circ.do_not_tally_claims_returned';
5081 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard boundary' WHERE name = 'circ.hold_boundary.hard';
5082 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft boundary' WHERE name = 'circ.hold_boundary.soft';
5083 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Alert Interval' WHERE name = 'circ.hold_expire_alert_interval';
5084 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Expire Interval' WHERE name = 'circ.hold_expire_interval';
5085 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.hold_shelf_status_delay';
5086 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Soft stalling interval' WHERE name = 'circ.hold_stalling.soft';
5087 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Hard stalling interval' WHERE name = 'circ.hold_stalling_hard';
5088 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use Active Date for Age Protection' WHERE name = 'circ.holds.age_protect.active_date';
5089 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Behind Desk Pickup Supported' WHERE name = 'circ.holds.behind_desk_pickup_supported';
5090 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display age' WHERE name = 'circ.holds.canceled.display_age';
5091 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Canceled holds display count' WHERE name = 'circ.holds.canceled.display_count';
5092 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Clear shelf copy status' WHERE name = 'circ.holds.clear_shelf.copy_status';
5093 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Bypass hold capture during clear shelf process' WHERE name = 'circ.holds.clear_shelf.no_capture_holds';
5094 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Default Estimated Wait' WHERE name = 'circ.holds.default_estimated_wait_interval';
5095 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.default_shelf_expire_interval';
5096 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Block hold request if hold recipient privileges have expired' WHERE name = 'circ.holds.expired_patron_block';
5097 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Alert' WHERE name = 'circ.holds.hold_has_copy_at.alert';
5098 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Has Local Copy Block' WHERE name = 'circ.holds.hold_has_copy_at.block';
5099 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Maximum library target attempts' WHERE name = 'circ.holds.max_org_unit_target_loops';
5100 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Minimum Estimated Wait' WHERE name = 'circ.holds.min_estimated_wait_interval';
5101 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Org Unit Target Weight' WHERE name = 'circ.holds.org_unit_target_weight';
5102 UPDATE config.org_unit_setting_type SET grp = 'recall', label='An array of fine amount, fine interval, and maximum fine.' WHERE name = 'circ.holds.recall_fine_rules';
5103 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Truncated loan period.' WHERE name = 'circ.holds.recall_return_interval';
5104 UPDATE config.org_unit_setting_type SET grp = 'recall', label='Circulation duration that triggers a recall.' WHERE name = 'circ.holds.recall_threshold';
5105 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Use weight-based hold targeting' WHERE name = 'circ.holds.target_holds_by_org_unit_weight';
5106 UPDATE config.org_unit_setting_type SET grp = 'holds' WHERE name = 'circ.holds.target_skip_me';
5107 UPDATE config.org_unit_setting_type SET grp = 'holds', label='Reset request time on un-cancel' WHERE name = 'circ.holds.uncancel.reset_request_time';
5108 UPDATE config.org_unit_setting_type SET grp = 'holds', label='FIFO' WHERE name = 'circ.holds_fifo';
5109 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'circ.item_checkout_history.max';
5110 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost Checkin Generates New Overdues' WHERE name = 'circ.lost.generate_overdue_on_checkin';
5111 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Lost items usable on checkin' WHERE name = 'circ.lost_immediately_available';
5112 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'circ.lost_materials_processing_fee';
5113 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost max interval' WHERE name = 'circ.max_accept_return_of_lost';
5114 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Cap Max Fine at Item Price' WHERE name = 'circ.max_fine.cap_at_price';
5115 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.max_patron_claim_return_count';
5116 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Item Status for Missing Pieces' WHERE name = 'circ.missing_pieces.copy_status';
5117 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'circ.obscure_dob';
5118 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkin if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkin_if_newer_status_changed_time';
5119 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline checkout if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_checkout_if_newer_status_changed_time';
5120 UPDATE config.org_unit_setting_type SET grp = 'offline', label='Skip offline renewal if newer item Status Changed Time.' WHERE name = 'circ.offline.skip_renew_if_newer_status_changed_time';
5121 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Offline: Patron Usernames Allowed' WHERE name = 'circ.offline.username_allowed';
5122 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests per user' WHERE name = 'circ.password_reset_request_per_user_limit';
5123 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Require matching email address for password reset requests' WHERE name = 'circ.password_reset_request_requires_matching_email';
5124 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Maximum concurrently active self-serve password reset requests' WHERE name = 'circ.password_reset_request_throttle';
5125 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Self-serve password reset request time-to-live' WHERE name = 'circ.password_reset_request_time_to_live';
5126 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Registration: Cloned patrons get address copy' WHERE name = 'circ.patron_edit.clone.copy_address';
5127 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.patron_invalid_address_apply_penalty';
5128 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.pre_cat_copy_circ_lib';
5129 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'circ.reshelving_complete.interval';
5130 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Restore overdues on lost item return' WHERE name = 'circ.restore_overdue_on_lost_return';
5131 UPDATE config.org_unit_setting_type SET grp = 'self', label='Pop-up alert for errors' WHERE name = 'circ.selfcheck.alert.popup';
5132 UPDATE config.org_unit_setting_type SET grp = 'self', label='Audio Alerts' WHERE name = 'circ.selfcheck.alert.sound';
5133 UPDATE config.org_unit_setting_type SET grp = 'self' WHERE name = 'circ.selfcheck.auto_override_checkout_events';
5134 UPDATE config.org_unit_setting_type SET grp = 'self', label='Block copy checkout status' WHERE name = 'circ.selfcheck.block_checkout_on_copy_status';
5135 UPDATE config.org_unit_setting_type SET grp = 'self', label='Patron Login Timeout (in seconds)' WHERE name = 'circ.selfcheck.patron_login_timeout';
5136 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require Patron Password' WHERE name = 'circ.selfcheck.patron_password_required';
5137 UPDATE config.org_unit_setting_type SET grp = 'self', label='Require patron password' WHERE name = 'circ.selfcheck.require_patron_password';
5138 UPDATE config.org_unit_setting_type SET grp = 'self', label='Workstation Required' WHERE name = 'circ.selfcheck.workstation_required';
5139 UPDATE config.org_unit_setting_type SET grp = 'circ' WHERE name = 'circ.staff_client.actor_on_checkout';
5140 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'circ.staff_client.do_not_auto_attempt_print';
5141 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of alert_text include' WHERE name = 'circ.staff_client.receipt.alert_text';
5142 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of event_text include' WHERE name = 'circ.staff_client.receipt.event_text';
5143 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of footer_text include' WHERE name = 'circ.staff_client.receipt.footer_text';
5144 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of header_text include' WHERE name = 'circ.staff_client.receipt.header_text';
5145 UPDATE config.org_unit_setting_type SET grp = 'receipt_template', label='Content of notice_text include' WHERE name = 'circ.staff_client.receipt.notice_text';
5146 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Minimum Transit Checkin Interval' WHERE name = 'circ.transit.min_checkin_interval';
5147 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Deactivate Card' WHERE name = 'circ.user_merge.deactivate_cards';
5148 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Address Delete' WHERE name = 'circ.user_merge.delete_addresses';
5149 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Patron Merge Barcode Delete' WHERE name = 'circ.user_merge.delete_cards';
5150 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void lost item billing when returned' WHERE name = 'circ.void_lost_on_checkin';
5151 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Void processing fee on lost item return' WHERE name = 'circ.void_lost_proc_fee_on_checkin';
5152 UPDATE config.org_unit_setting_type SET grp = 'finance', label='Void overdue fines when items are marked lost' WHERE name = 'circ.void_overdue_on_lost';
5153 UPDATE config.org_unit_setting_type SET grp = 'finance' WHERE name = 'credit.payments.allow';
5154 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable AuthorizeNet payments' WHERE name = 'credit.processor.authorizenet.enabled';
5155 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet login' WHERE name = 'credit.processor.authorizenet.login';
5156 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet password' WHERE name = 'credit.processor.authorizenet.password';
5157 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet server' WHERE name = 'credit.processor.authorizenet.server';
5158 UPDATE config.org_unit_setting_type SET grp = 'credit', label='AuthorizeNet test mode' WHERE name = 'credit.processor.authorizenet.testmode';
5159 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Name default credit processor' WHERE name = 'credit.processor.default';
5160 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayflowPro payments' WHERE name = 'credit.processor.payflowpro.enabled';
5161 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro login/merchant ID' WHERE name = 'credit.processor.payflowpro.login';
5162 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro partner' WHERE name = 'credit.processor.payflowpro.partner';
5163 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro password' WHERE name = 'credit.processor.payflowpro.password';
5164 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro test mode' WHERE name = 'credit.processor.payflowpro.testmode';
5165 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayflowPro vendor' WHERE name = 'credit.processor.payflowpro.vendor';
5166 UPDATE config.org_unit_setting_type SET grp = 'credit', label='Enable PayPal payments' WHERE name = 'credit.processor.paypal.enabled';
5167 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal login' WHERE name = 'credit.processor.paypal.login';
5168 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal password' WHERE name = 'credit.processor.paypal.password';
5169 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal signature' WHERE name = 'credit.processor.paypal.signature';
5170 UPDATE config.org_unit_setting_type SET grp = 'credit', label='PayPal test mode' WHERE name = 'credit.processor.paypal.testmode';
5171 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Dates with this pattern.' WHERE name = 'format.date';
5172 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Format Times with this pattern.' WHERE name = 'format.time';
5173 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.default_locale';
5174 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'global.juvenile_age_threshold';
5175 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'global.password_regex';
5176 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Disable the ability to save list column configurations locally.' WHERE name = 'gui.disable_local_save_columns';
5177 UPDATE config.org_unit_setting_type SET grp = 'lib', label='Courier Code' WHERE name = 'lib.courier_code';
5178 UPDATE config.org_unit_setting_type SET grp = 'lib' WHERE name = 'notice.telephony.callfile_lines';
5179 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Allow pending addresses' WHERE name = 'opac.allow_pending_address';
5180 UPDATE config.org_unit_setting_type SET grp = 'glob' WHERE name = 'opac.barcode_regex';
5181 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Use fully compressed serial holdings' WHERE name = 'opac.fully_compressed_serial_holdings';
5182 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Org Unit Hiding Depth' WHERE name = 'opac.org_unit_hiding.depth';
5183 UPDATE config.org_unit_setting_type SET grp = 'opac', label='Payment History Age Limit' WHERE name = 'opac.payment_history_age_limit';
5184 UPDATE config.org_unit_setting_type SET grp = 'prog' WHERE name = 'org.bounced_emails';
5185 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Boundary' WHERE name = 'org.patron_opt_boundary';
5186 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Patron Opt-In Default' WHERE name = 'org.patron_opt_default';
5187 UPDATE config.org_unit_setting_type SET grp = 'sec' WHERE name = 'patron.password.use_phone';
5188 UPDATE config.org_unit_setting_type SET grp = 'serial', label='Previous Issuance Copy Location' WHERE name = 'serial.prev_issuance_copy_location';
5189 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Patrons Logged' WHERE name = 'ui.admin.patron_log.max_entries';
5190 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Work Log: Maximum Actions Logged' WHERE name = 'ui.admin.work_log.max_entries';
5191 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Horizontal layout for Volume/Copy Creator/Editor.' WHERE name = 'ui.cat.volume_copy_editor.horizontal';
5192 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Uncheck bills by default in the patron billing interface' WHERE name = 'ui.circ.billing.uncheck_bills_and_unfocus_payment_box';
5193 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: Maximum # of uses allowed per entry.' WHERE name = 'ui.circ.in_house_use.entry_cap';
5194 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Record In-House Use: # of uses threshold for Are You Sure? dialog.' WHERE name = 'ui.circ.in_house_use.entry_warn';
5195 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.patron_summary.horizontal';
5196 UPDATE config.org_unit_setting_type SET grp = 'gui' WHERE name = 'ui.circ.show_billing_tab_on_bills';
5197 UPDATE config.org_unit_setting_type SET grp = 'circ', label='Suppress popup-dialogs during check-in.' WHERE name = 'ui.circ.suppress_checkin_popups';
5198 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Button bar' WHERE name = 'ui.general.button_bar';
5199 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Hotkeyset' WHERE name = 'ui.general.hotkeyset';
5200 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Idle timeout' WHERE name = 'ui.general.idle_timeout';
5201 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Country for New Addresses in Patron Editor' WHERE name = 'ui.patron.default_country';
5202 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default Ident Type for Patron Registration' WHERE name = 'ui.patron.default_ident_type';
5203 UPDATE config.org_unit_setting_type SET grp = 'sec', label='Default level of patrons'' internet access' WHERE name = 'ui.patron.default_inet_access_level';
5204 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show active field on patron registration' WHERE name = 'ui.patron.edit.au.active.show';
5205 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest active field on patron registration' WHERE name = 'ui.patron.edit.au.active.suggest';
5206 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.show';
5207 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alert_message field on patron registration' WHERE name = 'ui.patron.edit.au.alert_message.suggest';
5208 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.show';
5209 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest alias field on patron registration' WHERE name = 'ui.patron.edit.au.alias.suggest';
5210 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.show';
5211 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest barred field on patron registration' WHERE name = 'ui.patron.edit.au.barred.suggest';
5212 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.show';
5213 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_never_checked_out_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_never_checked_out_count.suggest';
5214 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.show';
5215 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest claims_returned_count field on patron registration' WHERE name = 'ui.patron.edit.au.claims_returned_count.suggest';
5216 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.example';
5217 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.regex';
5218 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.require';
5219 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.show';
5220 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest day_phone field on patron registration' WHERE name = 'ui.patron.edit.au.day_phone.suggest';
5221 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show calendar widget for dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.calendar';
5222 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.require';
5223 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.show';
5224 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest dob field on patron registration' WHERE name = 'ui.patron.edit.au.dob.suggest';
5225 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.example';
5226 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for email field on patron registration' WHERE name = 'ui.patron.edit.au.email.regex';
5227 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require email field on patron registration' WHERE name = 'ui.patron.edit.au.email.require';
5228 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show email field on patron registration' WHERE name = 'ui.patron.edit.au.email.show';
5229 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest email field on patron registration' WHERE name = 'ui.patron.edit.au.email.suggest';
5230 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.example';
5231 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.regex';
5232 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.require';
5233 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.show';
5234 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest evening_phone field on patron registration' WHERE name = 'ui.patron.edit.au.evening_phone.suggest';
5235 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.show';
5236 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value.suggest';
5237 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.show';
5238 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest ident_value2 field on patron registration' WHERE name = 'ui.patron.edit.au.ident_value2.suggest';
5239 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.show';
5240 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest juvenile field on patron registration' WHERE name = 'ui.patron.edit.au.juvenile.suggest';
5241 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.show';
5242 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest master_account field on patron registration' WHERE name = 'ui.patron.edit.au.master_account.suggest';
5243 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.example';
5244 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.regex';
5245 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.require';
5246 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.show';
5247 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest other_phone field on patron registration' WHERE name = 'ui.patron.edit.au.other_phone.suggest';
5248 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.show';
5249 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest second_given_name field on patron registration' WHERE name = 'ui.patron.edit.au.second_given_name.suggest';
5250 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Show suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.show';
5251 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Suggest suffix field on patron registration' WHERE name = 'ui.patron.edit.au.suffix.suggest';
5252 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require county field on patron registration' WHERE name = 'ui.patron.edit.aua.county.require';
5253 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.example';
5254 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for post_code field on patron registration' WHERE name = 'ui.patron.edit.aua.post_code.regex';
5255 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Default showing suggested patron registration fields' WHERE name = 'ui.patron.edit.default_suggested';
5256 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Example for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.example';
5257 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Regex for phone fields on patron registration' WHERE name = 'ui.patron.edit.phone.regex';
5258 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require at least one address for Patron Registration' WHERE name = 'ui.patron.registration.require_address';
5259 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Cap results in Patron Search at this number.' WHERE name = 'ui.patron_search.result_cap';
5260 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Require staff initials for entry/edit of item/patron/penalty notes/messages.' WHERE name = 'ui.staff.require_initials';
5261 UPDATE config.org_unit_setting_type SET grp = 'gui', label='Unified Volume/Item Creator/Editor' WHERE name = 'ui.unified_volume_copy_editor';
5262 UPDATE config.org_unit_setting_type SET grp = 'gui', label='URL for remote directory containing list column settings.' WHERE name = 'url.remote_column_settings';
5267 SELECT evergreen.upgrade_deps_block_check('0623', :eg_version);
5270 CREATE TABLE config.org_unit_setting_type_log (
5271 id BIGSERIAL PRIMARY KEY,
5272 date_applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
5273 org INT REFERENCES actor.org_unit (id),
5274 original_value TEXT,
5276 field_name TEXT REFERENCES config.org_unit_setting_type (name)
5279 -- Log each change in oust to oustl, so admins can see what they messed up if someting stops working.
5280 CREATE OR REPLACE FUNCTION ous_change_log() RETURNS TRIGGER AS $ous_change_log$
5284 -- Check for which setting is being updated, and log it.
5285 SELECT INTO original value FROM actor.org_unit_setting WHERE name = NEW.name AND org_unit = NEW.org_unit;
5287 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (NEW.org_unit, original, NEW.value, NEW.name);
5291 $ous_change_log$ LANGUAGE plpgsql;
5293 CREATE TRIGGER log_ous_change
5294 BEFORE INSERT OR UPDATE ON actor.org_unit_setting
5295 FOR EACH ROW EXECUTE PROCEDURE ous_change_log();
5297 CREATE OR REPLACE FUNCTION ous_delete_log() RETURNS TRIGGER AS $ous_delete_log$
5301 -- Check for which setting is being updated, and log it.
5302 SELECT INTO original value FROM actor.org_unit_setting WHERE name = OLD.name AND org_unit = OLD.org_unit;
5304 INSERT INTO config.org_unit_setting_type_log (org,original_value,new_value,field_name) VALUES (OLD.org_unit, original, 'null', OLD.name);
5308 $ous_delete_log$ LANGUAGE plpgsql;
5310 CREATE TRIGGER log_ous_del
5311 BEFORE DELETE ON actor.org_unit_setting
5312 FOR EACH ROW EXECUTE PROCEDURE ous_delete_log();
5314 -- Evergreen DB patch 0625.data.opac_staff_saved_search_size.sql
5317 SELECT evergreen.upgrade_deps_block_check('0625', :eg_version);
5319 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype)
5321 'opac.staff_saved_search.size', 'opac',
5322 oils_i18n_gettext('opac.staff_saved_search.size',
5323 'OPAC: Number of staff client saved searches to display on left side of results and record details pages', 'coust', 'label'),
5324 oils_i18n_gettext('opac.staff_saved_search.size',
5325 'If unset, the OPAC (only when wrapped in the staff client!) will default to showing you your ten most recent searches on the left side of the results and record details pages. If you actually don''t want to see this feature at all, set this value to zero at the top of your organizational tree.', 'coust', 'description'),
5329 -- Evergreen DB patch 0626.schema.bookbag-goodies.sql
5332 SELECT evergreen.upgrade_deps_block_check('0626', :eg_version);
5334 ALTER TABLE container.biblio_record_entry_bucket
5335 ADD COLUMN description TEXT;
5337 ALTER TABLE container.call_number_bucket
5338 ADD COLUMN description TEXT;
5340 ALTER TABLE container.copy_bucket
5341 ADD COLUMN description TEXT;
5343 ALTER TABLE container.user_bucket
5344 ADD COLUMN description TEXT;
5346 INSERT INTO action_trigger.hook (key, core_type, description, passive)
5348 'container.biblio_record_entry_bucket.csv',
5351 'container.biblio_record_entry_bucket.csv',
5352 'Produce a CSV file representing a bookbag',
5359 INSERT INTO action_trigger.reactor (module, description)
5364 'Facilitates produce a CSV file representing a bookbag by introducing an "items" variable into the TT environment, sorted as dictated according to user params',
5370 INSERT INTO action_trigger.event_definition (
5372 name, hook, reactor,
5376 'Bookbag CSV', 'container.biblio_record_entry_bucket.csv', 'ContainerCSV',
5380 # target is the bookbag itself. The 'items' variable does not need to be in
5381 # the environment because a special reactor will take care of filling it in.
5384 bibxml = helpers.xml_doc(item.target_biblio_record_entry.marc);
5386 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
5387 title = title _ part.textContent;
5389 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
5391 helpers.csv_datum(title) %],[% helpers.csv_datum(author) %],[% FOR note IN item.notes; helpers.csv_datum(note.note); ","; END; "\n";
5396 -- Evergreen DB patch 0627.data.patron-password-reset-msg.sql
5398 -- Updates password reset template to match TPAC reset form
5401 -- check whether patch can be applied
5402 SELECT evergreen.upgrade_deps_block_check('0627', :eg_version);
5404 UPDATE action_trigger.event_definition SET template =
5407 [%- user = target.usr -%]
5408 To: [%- params.recipient_email || user.email %]
5409 From: [%- params.sender_email || user.home_ou.email || default_sender %]
5410 Subject: [% user.home_ou.name %]: library account password reset request
5412 You have received this message because you, or somebody else, requested a reset
5413 of your library system password. If you did not request a reset of your library
5414 system password, just ignore this message and your current password will
5417 If you did request a reset of your library system password, please perform
5418 the following steps to continue the process of resetting your password:
5420 1. Open the following link in a web browser: https://[% params.hostname %]/eg/opac/password_reset/[% target.uuid %]
5421 The browser displays a password reset form.
5423 2. Enter your new password in the password reset form in the browser. You must
5424 enter the password twice to ensure that you do not make a mistake. If the
5425 passwords match, you will then be able to log in to your library system account
5426 with the new password.
5429 WHERE id = 20; -- Password reset request notification
5432 SELECT evergreen.upgrade_deps_block_check('0630', :eg_version);
5434 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5435 ( 'circ.transit.suppress_hold', 'circ',
5436 oils_i18n_gettext('circ.transit.suppress_hold',
5437 'Suppress Hold Transits Group',
5439 oils_i18n_gettext('circ.transit.suppress_hold',
5440 'If set to a non-empty value, Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5441 'coust', 'description'),
5443 ,( 'circ.transit.suppress_non_hold', 'circ',
5444 oils_i18n_gettext('circ.transit.suppress_non_hold',
5445 'Suppress Non-Hold Transits Group',
5447 oils_i18n_gettext('circ.transit.suppress_non_hold',
5448 'If set to a non-empty value, Non-Hold Transits will be suppressed between this OU and others with the same value. If set to an empty value, transits will not be suppressed.',
5449 'coust', 'description'),
5453 -- check whether patch can be applied
5454 SELECT evergreen.upgrade_deps_block_check('0632', :eg_version);
5456 INSERT INTO config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
5457 ( 'opac.username_regex', 'glob',
5458 oils_i18n_gettext('opac.username_regex',
5459 'Patron username format',
5461 oils_i18n_gettext('opac.username_regex',
5462 'Regular expression defining the patron username format, used for patron registration and self-service username changing only',
5463 'coust', 'description'),
5465 ,( 'opac.lock_usernames', 'glob',
5466 oils_i18n_gettext('opac.lock_usernames',
5469 oils_i18n_gettext('opac.lock_usernames',
5470 'If enabled username changing via the OPAC will be disabled',
5471 'coust', 'description'),
5473 ,( 'opac.unlimit_usernames', 'glob',
5474 oils_i18n_gettext('opac.unlimit_usernames',
5475 'Allow multiple username changes',
5477 oils_i18n_gettext('opac.unlimit_usernames',
5478 'If enabled (and Lock Usernames is not set) patrons will be allowed to change their username when it does not look like a barcode. Otherwise username changing in the OPAC will only be allowed when the patron''s username looks like a barcode.',
5479 'coust', 'description'),
5483 -- Evergreen DB patch 0635.data.opac.jump-to-details-setting.sql
5487 -- check whether patch can be applied
5488 SELECT evergreen.upgrade_deps_block_check('0635', :eg_version);
5490 INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype )
5492 'opac.staff.jump_to_details_on_single_hit',
5495 'opac.staff.jump_to_details_on_single_hit',
5496 'Jump to details on 1 hit (staff client)',
5501 'opac.staff.jump_to_details_on_single_hit',
5502 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the OPAC within the staff client',
5508 'opac.patron.jump_to_details_on_single_hit',
5511 'opac.patron.jump_to_details_on_single_hit',
5512 'Jump to details on 1 hit (public)',
5517 'opac.patron.jump_to_details_on_single_hit',
5518 'When a search yields only 1 result, jump directly to the record details page. This setting only affects the public OPAC',
5525 -- Evergreen DB patch 0636.data.grace_period_extend.sql
5527 -- OU setting turns on grace period auto extension. By default they only do so
5528 -- when the grace period ends on a closed date, but there are two modifiers to
5531 -- The first modifier causes grace periods to extend for all closed dates that
5532 -- they intersect. This is "grace periods are only consumed by open days."
5534 -- The second modifier causes a grace period that ends just before a closed
5535 -- day, with or without extension having happened, to include the closed day
5536 -- (and any following it) as well. This is mainly so that a backdate into the
5537 -- closed period following the grace period will assume the "best case" of the
5538 -- item having been returned after hours on the last day of the closed date.
5542 -- check whether patch can be applied
5543 SELECT evergreen.upgrade_deps_block_check('0636', :eg_version);
5545 INSERT INTO config.org_unit_setting_type(name, grp, label, description, datatype) VALUES
5547 ( 'circ.grace.extend', 'circ',
5548 oils_i18n_gettext('circ.grace.extend',
5549 'Auto-Extend Grace Periods',
5551 oils_i18n_gettext('circ.grace.extend',
5552 'When enabled grace periods will auto-extend. By default this will be only when they are a full day or more and end on a closed date, though other options can alter this.',
5553 'coust', 'description'),
5556 ,( 'circ.grace.extend.all', 'circ',
5557 oils_i18n_gettext('circ.grace.extend.all',
5558 'Auto-Extending Grace Periods extend for all closed dates',
5560 oils_i18n_gettext('circ.grace.extend.all',
5561 'If enabled and Grace Periods auto-extending is turned on grace periods will extend past all closed dates they intersect, within hard-coded limits. This basically becomes "grace periods can only be consumed by closed dates".',
5562 'coust', 'description'),
5565 ,( 'circ.grace.extend.into_closed', 'circ',
5566 oils_i18n_gettext('circ.grace.extend.into_closed',
5567 'Auto-Extending Grace Periods include trailing closed dates',
5569 oils_i18n_gettext('circ.grace.extend.into_closed',
5570 'If enabled and Grace Periods auto-extending is turned on grace periods will include closed dates that directly follow the last day of the grace period, to allow a backdate into the closed dates to assume "returned after hours on the last day of the grace period, and thus still within it" automatically.',
5571 'coust', 'description'),
5575 -- XXXX.schema-acs-nfi.sql
5577 SELECT evergreen.upgrade_deps_block_check('0640', :eg_version);
5579 -- AFTER UPDATE OR INSERT trigger for authority.record_entry
5580 CREATE OR REPLACE FUNCTION authority.indexing_ingest_or_delete () RETURNS TRIGGER AS $func$
5583 IF NEW.deleted IS TRUE THEN -- If this authority is deleted
5584 DELETE FROM authority.bib_linking WHERE authority = NEW.id; -- Avoid updating fields in bibs that are no longer visible
5585 DELETE FROM authority.full_rec WHERE record = NEW.id; -- Avoid validating fields against deleted authority records
5586 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5587 -- Should remove matching $0 from controlled fields at the same time?
5588 RETURN NEW; -- and we're done
5591 IF TG_OP = 'UPDATE' THEN -- re-ingest?
5592 PERFORM * FROM config.internal_flag WHERE name = 'ingest.reingest.force_on_same_marc' AND enabled;
5594 IF NOT FOUND AND OLD.marc = NEW.marc THEN -- don't do anything if the MARC didn't change
5598 -- Propagate these updates to any linked bib records
5599 PERFORM authority.propagate_changes(NEW.id) FROM authority.record_entry WHERE id = NEW.id;
5601 DELETE FROM authority.simple_heading WHERE record = NEW.id;
5604 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
5605 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(NEW.marc);
5607 -- Flatten and insert the afr data
5608 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_full_rec' AND enabled;
5610 PERFORM authority.reingest_authority_full_rec(NEW.id);
5611 PERFORM * FROM config.internal_flag WHERE name = 'ingest.disable_authority_rec_descriptor' AND enabled;
5613 PERFORM authority.reingest_authority_rec_descriptor(NEW.id);
5619 $func$ LANGUAGE PLPGSQL;
5621 ALTER TABLE authority.control_set_authority_field ADD COLUMN nfi CHAR(1);
5623 -- Entries that need to respect an NFI
5624 UPDATE authority.control_set_authority_field SET nfi = '2'
5625 WHERE id IN (4,24,44,64);
5627 DROP TRIGGER authority_full_rec_fti_trigger ON authority.full_rec;
5628 CREATE TRIGGER authority_full_rec_fti_trigger
5629 BEFORE UPDATE OR INSERT ON authority.full_rec
5630 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5632 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT, no_thesaurus BOOL ) RETURNS TEXT AS $func$
5634 acsaf authority.control_set_authority_field%ROWTYPE;
5643 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5645 SELECT control_set INTO cset FROM authority.record_entry WHERE id = auth_id;
5647 IF cset IS NULL THEN
5648 SELECT control_set INTO cset
5649 FROM authority.control_set_authority_field
5650 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]))
5654 thes_code := vandelay.marc21_extract_fixed_field(marcxml,'Subj');
5655 IF thes_code IS NULL THEN
5657 ELSIF thes_code = 'z' THEN
5658 thes_code := COALESCE( oils_xpath_string('//*[@tag="040"]/*[@code="f"][1]', marcxml), '' );
5662 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset AND main_entry IS NULL LOOP
5663 tag_used := acsaf.tag;
5664 nfi_used := acsaf.nfi;
5666 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5667 tmp_text := oils_xpath_string('//*[@tag="'||tag_used||'"]/*[@code="'||sf||'"]', marcxml);
5669 IF first_sf AND tmp_text IS NOT NULL AND nfi_used IS NOT NULL THEN
5671 tmp_text := SUBSTRING(
5676 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5691 IF tmp_text IS NOT NULL AND tmp_text <> '' THEN
5692 heading_text := heading_text || E'\u2021' || sf || ' ' || tmp_text;
5695 EXIT WHEN heading_text <> '';
5698 IF heading_text <> '' THEN
5699 IF no_thesaurus IS TRUE THEN
5700 heading_text := tag_used || ' ' || public.naco_normalize(heading_text);
5702 heading_text := tag_used || '_' || COALESCE(nfi_used,'-') || '_' || thes_code || ' ' || public.naco_normalize(heading_text);
5705 heading_text := 'NOHEADING_' || thes_code || ' ' || MD5(marcxml);
5708 RETURN heading_text;
5710 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5712 CREATE OR REPLACE FUNCTION authority.simple_normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5713 SELECT authority.normalize_heading($1, TRUE);
5714 $func$ LANGUAGE SQL IMMUTABLE;
5716 CREATE OR REPLACE FUNCTION authority.normalize_heading( marcxml TEXT ) RETURNS TEXT AS $func$
5717 SELECT authority.normalize_heading($1, FALSE);
5718 $func$ LANGUAGE SQL IMMUTABLE;
5721 CREATE TABLE authority.simple_heading (
5722 id BIGSERIAL PRIMARY KEY,
5723 record BIGINT NOT NULL REFERENCES authority.record_entry (id),
5724 atag INT NOT NULL REFERENCES authority.control_set_authority_field (id),
5725 value TEXT NOT NULL,
5726 sort_value TEXT NOT NULL,
5727 index_vector tsvector NOT NULL
5729 CREATE TRIGGER authority_simple_heading_fti_trigger
5730 BEFORE UPDATE OR INSERT ON authority.simple_heading
5731 FOR EACH ROW EXECUTE PROCEDURE oils_tsearch2('keyword');
5733 CREATE INDEX authority_simple_heading_index_vector_idx ON authority.simple_heading USING GIST (index_vector);
5734 CREATE INDEX authority_simple_heading_value_idx ON authority.simple_heading (value);
5735 CREATE INDEX authority_simple_heading_sort_value_idx ON authority.simple_heading (sort_value);
5737 CREATE OR REPLACE FUNCTION authority.simple_heading_set( marcxml TEXT ) RETURNS SETOF authority.simple_heading AS $func$
5739 res authority.simple_heading%ROWTYPE;
5740 acsaf authority.control_set_authority_field%ROWTYPE;
5750 auth_id INT DEFAULT oils_xpath_string('//*[@tag="901"]/*[local-name()="subfield" and @code="c"]', marcxml)::INT;
5753 res.record := auth_id;
5755 SELECT control_set INTO cset
5756 FROM authority.control_set_authority_field
5757 WHERE tag IN ( SELECT UNNEST(XPATH('//*[starts-with(@tag,"1")]/@tag',marcxml::XML)::TEXT[]) )
5760 FOR acsaf IN SELECT * FROM authority.control_set_authority_field WHERE control_set = cset LOOP
5762 res.atag := acsaf.id;
5763 tag_used := acsaf.tag;
5764 nfi_used := acsaf.nfi;
5766 FOR tmp_xml IN SELECT UNNEST(XPATH('//*[@tag="'||tag_used||'"]', marcxml::XML)) LOOP
5769 FOR sf IN SELECT * FROM regexp_split_to_table(acsaf.sf_list,'') LOOP
5770 heading_text := heading_text || COALESCE( ' ' || oils_xpath_string('//*[@code="'||sf||'"]',tmp_xml::TEXT), '');
5773 heading_text := public.naco_normalize(heading_text);
5775 IF nfi_used IS NOT NULL THEN
5777 sort_text := SUBSTRING(
5782 oils_xpath_string('//*[@tag="'||tag_used||'"]/@ind'||nfi_used, marcxml),
5794 sort_text := heading_text;
5797 IF heading_text IS NOT NULL AND heading_text <> '' THEN
5798 res.value := heading_text;
5799 res.sort_value := sort_text;
5809 $func$ LANGUAGE PLPGSQL IMMUTABLE;
5811 -- Support function used to find the pivot for alpha-heading-browse style searching
5812 CREATE OR REPLACE FUNCTION authority.simple_heading_find_pivot( a INT[], q TEXT ) RETURNS TEXT AS $$
5814 sort_value_row RECORD;
5819 t_term := public.naco_normalize(q);
5821 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5822 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5825 FROM authority.simple_heading ash
5826 WHERE ash.atag = ANY (a)
5827 AND ash.sort_value >= t_term
5828 ORDER BY rank DESC, ash.sort_value
5831 SELECT CASE WHEN ash.sort_value LIKE t_term || '%' THEN 1 ELSE 0 END
5832 + CASE WHEN ash.value LIKE t_term || '%' THEN 1 ELSE 0 END AS rank,
5835 FROM authority.simple_heading ash
5836 WHERE ash.atag = ANY (a)
5837 AND ash.value >= t_term
5838 ORDER BY rank DESC, ash.sort_value
5841 IF value_row.rank > sort_value_row.rank THEN
5842 RETURN value_row.sort_value;
5844 RETURN sort_value_row.sort_value;
5847 $$ LANGUAGE PLPGSQL;
5850 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_center( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
5852 pivot_sort_value TEXT;
5853 boffset INT DEFAULT 0;
5854 aoffset INT DEFAULT 0;
5855 blimit INT DEFAULT 0;
5856 alimit INT DEFAULT 0;
5859 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5862 blimit := pagesize / 2;
5865 IF pagesize % 2 <> 0 THEN
5866 alimit := alimit + 1;
5872 boffset := pagesize / 2;
5875 IF pagesize % 2 <> 0 THEN
5876 boffset := boffset + 1;
5882 -- "bottom" half of the browse results
5885 row_number() over ()
5886 FROM authority.simple_heading ash
5887 WHERE ash.atag = ANY (atag_list)
5888 AND ash.sort_value < pivot_sort_value
5889 ORDER BY ash.sort_value DESC
5891 OFFSET ABS(page) * pagesize - boffset
5892 ) x ORDER BY row_number DESC;
5897 -- "bottom" half of the browse results
5899 FROM authority.simple_heading ash
5900 WHERE ash.atag = ANY (atag_list)
5901 AND ash.sort_value >= pivot_sort_value
5902 ORDER BY ash.sort_value
5904 OFFSET ABS(page) * pagesize - aoffset;
5907 $$ LANGUAGE PLPGSQL ROWS 10;
5909 CREATE OR REPLACE FUNCTION authority.simple_heading_browse_top( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5911 pivot_sort_value TEXT;
5914 pivot_sort_value := authority.simple_heading_find_pivot(atag_list,q);
5918 -- "bottom" half of the browse results
5921 row_number() over ()
5922 FROM authority.simple_heading ash
5923 WHERE ash.atag = ANY (atag_list)
5924 AND ash.sort_value < pivot_sort_value
5925 ORDER BY ash.sort_value DESC
5927 OFFSET (ABS(page) - 1) * pagesize
5928 ) x ORDER BY row_number DESC;
5933 -- "bottom" half of the browse results
5935 FROM authority.simple_heading ash
5936 WHERE ash.atag = ANY (atag_list)
5937 AND ash.sort_value >= pivot_sort_value
5938 ORDER BY ash.sort_value
5940 OFFSET ABS(page) * pagesize ;
5943 $$ LANGUAGE PLPGSQL ROWS 10;
5945 CREATE OR REPLACE FUNCTION authority.simple_heading_search_rank( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5947 FROM authority.simple_heading ash,
5948 public.naco_normalize($2) t(term),
5949 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5950 WHERE ash.atag = ANY ($1)
5951 AND ash.index_vector @@ ptsq.term
5952 ORDER BY ts_rank_cd(ash.index_vector,ptsq.term,14)::numeric
5953 + CASE WHEN ash.sort_value LIKE t.term || '%' THEN 2 ELSE 0 END
5954 + CASE WHEN ash.value LIKE t.term || '%' THEN 1 ELSE 0 END DESC
5957 $$ LANGUAGE SQL ROWS 10;
5959 CREATE OR REPLACE FUNCTION authority.simple_heading_search_heading( atag_list INT[], q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
5961 FROM authority.simple_heading ash,
5962 public.naco_normalize($2) t(term),
5963 plainto_tsquery('keyword'::regconfig,$2) ptsq(term)
5964 WHERE ash.atag = ANY ($1)
5965 AND ash.index_vector @@ ptsq.term
5966 ORDER BY ash.sort_value
5969 $$ LANGUAGE SQL ROWS 10;
5972 CREATE OR REPLACE FUNCTION authority.axis_authority_tags(a TEXT) RETURNS INT[] AS $$
5973 SELECT ARRAY_ACCUM(field) FROM authority.browse_axis_authority_field_map WHERE axis = $1;
5976 CREATE OR REPLACE FUNCTION authority.axis_authority_tags_refs(a TEXT) RETURNS INT[] AS $$
5979 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.field)
5981 FROM authority.browse_axis_authority_field_map a
5987 CREATE OR REPLACE FUNCTION authority.btag_authority_tags(btag TEXT) RETURNS INT[] AS $$
5988 SELECT ARRAY_ACCUM(authority_field) FROM authority.control_set_bib_field WHERE tag = $1
5991 CREATE OR REPLACE FUNCTION authority.btag_authority_tags_refs(btag TEXT) RETURNS INT[] AS $$
5993 ARRAY[a.authority_field],
5994 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.authority_field)
5996 FROM authority.control_set_bib_field a
6002 CREATE OR REPLACE FUNCTION authority.atag_authority_tags(atag TEXT) RETURNS INT[] AS $$
6003 SELECT ARRAY_ACCUM(id) FROM authority.control_set_authority_field WHERE tag = $1
6006 CREATE OR REPLACE FUNCTION authority.atag_authority_tags_refs(atag TEXT) RETURNS INT[] AS $$
6009 (SELECT ARRAY_ACCUM(x.id) FROM authority.control_set_authority_field x WHERE x.main_entry = a.id)
6011 FROM authority.control_set_authority_field a
6016 CREATE OR REPLACE FUNCTION authority.axis_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6017 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags($1), $2, $3, $4)
6018 $$ LANGUAGE SQL ROWS 10;
6020 CREATE OR REPLACE FUNCTION authority.btag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6021 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags($1), $2, $3, $4)
6022 $$ LANGUAGE SQL ROWS 10;
6024 CREATE OR REPLACE FUNCTION authority.atag_browse_center( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6025 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags($1), $2, $3, $4)
6026 $$ LANGUAGE SQL ROWS 10;
6028 CREATE OR REPLACE FUNCTION authority.axis_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6029 SELECT * FROM authority.simple_heading_browse_center(authority.axis_authority_tags_refs($1), $2, $3, $4)
6030 $$ LANGUAGE SQL ROWS 10;
6032 CREATE OR REPLACE FUNCTION authority.btag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6033 SELECT * FROM authority.simple_heading_browse_center(authority.btag_authority_tags_refs($1), $2, $3, $4)
6034 $$ LANGUAGE SQL ROWS 10;
6036 CREATE OR REPLACE FUNCTION authority.atag_browse_center_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 9 ) RETURNS SETOF BIGINT AS $$
6037 SELECT * FROM authority.simple_heading_browse_center(authority.atag_authority_tags_refs($1), $2, $3, $4)
6038 $$ LANGUAGE SQL ROWS 10;
6041 CREATE OR REPLACE FUNCTION authority.axis_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6042 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags($1), $2, $3, $4)
6043 $$ LANGUAGE SQL ROWS 10;
6045 CREATE OR REPLACE FUNCTION authority.btag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6046 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags($1), $2, $3, $4)
6047 $$ LANGUAGE SQL ROWS 10;
6049 CREATE OR REPLACE FUNCTION authority.atag_browse_top( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6050 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags($1), $2, $3, $4)
6051 $$ LANGUAGE SQL ROWS 10;
6053 CREATE OR REPLACE FUNCTION authority.axis_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6054 SELECT * FROM authority.simple_heading_browse_top(authority.axis_authority_tags_refs($1), $2, $3, $4)
6055 $$ LANGUAGE SQL ROWS 10;
6057 CREATE OR REPLACE FUNCTION authority.btag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6058 SELECT * FROM authority.simple_heading_browse_top(authority.btag_authority_tags_refs($1), $2, $3, $4)
6059 $$ LANGUAGE SQL ROWS 10;
6061 CREATE OR REPLACE FUNCTION authority.atag_browse_top_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6062 SELECT * FROM authority.simple_heading_browse_top(authority.atag_authority_tags_refs($1), $2, $3, $4)
6063 $$ LANGUAGE SQL ROWS 10;
6066 CREATE OR REPLACE FUNCTION authority.axis_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6067 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags($1), $2, $3, $4)
6068 $$ LANGUAGE SQL ROWS 10;
6070 CREATE OR REPLACE FUNCTION authority.btag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6071 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags($1), $2, $3, $4)
6072 $$ LANGUAGE SQL ROWS 10;
6074 CREATE OR REPLACE FUNCTION authority.atag_search_rank( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6075 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags($1), $2, $3, $4)
6076 $$ LANGUAGE SQL ROWS 10;
6078 CREATE OR REPLACE FUNCTION authority.axis_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6079 SELECT * FROM authority.simple_heading_search_rank(authority.axis_authority_tags_refs($1), $2, $3, $4)
6080 $$ LANGUAGE SQL ROWS 10;
6082 CREATE OR REPLACE FUNCTION authority.btag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6083 SELECT * FROM authority.simple_heading_search_rank(authority.btag_authority_tags_refs($1), $2, $3, $4)
6084 $$ LANGUAGE SQL ROWS 10;
6086 CREATE OR REPLACE FUNCTION authority.atag_search_rank_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6087 SELECT * FROM authority.simple_heading_search_rank(authority.atag_authority_tags_refs($1), $2, $3, $4)
6088 $$ LANGUAGE SQL ROWS 10;
6091 CREATE OR REPLACE FUNCTION authority.axis_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6092 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags($1), $2, $3, $4)
6093 $$ LANGUAGE SQL ROWS 10;
6095 CREATE OR REPLACE FUNCTION authority.btag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6096 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags($1), $2, $3, $4)
6097 $$ LANGUAGE SQL ROWS 10;
6099 CREATE OR REPLACE FUNCTION authority.atag_search_heading( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6100 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags($1), $2, $3, $4)
6101 $$ LANGUAGE SQL ROWS 10;
6103 CREATE OR REPLACE FUNCTION authority.axis_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6104 SELECT * FROM authority.simple_heading_search_heading(authority.axis_authority_tags_refs($1), $2, $3, $4)
6105 $$ LANGUAGE SQL ROWS 10;
6107 CREATE OR REPLACE FUNCTION authority.btag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6108 SELECT * FROM authority.simple_heading_search_heading(authority.btag_authority_tags_refs($1), $2, $3, $4)
6109 $$ LANGUAGE SQL ROWS 10;
6111 CREATE OR REPLACE FUNCTION authority.atag_search_heading_refs( a TEXT, q TEXT, page INT DEFAULT 0, pagesize INT DEFAULT 10 ) RETURNS SETOF BIGINT AS $$
6112 SELECT * FROM authority.simple_heading_search_heading(authority.atag_authority_tags_refs($1), $2, $3, $4)
6113 $$ LANGUAGE SQL ROWS 10;
6117 -- Evergreen DB patch 0641.schema.org_unit_setting_json_check.sql
6121 -- check whether patch can be applied
6122 SELECT evergreen.upgrade_deps_block_check('0641', :eg_version);
6124 ALTER TABLE actor.org_unit_setting ADD CONSTRAINT aous_must_be_json CHECK ( evergreen.is_json(value) );
6126 -- Evergreen DB patch 0642.data.acq-worksheet-hold-count.sql
6128 -- check whether patch can be applied
6129 SELECT evergreen.upgrade_deps_block_check('0642', :eg_version);
6131 UPDATE action_trigger.event_definition SET template =
6134 [%- SET li = target; -%]
6135 <div class="wrapper">
6136 <div class="summary" style='font-size:110%; font-weight:bold;'>
6138 <div>Title: [% helpers.get_li_attr("title", "", li.attributes) %]</div>
6139 <div>Author: [% helpers.get_li_attr("author", "", li.attributes) %]</div>
6140 <div class="count">Item Count: [% li.lineitem_details.size %]</div>
6141 <div class="lineid">Lineitem ID: [% li.id %]</div>
6142 <div>Open Holds: [% helpers.bre_open_hold_count(li.eg_bib_id) %]</div>
6144 [% IF li.distribution_formulas.size > 0 %]
6145 [% SET forms = [] %]
6146 [% FOREACH form IN li.distribution_formulas; forms.push(form.formula.name); END %]
6147 <div>Distribution Formulas: [% forms.join(',') %]</div>
6150 [% IF li.lineitem_notes.size > 0 %]
6153 [%- FOR note IN li.lineitem_notes -%]
6155 [% IF note.alert_text %]
6156 [% note.alert_text.code -%]
6157 [% IF note.value -%]
6174 <th>Call Number</th>
6176 <th>Shelving Location</th>
6182 [% FOREACH detail IN li.lineitem_details.sort('owning_lib') %]
6184 IF detail.eg_copy_id;
6185 SET copy = detail.eg_copy_id;
6186 SET cn_label = copy.call_number.label;
6189 SET cn_label = detail.cn_label;
6193 <!-- acq.lineitem_detail.id = [%- detail.id -%] -->
6194 <td style='padding:5px;'>[% detail.owning_lib.shortname %]</td>
6195 <td style='padding:5px;'>[% IF copy.barcode %]<span class="barcode" >[% detail.barcode %]</span>[% END %]</td>
6196 <td style='padding:5px;'>[% IF cn_label %]<span class="cn_label" >[% cn_label %]</span>[% END %]</td>
6197 <td style='padding:5px;'>[% IF detail.fund %]<span class="fund">[% detail.fund.code %] ([% detail.fund.year %])</span>[% END %]</td>
6198 <td style='padding:5px;'>[% copy.location.name %]</td>
6199 <td style='padding:5px;'>[% IF detail.recv_time %]<span class="recv_time">[% detail.recv_time %]</span>[% END %]</td>
6200 <td style='padding:5px;'>[% detail.note %]</td>
6210 SELECT evergreen.upgrade_deps_block_check('0643', :eg_version);
6218 FROM authority.record_entry
6221 AND id NOT IN (SELECT DISTINCT record FROM authority.simple_heading)
6223 INSERT INTO authority.simple_heading (record,atag,value,sort_value)
6224 SELECT record, atag, value, sort_value FROM authority.simple_heading_set(x);
6231 SELECT evergreen.upgrade_deps_block_check('0644', :eg_version);
6233 INSERT into config.org_unit_setting_type (name, grp, label, description, datatype) VALUES
6234 ( 'circ.holds.target_when_closed', 'circ',
6235 oils_i18n_gettext('circ.holds.target_when_closed',
6236 'Target copies for a hold even if copy''s circ lib is closed',
6238 oils_i18n_gettext('circ.holds.target_when_closed',
6239 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table).',
6240 'coust', 'description'),
6242 ( 'circ.holds.target_when_closed_if_at_pickup_lib', 'circ',
6243 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6244 'Target copies for a hold even if copy''s circ lib is closed IF the circ lib is the hold''s pickup lib',
6246 oils_i18n_gettext('circ.holds.target_when_closed_if_at_pickup_lib',
6247 'If this setting is true at a given org unit or one of its ancestors, the hold targeter will target copies from this org unit even if the org unit is closed (according to the actor.org_unit.closed_date table) IF AND ONLY IF the copy''s circ lib is the same as the hold''s pickup lib.',
6248 'coust', 'description'),
6252 -- Evergreen DB patch XXXX.data.hold-notification-cleanup-mod.sql
6254 -- check whether patch can be applied
6255 SELECT evergreen.upgrade_deps_block_check('0647', :eg_version);
6257 INSERT INTO action_trigger.cleanup ( module, description ) VALUES (
6258 'CreateHoldNotification',
6260 'CreateHoldNotification',
6261 'Creates a hold_notification record for each notified hold',
6267 UPDATE action_trigger.event_definition
6269 cleanup_success = 'CreateHoldNotification'
6271 id = 5 -- stock hold-ready email event_def
6272 AND cleanup_success IS NULL; -- don't clobber any existing cleanup mod
6274 -- Evergreen DB patch XXXX.schema.unnest-hold-permit-upgrade-script-repair.sql
6276 -- This patch makes no changes to the baseline schema and is
6277 -- only meant to repair a previous upgrade script.
6280 -- check whether patch can be applied
6281 SELECT evergreen.upgrade_deps_block_check('0651', :eg_version);
6283 CREATE OR REPLACE FUNCTION action.hold_request_permit_test( pickup_ou INT, request_ou INT, match_item BIGINT, match_user INT, match_requestor INT, retargetting BOOL ) RETURNS SETOF action.matrix_test_result AS $func$
6286 user_object actor.usr%ROWTYPE;
6287 age_protect_object config.rule_age_hold_protect%ROWTYPE;
6288 standing_penalty config.standing_penalty%ROWTYPE;
6289 transit_range_ou_type actor.org_unit_type%ROWTYPE;
6290 transit_source actor.org_unit%ROWTYPE;
6291 item_object asset.copy%ROWTYPE;
6292 item_cn_object asset.call_number%ROWTYPE;
6293 ou_skip actor.org_unit_setting%ROWTYPE;
6294 result action.matrix_test_result;
6295 hold_test config.hold_matrix_matchpoint%ROWTYPE;
6296 use_active_date TEXT;
6297 age_protect_date TIMESTAMP WITH TIME ZONE;
6299 hold_transit_prox INT;
6300 frozen_hold_count INT;
6301 context_org_list INT[];
6304 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
6305 SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( pickup_ou );
6307 result.success := TRUE;
6309 -- Fail if we couldn't find a user
6310 IF user_object.id IS NULL THEN
6311 result.fail_part := 'no_user';
6312 result.success := FALSE;
6318 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
6320 -- Fail if we couldn't find a copy
6321 IF item_object.id IS NULL THEN
6322 result.fail_part := 'no_item';
6323 result.success := FALSE;
6329 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(pickup_ou, request_ou, match_item, match_user, match_requestor);
6330 result.matchpoint := matchpoint_id;
6332 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
6334 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
6335 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
6336 result.fail_part := 'circ.holds.target_skip_me';
6337 result.success := FALSE;
6343 -- Fail if user is barred
6344 IF user_object.barred IS TRUE THEN
6345 result.fail_part := 'actor.usr.barred';
6346 result.success := FALSE;
6352 -- Fail if we couldn't find any matchpoint (requires a default)
6353 IF matchpoint_id IS NULL THEN
6354 result.fail_part := 'no_matchpoint';
6355 result.success := FALSE;
6361 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
6363 IF hold_test.holdable IS FALSE THEN
6364 result.fail_part := 'config.hold_matrix_test.holdable';
6365 result.success := FALSE;
6370 IF hold_test.transit_range IS NOT NULL THEN
6371 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
6372 IF hold_test.distance_is_from_owner THEN
6373 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;
6375 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
6378 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = pickup_ou;
6381 result.fail_part := 'transit_range';
6382 result.success := FALSE;
6388 FOR standing_penalty IN
6389 SELECT DISTINCT csp.*
6390 FROM actor.usr_standing_penalty usp
6391 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
6392 WHERE usr = match_user
6393 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
6394 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
6395 AND csp.block_list LIKE '%HOLD%' LOOP
6397 result.fail_part := standing_penalty.name;
6398 result.success := FALSE;
6403 IF hold_test.stop_blocked_user IS TRUE THEN
6404 FOR standing_penalty IN
6405 SELECT DISTINCT csp.*
6406 FROM actor.usr_standing_penalty usp
6407 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
6408 WHERE usr = match_user
6409 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
6410 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
6411 AND csp.block_list LIKE '%CIRC%' LOOP
6413 result.fail_part := standing_penalty.name;
6414 result.success := FALSE;
6420 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
6421 SELECT INTO hold_count COUNT(*)
6422 FROM action.hold_request
6423 WHERE usr = match_user
6424 AND fulfillment_time IS NULL
6425 AND cancel_time IS NULL
6426 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
6428 IF hold_count >= hold_test.max_holds THEN
6429 result.fail_part := 'config.hold_matrix_test.max_holds';
6430 result.success := FALSE;
6436 IF item_object.age_protect IS NOT NULL THEN
6437 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
6438 IF hold_test.distance_is_from_owner THEN
6439 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
6441 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
6443 IF use_active_date = 'true' THEN
6444 age_protect_date := COALESCE(item_object.active_date, NOW());
6446 age_protect_date := item_object.create_date;
6448 IF age_protect_date + age_protect_object.age > NOW() THEN
6449 IF hold_test.distance_is_from_owner THEN
6450 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
6451 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_cn_object.owning_lib AND to_org = pickup_ou;
6453 SELECT INTO hold_transit_prox prox FROM actor.org_unit_proximity WHERE from_org = item_object.circ_lib AND to_org = pickup_ou;
6456 IF hold_transit_prox > age_protect_object.prox THEN
6457 result.fail_part := 'config.rule_age_hold_protect.prox';
6458 result.success := FALSE;
6471 $func$ LANGUAGE plpgsql;