1 --Upgrade Script for 3.5.1 to 3.6-beta2
2 \set eg_version '''3.6-beta2'''
4 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('3.6-beta2', :eg_version);
6 SELECT evergreen.upgrade_deps_block_check('1205', :eg_version);
8 INSERT INTO config.print_template
9 (id, name, locale, active, owner, label, template)
11 3, 'booking_capture', 'en-US', TRUE,
12 (SELECT id FROM actor.org_unit WHERE parent_ou IS NULL),
13 oils_i18n_gettext(3, 'Booking capture slip', 'cpt', 'label'),
17 SET data = template_data;
18 # template_data is data returned from open-ils.booking.resources.capture_for_reservation.
22 dest_ou = helpers.get_org_unit(data.transit.dest);
24 <div>This item need to be routed to <strong>[% dest_ou.shortname %]</strong></div>
26 <div>This item need to be routed to <strong>RESERVATION SHELF:</strong></div>
28 <div>Barcode: [% data.reservation.current_resource.barcode %]</div>
29 <div>Title: [% data.reservation.current_resource.type.name %]</div>
30 <div>Note: [% data.reservation.note %]</div>
32 <p><strong>Reserved for patron</strong> [% data.reservation.usr.family_name %], [% data.reservation.usr.first_given_name %] [% data.reservation.usr.second_given_name %]
33 <br/>Barcode: [% data.reservation.usr.card.barcode %]</p>
34 <p>Request time: [% date.format(helpers.format_date(data.reservation.request_time, client_timezone), '%x %r', locale) %]
36 [% date.format(helpers.format_date(data.reservation.start_time, client_timezone), '%x %r', locale) %]
37 - [% date.format(helpers.format_date(data.reservation.end_time, client_timezone), '%x %r', locale) %]</p>
38 <p>Slip date: [% date.format(helpers.current_date(client_timezone), '%x %r', locale) %]<br/>
39 Printed by [% data.staff.family_name %], [% data.staff.first_given_name %] [% data.staff.second_given_name %]
40 at [% data.workstation %]</p>
47 INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
49 'eg.grid.booking.captured', 'gui', 'object',
52 'Grid Config: Booking Captured Reservations',
58 SELECT evergreen.upgrade_deps_block_check('1210', :eg_version); -- csharp/rhamby/sandbergja/gmcharlt
60 ALTER TABLE action.in_house_use ADD COLUMN workstation INT REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED;
61 ALTER TABLE action.non_cat_in_house_use ADD COLUMN workstation INT REFERENCES actor.workstation (id) DEFERRABLE INITIALLY DEFERRED;
63 CREATE INDEX action_in_house_use_ws_idx ON action.in_house_use ( workstation );
64 CREATE INDEX non_cat_in_house_use_ws_idx ON action.non_cat_in_house_use ( workstation );
68 SELECT evergreen.upgrade_deps_block_check('1212', :eg_version); -- berick/sandbergja/gmcharlt
70 DELETE FROM actor.org_unit_setting
71 WHERE name = 'ui.staff.angular_catalog.enabled';
73 DELETE FROM config.org_unit_setting_type_log
74 WHERE field_name = 'ui.staff.angular_catalog.enabled';
76 DELETE FROM config.org_unit_setting_type
77 WHERE name = 'ui.staff.angular_catalog.enabled';
79 -- activate the stock hold-for-bib server print template
80 UPDATE config.print_template SET active = TRUE WHERE name = 'holds_for_bib';
83 SELECT evergreen.upgrade_deps_block_check('1213', :eg_version);
85 CREATE OR REPLACE FUNCTION actor.change_password (user_id INT, new_pw TEXT, pw_type TEXT DEFAULT 'main')
90 SELECT actor.create_salt(pw_type) INTO new_salt;
92 IF pw_type = 'main' THEN
93 -- Only 'main' passwords are required to have
94 -- the extra layer of MD5 hashing.
95 PERFORM actor.set_passwd(
96 user_id, pw_type, md5(new_salt || md5(new_pw)), new_salt
100 PERFORM actor.set_passwd(user_id, pw_type, new_pw, new_salt);
103 $$ LANGUAGE 'plpgsql';
105 COMMENT ON FUNCTION actor.change_password(INT,TEXT,TEXT) IS $$
106 Allows setting a salted password for a user by passing actor.usr id and the text of the password.
110 SELECT evergreen.upgrade_deps_block_check('1214', :eg_version);
112 CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$
114 moved_objects INT := 0;
115 source_cn asset.call_number%ROWTYPE;
116 target_cn asset.call_number%ROWTYPE;
117 metarec metabib.metarecord%ROWTYPE;
118 hold action.hold_request%ROWTYPE;
119 ser_rec serial.record_entry%ROWTYPE;
120 ser_sub serial.subscription%ROWTYPE;
121 acq_lineitem acq.lineitem%ROWTYPE;
122 acq_request acq.user_request%ROWTYPE;
123 booking booking.resource_type%ROWTYPE;
124 source_part biblio.monograph_part%ROWTYPE;
125 target_part biblio.monograph_part%ROWTYPE;
126 multi_home biblio.peer_bib_copy_map%ROWTYPE;
133 -- move any 856 entries on records that have at least one MARC-mapped URI entry
134 SELECT INTO uri_count COUNT(*)
135 FROM asset.uri_call_number_map m
136 JOIN asset.call_number cn ON (m.call_number = cn.id)
137 WHERE cn.record = source_record;
139 IF uri_count > 0 THEN
141 -- This returns more nodes than you might expect:
142 -- 7 instead of 1 for an 856 with $u $y $9
143 SELECT COUNT(*) INTO counter
144 FROM oils_xpath_table(
147 'biblio.record_entry',
149 'id=' || source_record
150 ) as t(i int,c text);
152 FOR i IN 1 .. counter LOOP
153 SELECT '<datafield xmlns="http://www.loc.gov/MARC21/slim"' ||
155 ' ind1="' || FIRST(ind1) || '"' ||
156 ' ind2="' || FIRST(ind2) || '">' ||
158 '<subfield code="' || subfield || '">' ||
161 regexp_replace(data,'&','&','g'),
165 ) || '</subfield>', ''
166 ) || '</datafield>' INTO uri_datafield
167 FROM oils_xpath_table(
170 'biblio.record_entry',
171 '//*[@tag="856"][position()=' || i || ']/@ind1|' ||
172 '//*[@tag="856"][position()=' || i || ']/@ind2|' ||
173 '//*[@tag="856"][position()=' || i || ']/*/@code|' ||
174 '//*[@tag="856"][position()=' || i || ']/*[@code]',
175 'id=' || source_record
176 ) as t(id int,ind1 text, ind2 text,subfield text,data text);
178 -- As most of the results will be NULL, protect against NULLifying
179 -- the valid content that we do generate
180 uri_text := uri_text || COALESCE(uri_datafield, '');
183 IF uri_text <> '' THEN
184 UPDATE biblio.record_entry
185 SET marc = regexp_replace(marc,'(</[^>]*record>)', uri_text || E'\\1')
186 WHERE id = target_record;
191 -- Find and move metarecords to the target record
192 SELECT INTO metarec *
193 FROM metabib.metarecord
194 WHERE master_record = source_record;
197 UPDATE metabib.metarecord
198 SET master_record = target_record,
200 WHERE id = metarec.id;
202 moved_objects := moved_objects + 1;
205 -- Find call numbers attached to the source ...
206 FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP
208 SELECT INTO target_cn *
209 FROM asset.call_number
210 WHERE label = source_cn.label
211 AND prefix = source_cn.prefix
212 AND suffix = source_cn.suffix
213 AND owning_lib = source_cn.owning_lib
214 AND record = target_record
217 -- ... and if there's a conflicting one on the target ...
220 -- ... move the copies to that, and ...
222 SET call_number = target_cn.id
223 WHERE call_number = source_cn.id;
225 -- ... move V holds to the move-target call number
226 FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP
228 UPDATE action.hold_request
229 SET target = target_cn.id
232 moved_objects := moved_objects + 1;
235 UPDATE asset.call_number SET deleted = TRUE WHERE id = source_cn.id;
239 -- ... just move the call number to the target record
240 UPDATE asset.call_number
241 SET record = target_record
242 WHERE id = source_cn.id;
245 moved_objects := moved_objects + 1;
248 -- Find T holds targeting the source record ...
249 FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP
251 -- ... and move them to the target record
252 UPDATE action.hold_request
253 SET target = target_record
256 moved_objects := moved_objects + 1;
259 -- Find serial records targeting the source record ...
260 FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP
261 -- ... and move them to the target record
262 UPDATE serial.record_entry
263 SET record = target_record
264 WHERE id = ser_rec.id;
266 moved_objects := moved_objects + 1;
269 -- Find serial subscriptions targeting the source record ...
270 FOR ser_sub IN SELECT * FROM serial.subscription WHERE record_entry = source_record LOOP
271 -- ... and move them to the target record
272 UPDATE serial.subscription
273 SET record_entry = target_record
274 WHERE id = ser_sub.id;
276 moved_objects := moved_objects + 1;
279 -- Find booking resource types targeting the source record ...
280 FOR booking IN SELECT * FROM booking.resource_type WHERE record = source_record LOOP
281 -- ... and move them to the target record
282 UPDATE booking.resource_type
283 SET record = target_record
284 WHERE id = booking.id;
286 moved_objects := moved_objects + 1;
289 -- Find acq lineitems targeting the source record ...
290 FOR acq_lineitem IN SELECT * FROM acq.lineitem WHERE eg_bib_id = source_record LOOP
291 -- ... and move them to the target record
293 SET eg_bib_id = target_record
294 WHERE id = acq_lineitem.id;
296 moved_objects := moved_objects + 1;
299 -- Find acq user purchase requests targeting the source record ...
300 FOR acq_request IN SELECT * FROM acq.user_request WHERE eg_bib = source_record LOOP
301 -- ... and move them to the target record
302 UPDATE acq.user_request
303 SET eg_bib = target_record
304 WHERE id = acq_request.id;
306 moved_objects := moved_objects + 1;
309 -- Find parts attached to the source ...
310 FOR source_part IN SELECT * FROM biblio.monograph_part WHERE record = source_record LOOP
312 SELECT INTO target_part *
313 FROM biblio.monograph_part
314 WHERE label = source_part.label
315 AND record = target_record;
317 -- ... and if there's a conflicting one on the target ...
320 -- ... move the copy-part maps to that, and ...
321 UPDATE asset.copy_part_map
322 SET part = target_part.id
323 WHERE part = source_part.id;
325 -- ... move P holds to the move-target part
326 FOR hold IN SELECT * FROM action.hold_request WHERE target = source_part.id AND hold_type = 'P' LOOP
328 UPDATE action.hold_request
329 SET target = target_part.id
332 moved_objects := moved_objects + 1;
337 -- ... just move the part to the target record
338 UPDATE biblio.monograph_part
339 SET record = target_record
340 WHERE id = source_part.id;
343 moved_objects := moved_objects + 1;
346 -- Find multi_home items attached to the source ...
347 FOR multi_home IN SELECT * FROM biblio.peer_bib_copy_map WHERE peer_record = source_record LOOP
348 -- ... and move them to the target record
349 UPDATE biblio.peer_bib_copy_map
350 SET peer_record = target_record
351 WHERE id = multi_home.id;
353 moved_objects := moved_objects + 1;
356 -- And delete mappings where the item's home bib was merged with the peer bib
357 DELETE FROM biblio.peer_bib_copy_map WHERE peer_record = (
358 SELECT (SELECT record FROM asset.call_number WHERE id = call_number)
359 FROM asset.copy WHERE id = target_copy
362 -- Apply merge tracking
363 UPDATE biblio.record_entry
364 SET merge_date = NOW() WHERE id = target_record;
366 UPDATE biblio.record_entry
367 SET merge_date = NOW(), merged_to = target_record
368 WHERE id = source_record;
370 -- replace book bag entries of source_record with target_record
371 UPDATE container.biblio_record_entry_bucket_item
372 SET target_biblio_record_entry = target_record
373 WHERE bucket IN (SELECT id FROM container.biblio_record_entry_bucket WHERE btype = 'bookbag')
374 AND target_biblio_record_entry = source_record;
376 -- Finally, "delete" the source record
377 UPDATE biblio.record_entry SET active = FALSE WHERE id = source_record;
378 DELETE FROM biblio.record_entry WHERE id = source_record;
380 -- That's all, folks!
381 RETURN moved_objects;
383 $func$ LANGUAGE plpgsql;
386 SELECT evergreen.upgrade_deps_block_check('1215', :eg_version);
388 INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
390 'eg.orgselect.cat.catalog.wide_holds', 'gui', 'integer',
392 'eg.orgselect.cat.catalog.wide_holds',
393 'Default org unit for catalog holds org unit selector',
401 SELECT evergreen.upgrade_deps_block_check('1216', :eg_version);
403 INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
405 'eg.orgselect.patron.search', 'gui', 'integer',
407 'eg.orgselect.patron.search',
408 'Default org unit for patron search',
416 SELECT evergreen.upgrade_deps_block_check('1217', :eg_version);
418 INSERT INTO config.workstation_setting_type
419 (name, grp, datatype, label)
421 'eg.acq.search.default.lineitems', 'gui', 'object',
423 'eg.acq.search.default.lineitems',
424 'Acquisitions Default Search: Lineitems',
427 'eg.acq.search.default.purchaseorders', 'gui', 'object',
429 'eg.acq.search.default.purchaseorders',
430 'Acquisitions Default Search: Purchase Orders',
433 'eg.acq.search.default.invoices', 'gui', 'object',
435 'eg.acq.search.default.invoices',
436 'Acquisitions Default Search: Invoices',
439 'eg.acq.search.default.selectionlists', 'gui', 'object',
441 'eg.acq.search.default.selectionlists',
442 'Acquisitions Default Search: Selection Lists',
446 INSERT INTO config.workstation_setting_type
447 (name, grp, datatype, label)
449 'eg.acq.search.lineitems.run_immediately', 'gui', 'bool',
451 'eg.acq.search.lineitems.run_immediately',
452 'Acquisitions Search: Immediately Search Lineitems',
455 'eg.acq.search.purchaseorders.run_immediately', 'gui', 'bool',
457 'eg.acq.search.purchaseorders.run_immediately',
458 'Acquisitions Search: Immediately Search Purchase Orders',
461 'eg.acq.search.invoices.run_immediately', 'gui', 'bool',
463 'eg.acq.search.invoices.run_immediately',
464 'Acquisitions Search: Immediately Search Invoices',
467 'eg.acq.search.selectionlists.run_immediately', 'gui', 'bool',
469 'eg.acq.search.selectionlists.run_immediately',
470 'Acquisitions Search: Immediately Search Selection Lists',
474 INSERT INTO config.workstation_setting_type
475 (name, grp, datatype, label)
477 'eg.grid.acq.search.lineitems', 'gui', 'object',
479 'eg.grid.acq.search.lineitems',
480 'Grid Config: acq.search.lineitems',
483 'eg.grid.acq.search.purchaseorders', 'gui', 'object',
485 'eg.grid.acq.search.purchaseorders',
486 'Grid Config: acq.search.purchaseorders',
489 'eg.grid.acq.search.selectionlists', 'gui', 'object',
491 'eg.grid.acq.search.selectionlists',
492 'Grid Config: acq.search.selectionlists',
495 'eg.grid.acq.search.invoices', 'gui', 'object',
497 'eg.grid.acq.search.invoices',
498 'Grid Config: acq.search.invoices',
503 SELECT evergreen.upgrade_deps_block_check('1218', :eg_version);
505 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
507 pgt.id, perm.id, aout.depth, TRUE
509 permission.grp_tree pgt,
510 permission.perm_list perm,
511 actor.org_unit_type aout
513 pgt.name = 'Acquisitions Administrator' AND
514 aout.name = 'Consortium' AND
517 'VIEW_FUNDING_SOURCE',
518 'VIEW_FUND_ALLOCATION',
521 'VIEW_PURCHASE_ORDER',
524 'ACQ_ADD_LINEITEM_IDENTIFIER',
525 'ACQ_SET_LINEITEM_IDENTIFIER',
528 'CREATE_PURCHASE_ORDER',
529 'IMPORT_ACQ_LINEITEM_BIB_RECORD',
530 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
533 'MANAGE_FUNDING_SOURCE',
534 'RECEIVE_PURCHASE_ORDER',
535 'ADMIN_ACQ_LINEITEM_ALERT_TEXT',
536 'UPDATE_FUNDING_SOURCE',
538 'VIEW_IMPORT_MATCH_SET',
539 'VIEW_MERGE_PROFILE',
544 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
546 pgt.id, perm.id, aout.depth, FALSE
548 permission.grp_tree pgt,
549 permission.perm_list perm,
550 actor.org_unit_type aout
552 pgt.name = 'Acquisitions' AND
553 aout.name = 'Consortium' AND
555 'ACQ_ADD_LINEITEM_IDENTIFIER',
556 'ACQ_SET_LINEITEM_IDENTIFIER',
559 'ACQ_INVOICE-REOPEN',
560 'ADMIN_ACQ_DISTRIB_FORMULA',
562 'IMPORT_ACQ_LINEITEM_BIB_RECORD_UPLOAD',
563 'VIEW_IMPORT_MATCH_SET',
568 SELECT evergreen.upgrade_deps_block_check('1219', :eg_version);
570 CREATE VIEW acq.li_state_label AS
574 ('selector-ready', 'Selector-Ready'),
575 ('order-ready', 'Order-Ready'),
576 ('approved', 'Approved'),
577 ('pending-order', 'Pending-Order'),
578 ('on-order', 'On-Order'),
579 ('received', 'Received'),
580 ('cancelled', 'Cancelled')
583 CREATE VIEW acq.po_state_label AS
587 ('pending', 'Pending'),
588 ('on-order', 'On-Order'),
589 ('received', 'Received'),
590 ('cancelled', 'Cancelled')
594 SELECT evergreen.upgrade_deps_block_check('1220', :eg_version);
596 INSERT into config.org_unit_setting_type
597 ( name, grp, label, description, datatype, fm_class ) VALUES
598 ( 'circ.holds.calculated_age_proximity', 'circ',
599 oils_i18n_gettext('circ.holds.calculated_age_proximity',
600 'Use calculated proximity for age-protection check',
602 oils_i18n_gettext('circ.holds.calculated_age_proximity',
603 'When checking whether a copy is viable for a hold based on transit distance, use calculated proximity with adjustments rather than baseline Org Unit proximity.',
604 'coust', 'description'),
609 SELECT evergreen.upgrade_deps_block_check('1221', :eg_version);
611 CREATE OR REPLACE FUNCTION action.copy_calculated_proximity(
618 ) RETURNS NUMERIC AS $f$
620 baseline_prox NUMERIC;
621 aoupa actor.org_unit_proximity_adjustment%ROWTYPE;
624 -- First, gather the baseline proximity of "here" to pickup lib
625 SELECT prox INTO baseline_prox FROM actor.org_unit_proximity WHERE from_org = vacp_cl AND to_org = pickup;
627 -- Find any absolute adjustments, and set the baseline prox to that
628 SELECT adj.* INTO aoupa
629 FROM actor.org_unit_proximity_adjustment adj
630 LEFT JOIN actor.org_unit_ancestors_distance(vacp_cl) acp_cl ON (acp_cl.id = adj.item_circ_lib)
631 LEFT JOIN actor.org_unit_ancestors_distance(vacn_ol) acn_ol ON (acn_ol.id = adj.item_owning_lib)
632 LEFT JOIN actor.org_unit_ancestors_distance(vacl_ol) acl_ol ON (acl_ol.id = adj.copy_location)
633 LEFT JOIN actor.org_unit_ancestors_distance(pickup) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
634 LEFT JOIN actor.org_unit_ancestors_distance(request) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
635 WHERE (adj.circ_mod IS NULL OR adj.circ_mod = vacp_cm) AND
636 (adj.item_circ_lib IS NULL OR adj.item_circ_lib = acp_cl.id) AND
637 (adj.item_owning_lib IS NULL OR adj.item_owning_lib = acn_ol.id) AND
638 (adj.copy_location IS NULL OR adj.copy_location = acl_ol.id) AND
639 (adj.hold_pickup_lib IS NULL OR adj.hold_pickup_lib = ahr_pl.id) AND
640 (adj.hold_request_lib IS NULL OR adj.hold_request_lib = ahr_rl.id) AND
641 absolute_adjustment AND
642 COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
644 COALESCE(acp_cl.distance,999)
645 + COALESCE(acn_ol.distance,999)
646 + COALESCE(acl_ol.distance,999)
647 + COALESCE(ahr_pl.distance,999)
648 + COALESCE(ahr_rl.distance,999),
653 baseline_prox := aoupa.prox_adjustment;
656 -- Now find any relative adjustments, and change the baseline prox based on them
659 FROM actor.org_unit_proximity_adjustment adj
660 LEFT JOIN actor.org_unit_ancestors_distance(vacp_cl) acp_cl ON (acp_cl.id = adj.item_circ_lib)
661 LEFT JOIN actor.org_unit_ancestors_distance(vacn_ol) acn_ol ON (acn_ol.id = adj.item_owning_lib)
662 LEFT JOIN actor.org_unit_ancestors_distance(vacl_ol) acl_ol ON (acn_ol.id = adj.copy_location)
663 LEFT JOIN actor.org_unit_ancestors_distance(pickup) ahr_pl ON (ahr_pl.id = adj.hold_pickup_lib)
664 LEFT JOIN actor.org_unit_ancestors_distance(request) ahr_rl ON (ahr_rl.id = adj.hold_request_lib)
665 WHERE (adj.circ_mod IS NULL OR adj.circ_mod = vacp_cm) AND
666 (adj.item_circ_lib IS NULL OR adj.item_circ_lib = acp_cl.id) AND
667 (adj.item_owning_lib IS NULL OR adj.item_owning_lib = acn_ol.id) AND
668 (adj.copy_location IS NULL OR adj.copy_location = acl_ol.id) AND
669 (adj.hold_pickup_lib IS NULL OR adj.hold_pickup_lib = ahr_pl.id) AND
670 (adj.hold_request_lib IS NULL OR adj.hold_request_lib = ahr_rl.id) AND
671 NOT absolute_adjustment AND
672 COALESCE(acp_cl.id, acn_ol.id, acl_ol.id, ahr_pl.id, ahr_rl.id) IS NOT NULL
674 baseline_prox := baseline_prox + aoupa.prox_adjustment;
677 RETURN baseline_prox;
679 $f$ LANGUAGE PLPGSQL;
681 CREATE OR REPLACE FUNCTION action.hold_copy_calculated_proximity(
684 copy_context_ou INT DEFAULT NULL
685 -- TODO maybe? hold_context_ou INT DEFAULT NULL. This would optionally
686 -- support an "ahprox" measurement: adjust prox between copy circ lib and
687 -- hold request lib, but I'm unsure whether to use this theoretical
688 -- argument only in the baseline calculation or later in the other
689 -- queries in this function.
690 ) RETURNS NUMERIC AS $f$
692 ahr action.hold_request%ROWTYPE;
693 acp asset.copy%ROWTYPE;
694 acn asset.call_number%ROWTYPE;
695 acl asset.copy_location%ROWTYPE;
700 SELECT * INTO ahr FROM action.hold_request WHERE id = ahr_id;
701 SELECT * INTO acp FROM asset.copy WHERE id = acp_id;
702 SELECT * INTO acn FROM asset.call_number WHERE id = acp.call_number;
703 SELECT * INTO acl FROM asset.copy_location WHERE id = acp.location;
705 IF copy_context_ou IS NULL THEN
706 copy_context_ou := acp.circ_lib;
709 SELECT action.copy_calculated_proximity(
720 $f$ LANGUAGE PLPGSQL;
722 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$
725 user_object actor.usr%ROWTYPE;
726 age_protect_object config.rule_age_hold_protect%ROWTYPE;
727 standing_penalty config.standing_penalty%ROWTYPE;
728 transit_range_ou_type actor.org_unit_type%ROWTYPE;
729 transit_source actor.org_unit%ROWTYPE;
730 item_object asset.copy%ROWTYPE;
731 item_cn_object asset.call_number%ROWTYPE;
732 item_status_object config.copy_status%ROWTYPE;
733 item_location_object asset.copy_location%ROWTYPE;
734 ou_skip actor.org_unit_setting%ROWTYPE;
735 calc_age_prox actor.org_unit_setting%ROWTYPE;
736 result action.matrix_test_result;
737 hold_test config.hold_matrix_matchpoint%ROWTYPE;
738 use_active_date TEXT;
740 age_protect_date TIMESTAMP WITH TIME ZONE;
742 hold_transit_prox NUMERIC;
743 frozen_hold_count INT;
744 context_org_list INT[];
747 v_pickup_ou ALIAS FOR pickup_ou;
748 v_request_ou ALIAS FOR request_ou;
752 SELECT INTO user_object * FROM actor.usr WHERE id = match_user;
753 SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( v_pickup_ou );
755 result.success := TRUE;
757 -- The HOLD penalty block only applies to new holds.
758 -- The CAPTURE penalty block applies to existing holds.
759 hold_penalty := 'HOLD';
761 hold_penalty := 'CAPTURE';
764 -- Fail if we couldn't find a user
765 IF user_object.id IS NULL THEN
766 result.fail_part := 'no_user';
767 result.success := FALSE;
773 SELECT INTO item_object * FROM asset.copy WHERE id = match_item;
775 -- Fail if we couldn't find a copy
776 IF item_object.id IS NULL THEN
777 result.fail_part := 'no_item';
778 result.success := FALSE;
784 SELECT INTO matchpoint_id action.find_hold_matrix_matchpoint(v_pickup_ou, v_request_ou, match_item, match_user, match_requestor);
785 result.matchpoint := matchpoint_id;
787 SELECT INTO ou_skip * FROM actor.org_unit_setting WHERE name = 'circ.holds.target_skip_me' AND org_unit = item_object.circ_lib;
789 -- Fail if the circ_lib for the item has circ.holds.target_skip_me set to true
790 IF ou_skip.id IS NOT NULL AND ou_skip.value = 'true' THEN
791 result.fail_part := 'circ.holds.target_skip_me';
792 result.success := FALSE;
798 -- Fail if user is barred
799 IF user_object.barred IS TRUE THEN
800 result.fail_part := 'actor.usr.barred';
801 result.success := FALSE;
807 SELECT INTO item_cn_object * FROM asset.call_number WHERE id = item_object.call_number;
808 SELECT INTO item_status_object * FROM config.copy_status WHERE id = item_object.status;
809 SELECT INTO item_location_object * FROM asset.copy_location WHERE id = item_object.location;
811 -- Fail if we couldn't find any matchpoint (requires a default)
812 IF matchpoint_id IS NULL THEN
813 result.fail_part := 'no_matchpoint';
814 result.success := FALSE;
820 SELECT INTO hold_test * FROM config.hold_matrix_matchpoint WHERE id = matchpoint_id;
822 IF hold_test.holdable IS FALSE THEN
823 result.fail_part := 'config.hold_matrix_test.holdable';
824 result.success := FALSE;
829 IF item_object.holdable IS FALSE THEN
830 result.fail_part := 'item.holdable';
831 result.success := FALSE;
836 IF item_status_object.holdable IS FALSE THEN
837 result.fail_part := 'status.holdable';
838 result.success := FALSE;
843 IF item_location_object.holdable IS FALSE THEN
844 result.fail_part := 'location.holdable';
845 result.success := FALSE;
850 IF hold_test.transit_range IS NOT NULL THEN
851 SELECT INTO transit_range_ou_type * FROM actor.org_unit_type WHERE id = hold_test.transit_range;
852 IF hold_test.distance_is_from_owner THEN
853 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;
855 SELECT INTO transit_source * FROM actor.org_unit WHERE id = item_object.circ_lib;
858 PERFORM * FROM actor.org_unit_descendants( transit_source.id, transit_range_ou_type.depth ) WHERE id = v_pickup_ou;
861 result.fail_part := 'transit_range';
862 result.success := FALSE;
868 -- Proximity of user's home_ou to the pickup_lib to see if penalty should be ignored.
869 SELECT INTO pickup_prox prox FROM actor.org_unit_proximity WHERE from_org = user_object.home_ou AND to_org = v_pickup_ou;
870 -- Proximity of user's home_ou to the items' lib to see if penalty should be ignored.
871 IF hold_test.distance_is_from_owner THEN
872 SELECT INTO item_prox prox FROM actor.org_unit_proximity WHERE from_org = user_object.home_ou AND to_org = item_cn_object.owning_lib;
874 SELECT INTO item_prox prox FROM actor.org_unit_proximity WHERE from_org = user_object.home_ou AND to_org = item_object.circ_lib;
877 FOR standing_penalty IN
878 SELECT DISTINCT csp.*
879 FROM actor.usr_standing_penalty usp
880 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
881 WHERE usr = match_user
882 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
883 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
884 AND (csp.ignore_proximity IS NULL OR csp.ignore_proximity < item_prox
885 OR csp.ignore_proximity < pickup_prox)
886 AND csp.block_list LIKE '%' || hold_penalty || '%' LOOP
888 result.fail_part := standing_penalty.name;
889 result.success := FALSE;
894 IF hold_test.stop_blocked_user IS TRUE THEN
895 FOR standing_penalty IN
896 SELECT DISTINCT csp.*
897 FROM actor.usr_standing_penalty usp
898 JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
899 WHERE usr = match_user
900 AND usp.org_unit IN ( SELECT * FROM unnest(context_org_list) )
901 AND (usp.stop_date IS NULL or usp.stop_date > NOW())
902 AND csp.block_list LIKE '%CIRC%' LOOP
904 result.fail_part := standing_penalty.name;
905 result.success := FALSE;
911 IF hold_test.max_holds IS NOT NULL AND NOT retargetting THEN
912 SELECT INTO hold_count COUNT(*)
913 FROM action.hold_request
914 WHERE usr = match_user
915 AND fulfillment_time IS NULL
916 AND cancel_time IS NULL
917 AND CASE WHEN hold_test.include_frozen_holds THEN TRUE ELSE frozen IS FALSE END;
919 IF hold_count >= hold_test.max_holds THEN
920 result.fail_part := 'config.hold_matrix_test.max_holds';
921 result.success := FALSE;
927 IF item_object.age_protect IS NOT NULL THEN
928 SELECT INTO age_protect_object * FROM config.rule_age_hold_protect WHERE id = item_object.age_protect;
929 IF hold_test.distance_is_from_owner THEN
930 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_cn_object.owning_lib);
932 SELECT INTO use_active_date value FROM actor.org_unit_ancestor_setting('circ.holds.age_protect.active_date', item_object.circ_lib);
934 IF use_active_date = 'true' THEN
935 age_protect_date := COALESCE(item_object.active_date, NOW());
937 age_protect_date := item_object.create_date;
939 IF age_protect_date + age_protect_object.age > NOW() THEN
940 SELECT INTO calc_age_prox * FROM actor.org_unit_setting WHERE name = 'circ.holds.calculated_age_proximity' AND org_unit = item_object.circ_lib;
941 IF hold_test.distance_is_from_owner THEN
942 prox_ou := item_cn_object.owning_lib;
944 prox_ou := item_object.circ_lib;
946 IF calc_age_prox.id IS NOT NULL AND calc_age_prox.value = 'true' THEN
947 SELECT INTO hold_transit_prox action.copy_calculated_proximity(
951 item_object.circ_modifier,
952 item_cn_object.owning_lib,
953 item_location_object.owning_lib
956 SELECT INTO hold_transit_prox prox::NUMERIC FROM actor.org_unit_proximity WHERE from_org = prox_ou AND to_org = v_pickup_ou;
959 IF hold_transit_prox > age_protect_object.prox::NUMERIC THEN
960 result.fail_part := 'config.rule_age_hold_protect.prox';
961 result.success := FALSE;
974 $func$ LANGUAGE plpgsql;
978 SELECT evergreen.upgrade_deps_block_check('1222', :eg_version);
980 INSERT INTO action_trigger.reactor (module, description) VALUES (
981 'CallHTTP', 'Push event information out to an external system via HTTP'
984 INSERT INTO action_trigger.hook (key, core_type, description, passive) VALUES (
985 'bre.edit', 'bre', 'A bib record was edited', FALSE
990 SELECT evergreen.upgrade_deps_block_check('1223', :eg_version);
992 -- First, normalize the au.create[d] and au.update[d] hooks. The code and seed data differ.
994 INSERT INTO action_trigger.hook (key, core_type, description, passive)
995 VALUES ('au.created', 'au', 'A user was created', 't') ON CONFLICT DO NOTHING;
996 INSERT INTO action_trigger.hook (key, core_type, description, passive)
997 VALUES ('au.updated', 'au', 'A user was updated', 't') ON CONFLICT DO NOTHING;
1000 UPDATE action_trigger.event_definition SET hook = 'au.created' WHERE hook = 'au.create';
1001 UPDATE action_trigger.event_definition SET hook = 'au.updated' WHERE hook = 'au.update';
1003 DELETE FROM action_trigger.hook WHERE key = 'au.create';
1004 DELETE FROM action_trigger.hook WHERE key = 'au.update';
1006 -- Now the entirely new ones...
1007 INSERT INTO action_trigger.hook (key, core_type, description, passive)
1008 VALUES ('au.renewed', 'au', 'A user was renewed by having their expire date changed', 't');
1010 INSERT INTO action_trigger.hook (key, core_type, description, passive)
1011 VALUES ('au.barcode_changed', 'au', 'A card was updated or created for an existing user', 't');
1014 SELECT evergreen.upgrade_deps_block_check('1224', :eg_version);
1016 INSERT INTO config.coded_value_map (id,ctype,code,opac_visible,is_simple,value,search_label) VALUES
1017 (1736,'icon_format','preloadedaudio',TRUE,FALSE,
1018 oils_i18n_gettext(1736, 'Preloaded Audio', 'ccvm', 'value'),
1019 oils_i18n_gettext(1736, 'Preloaded Audio', 'ccvm', 'search_label')),
1020 (1737,'search_format','preloadedaudio',TRUE,FALSE,
1021 oils_i18n_gettext(1737, 'Preloaded Audio', 'ccvm', 'value'),
1022 oils_i18n_gettext(1737, 'Preloaded Audio', 'ccvm', 'search_label'))
1025 INSERT INTO config.composite_attr_entry_definition (coded_value, definition) VALUES
1026 ((SELECT id from config.coded_value_map where ctype = 'search_format' AND code = 'preloadedaudio'),'{"0":{"_attr":"item_type","_val":"i"},"1":{"_attr":"item_form","_val":"q"}}'),
1027 ((SELECT id from config.coded_value_map where ctype = 'icon_format' AND code = 'preloadedaudio'),'{"0":{"_attr":"item_type","_val":"i"},"1":{"_attr":"item_form","_val":"q"}}');
1031 SELECT evergreen.upgrade_deps_block_check('1225', :eg_version);
1033 ALTER TABLE acq.provider ADD COLUMN primary_contact INT;
1034 ALTER TABLE acq.provider ADD CONSTRAINT acq_provider_primary_contact_fkey FOREIGN KEY (primary_contact) REFERENCES acq.provider_contact (id) ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
1037 SELECT evergreen.upgrade_deps_block_check('1226', :eg_version);
1039 INSERT INTO config.workstation_setting_type
1040 (name, grp, datatype, label)
1042 'eg.grid.acq.provider.addresses', 'gui', 'object',
1044 'eg.grid.acq.provider.addresses',
1045 'Grid Config: acq.provider.addresses',
1048 'eg.grid.acq.provider.attributes', 'gui', 'object',
1050 'eg.grid.acq.provider.attributes',
1051 'Grid Config: acq.provider.attributes',
1054 'eg.grid.acq.provider.contact.addresses', 'gui', 'object',
1056 'eg.grid.acq.provider.contact.addresses',
1057 'Grid Config: acq.provider.contact.addresses',
1060 'eg.grid.acq.provider.contacts', 'gui', 'object',
1062 'eg.grid.acq.provider.contacts',
1063 'Grid Config: acq.provider.contacts',
1066 'eg.grid.acq.provider.edi_accounts', 'gui', 'object',
1068 'eg.grid.acq.provider.edi_accounts',
1069 'Grid Config: acq.provider.edi_accounts',
1072 'eg.grid.acq.provider.edi_messages', 'gui', 'object',
1074 'eg.grid.acq.provider.edi_messages',
1075 'Grid Config: acq.provider.edi_messages',
1078 'eg.grid.acq.provider.holdings', 'gui', 'object',
1080 'eg.grid.acq.provider.holdings',
1081 'Grid Config: acq.provider.holdings',
1084 'eg.grid.acq.provider.invoices', 'gui', 'object',
1086 'eg.grid.acq.provider.invoices',
1087 'Grid Config: acq.provider.invoices',
1090 'eg.grid.acq.provider.purchaseorders', 'gui', 'object',
1092 'eg.grid.acq.provider.purchaseorders',
1093 'Grid Config: acq.provider.purchaseorders',
1096 'eg.grid.acq.provider.search.results', 'gui', 'object',
1098 'eg.grid.acq.provider.search.results',
1099 'Grid Config: acq.provider.search.results',
1104 SELECT evergreen.upgrade_deps_block_check('1227', :eg_version);
1106 INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
1108 'eg.grid.cat.authority.browse', 'gui', 'object',
1110 'eg.grid.cat.authority.browse',
1111 'Grid Config: eg.grid.cat.authority.browse',
1114 'eg.grid.cat.authority.manage.bibs', 'gui', 'object',
1116 'eg.grid.cat.authority.manage.bibs',
1117 'Grid Config: eg.grid.cat.authority.manage.bibs',
1122 SELECT evergreen.upgrade_deps_block_check('1228', :eg_version);
1124 CREATE OR REPLACE FUNCTION actor.org_unit_full_path ( INT ) RETURNS SETOF actor.org_unit AS $$
1126 FROM actor.org_unit AS aou
1128 (SELECT au.id, t.depth FROM actor.org_unit_ancestors($1) AS au JOIN actor.org_unit_type t ON (au.ou_type = t.id))
1130 (SELECT au.id, t.depth FROM actor.org_unit_descendants($1) AS au JOIN actor.org_unit_type t ON (au.ou_type = t.id))
1131 ) AS ad ON (aou.id=ad.id)
1133 $$ LANGUAGE SQL STABLE;
1137 SELECT evergreen.upgrade_deps_block_check('1229', :eg_version);
1140 INSERT into action_trigger.hook (key, core_type, description) VALUES (
1141 'au.email.test', 'au', 'A test email has been requested for this user'
1144 'au.sms_text.test', 'au', 'A test SMS has been requested for this user'
1147 INSERT INTO action_trigger.event_definition (active, owner, name, hook, validator, reactor, delay, template)
1149 't', 1, 'Send Test Email', 'au.email.test', 'NOOP_True', 'SendEmail', '00:01:00',
1152 [%- user = target -%]
1153 [%- lib = target.home_ou -%]
1154 To: [%- user.email %]
1155 From: [%- helpers.get_org_setting(target.home_ou.id, 'org.bounced_emails') || lib.email || params.sender_email || default_sender %]
1156 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
1157 Reply-To: [%- lib.email || params.sender_email || default_sender %]
1158 Subject: Email Test Notification
1159 Auto-Submitted: auto-generated
1161 Dear [% user.first_given_name %] [% user.family_name %],
1163 This is a test of the email associated with your account at [%- lib.name -%]. If you are receiving this message, your email information is correct.
1168 Contact your library for more information:
1171 [%- SET addr = lib.mailing_address -%]
1172 [%- IF !addr -%] [%- SET addr = lib.billing_address -%] [%- END %]
1173 [% addr.street1 %] [% addr.street2 %]
1174 [% addr.city %], [% addr.state %]
1175 [% addr.post_code %]
1179 INSERT INTO action_trigger.environment (event_def, path)
1180 VALUES (currval('action_trigger.event_definition_id_seq'), 'home_ou'),
1181 (currval('action_trigger.event_definition_id_seq'), 'home_ou.mailing_address'),
1182 (currval('action_trigger.event_definition_id_seq'), 'home_ou.billing_address');
1184 INSERT INTO action_trigger.event_definition (active, owner, name, hook, validator, reactor, delay, template)
1186 't', 1, 'Send Test SMS', 'au.sms_text.test', 'NOOP_True', 'SendSMS', '00:01:00',
1189 [%- user = target -%]
1190 [%- lib = user.home_ou -%]
1191 [%- sms_number = helpers.get_user_setting(target.id, 'opac.default_sms_notify') -%]
1192 [%- sms_carrier = helpers.get_user_setting(target.id, 'opac.default_sms_carrier') -%]
1193 From: [%- helpers.get_org_setting(target.home_ou.id, 'org.bounced_emails') || lib.email || params.sender_email || default_sender %]
1194 To: [%- helpers.get_sms_gateway_email(sms_carrier,sms_number) %]
1195 Subject: Test Text Message
1197 This is a test confirming your mobile number for [% lib.name %] is correct.
1202 Contact your library for more information:
1205 [%- SET addr = lib.mailing_address -%]
1206 [%- IF !addr -%] [%- SET addr = lib.billing_address -%] [%- END %]
1207 [% addr.street1 %] [% addr.street2 %]
1208 [% addr.city %], [% addr.state %]
1209 [% addr.post_code %]
1213 INSERT INTO action_trigger.environment (event_def, path)
1214 VALUES (currval('action_trigger.event_definition_id_seq'), 'home_ou'),
1215 (currval('action_trigger.event_definition_id_seq'), 'home_ou.mailing_address'),
1216 (currval('action_trigger.event_definition_id_seq'), 'home_ou.billing_address');
1220 SELECT evergreen.upgrade_deps_block_check('1230', :eg_version);
1222 INSERT INTO permission.perm_list
1223 ( id, code, description )
1225 623, 'UPDATE_ORG_UNIT_SETTING.opac.matomo', oils_i18n_gettext(623,
1226 'Allows a user to configure Matomo Analytics org unit settings', 'ppl', 'description')
1229 INSERT into config.org_unit_setting_type
1230 ( name, grp, label, description, datatype, update_perm )
1232 'opac.analytics.matomo_id', 'opac',
1234 'opac.analytics.matomo_id',
1237 oils_i18n_gettext('opac.analytics.matomo_id',
1238 'The Site ID for your Evergreen catalog. You can find the Site ID in the tracking code you got from Matomo.',
1239 'coust', 'description'),
1242 'opac.analytics.matomo_url', 'opac',
1243 oils_i18n_gettext('opac.analytics.matomo_url',
1246 oils_i18n_gettext('opac.analytics.matomo_url',
1247 'The URL for your the Matomo software. Be sure to include the trailing slash, e.g. https://my-evergreen.matomo.cloud/',
1248 'coust', 'description'),
1254 SELECT evergreen.upgrade_deps_block_check('1231', :eg_version);
1256 INSERT into config.org_unit_setting_type
1257 ( name, grp, label, description, datatype, fm_class ) VALUES
1258 ( 'opac.email_record.allow_without_login', 'opac',
1259 oils_i18n_gettext('opac.email_record.allow_without_login',
1260 'Allow record emailing without login',
1262 oils_i18n_gettext('opac.email_record.allow_without_login',
1263 'Instead of forcing a patron to log in in order to email the details of a record, just challenge them with a simple catpcha.',
1264 'coust', 'description'),
1268 CREATE TABLE action_trigger.event_def_group (
1269 id SERIAL PRIMARY KEY,
1270 owner INT NOT NULL REFERENCES actor.org_unit (id)
1271 ON DELETE RESTRICT ON UPDATE CASCADE
1272 DEFERRABLE INITIALLY DEFERRED,
1273 hook TEXT NOT NULL REFERENCES action_trigger.hook (key)
1274 ON DELETE RESTRICT ON UPDATE CASCADE
1275 DEFERRABLE INITIALLY DEFERRED,
1276 active BOOL NOT NULL DEFAULT TRUE,
1279 SELECT SETVAL('action_trigger.event_def_group_id_seq'::TEXT, 100, TRUE);
1281 CREATE TABLE action_trigger.event_def_group_member (
1282 id SERIAL PRIMARY KEY,
1283 grp INT NOT NULL REFERENCES action_trigger.event_def_group (id)
1284 ON DELETE CASCADE ON UPDATE CASCADE
1285 DEFERRABLE INITIALLY DEFERRED,
1286 event_def INT NOT NULL REFERENCES action_trigger.event_definition (id)
1287 ON DELETE RESTRICT ON UPDATE CASCADE
1288 DEFERRABLE INITIALLY DEFERRED,
1289 sortable BOOL NOT NULL DEFAULT TRUE,
1290 holdings BOOL NOT NULL DEFAULT FALSE,
1291 external BOOL NOT NULL DEFAULT FALSE,
1295 INSERT INTO action_trigger.event_def_group (id, owner, hook, name)
1296 VALUES (1, 1, 'biblio.format.record_entry.print','Print Record(s)');
1298 INSERT INTO action_trigger.event_def_group (id, owner, hook, name)
1299 VALUES (2,1,'biblio.format.record_entry.email','Email Record(s)');
1303 PERFORM * FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email' AND owner = 1 AND active AND template =
1306 [%- SET user = target.0.owner -%]
1307 To: [%- params.recipient_email || user.email %]
1308 From: [%- params.sender_email || default_sender %]
1309 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
1310 Subject: Bibliographic Records
1311 Auto-Submitted: auto-generated
1313 [% FOR cbreb IN target %]
1314 [% FOR item IN cbreb.items;
1315 bre_id = item.target_biblio_record_entry;
1317 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
1319 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
1320 title = title _ part.textContent;
1323 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
1324 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
1325 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
1326 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
1327 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
1328 issn = bibxml.findnodes('//*[@tag="022"]/*[@code="a"]').textContent;
1329 upc = bibxml.findnodes('//*[@tag="024"]/*[@code="a"]').textContent;
1332 [% loop.count %]/[% loop.size %]. Bib ID# [% bre_id %]
1333 [% IF isbn %]ISBN: [% isbn _ "\n" %][% END -%]
1334 [% IF issn %]ISSN: [% issn _ "\n" %][% END -%]
1335 [% IF upc %]UPC: [% upc _ "\n" %] [% END -%]
1337 Author: [% author %]
1338 Publication Info: [% publisher %] [% pubdate %]
1339 Item Type: [% item_type %]
1345 IF FOUND THEN -- update
1347 INSERT INTO action_trigger.event_def_group_member (grp, name, event_def)
1348 SELECT 2, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email';
1350 INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def)
1351 SELECT 2, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email';
1353 UPDATE action_trigger.event_definition SET template = $$
1355 [%- SET user = target.0.owner -%]
1356 To: [%- params.recipient_email || user_data.0.email || user.email %]
1357 From: [%- params.sender_email || default_sender %]
1358 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
1359 Subject: [%- user_data.0.subject || 'Bibliographic Records' %]
1360 Auto-Submitted: auto-generated
1362 [%- FOR cbreb IN target;
1364 flesh_list = '{mra';
1365 IF user_data.0.type == 'full';
1366 flesh_list = flesh_list _ ',holdings_xml,acp';
1367 IF params.holdings_limit;
1368 flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit;
1371 flesh_list = flesh_list _ '}';
1373 item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir);
1375 FOR item IN item_list -%]
1377 [% loop.count %]/[% loop.size %]. Bib ID# [% item.id %]
1378 [% IF item.isbn %]ISBN: [% item.isbn _ "\n" %][% END -%]
1379 [% IF item.issn %]ISSN: [% item.issn _ "\n" %][% END -%]
1380 [% IF item.upc %]UPC: [% item.upc _ "\n" %][% END -%]
1381 Title: [% item.title %]
1382 [% IF item.author %]Author: [% item.author _ "\n" %][% END -%]
1383 Publication Info: [% item.publisher %] [% item.pubdate %]
1384 Item Type: [% item.item_type %]
1385 [% IF user_data.0.type == 'full' && item.holdings.size == 0 %]
1386 * No items for this record at the selected location
1388 [% FOR cp IN item.holdings -%]
1389 * Library: [% cp.circ_lib %]
1390 Location: [% cp.location %]
1391 Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]
1392 [% IF cp.parts %] Parts: [% cp.parts _ "\n" %][% END -%]
1393 Status: [% cp.status_label %]
1394 Barcode: [% cp.barcode %]
1399 $$ WHERE hook = 'biblio.format.record_entry.email' AND owner = 1 AND active;
1401 ELSE -- insert full and add existing brief
1403 INSERT INTO action_trigger.event_def_group_member (grp, name, event_def)
1404 SELECT 2, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email' AND active;
1406 INSERT INTO action_trigger.event_definition (
1422 'biblio.record_entry.email.full',
1423 'biblio.format.record_entry.email',
1426 'DeleteTempBiblioBucket',
1427 'DeleteTempBiblioBucket',
1433 [%- SET user = target.0.owner -%]
1434 To: [%- params.recipient_email || user_data.0.email || user.email %]
1435 From: [%- params.sender_email || default_sender %]
1436 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
1437 Subject: [%- user_data.0.subject || 'Bibliographic Records' %]
1438 Auto-Submitted: auto-generated
1440 [%- FOR cbreb IN target;
1442 flesh_list = '{mra';
1443 IF user_data.0.type == 'full';
1444 flesh_list = flesh_list _ ',holdings_xml,acp';
1445 IF params.holdings_limit;
1446 flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit;
1449 flesh_list = flesh_list _ '}';
1451 item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir);
1453 FOR item IN item_list -%]
1455 [% loop.count %]/[% loop.size %]. Bib ID# [% item.id %]
1456 [% IF item.isbn %]ISBN: [% item.isbn _ "\n" %][% END -%]
1457 [% IF item.issn %]ISSN: [% item.issn _ "\n" %][% END -%]
1458 [% IF item.upc %]UPC: [% item.upc _ "\n" %][% END -%]
1459 Title: [% item.title %]
1460 [% IF item.author %]Author: [% item.author _ "\n" %][% END -%]
1461 Publication Info: [% item.publisher %] [% item.pubdate %]
1462 Item Type: [% item.item_type %]
1463 [% IF user_data.0.type == 'full' && item.holdings.size == 0 %]
1464 * No items for this record at the selected location
1466 [% FOR cp IN item.holdings -%]
1467 * Library: [% cp.circ_lib %]
1468 Location: [% cp.location %]
1469 Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]
1470 [% IF cp.parts %] Parts: [% cp.parts _ "\n" %][% END -%]
1471 Status: [% cp.status_label %]
1472 Barcode: [% cp.barcode %]
1477 $$ FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.email' AND active;
1479 INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def)
1480 SELECT 2, 'Full', TRUE, id FROM action_trigger.event_definition WHERE name = 'biblio.record_entry.email.full' and active;
1488 PERFORM * FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print' AND owner = 1 AND active AND template =
1491 <style> li { padding: 8px; margin 5px; }</style>
1493 [% FOR cbreb IN target %]
1494 [% FOR item IN cbreb.items;
1495 bre_id = item.target_biblio_record_entry;
1497 bibxml = helpers.unapi_bre(bre_id, {flesh => '{mra}'});
1499 FOR part IN bibxml.findnodes('//*[@tag="245"]/*[@code="a" or @code="b"]');
1500 title = title _ part.textContent;
1503 author = bibxml.findnodes('//*[@tag="100"]/*[@code="a"]').textContent;
1504 item_type = bibxml.findnodes('//*[local-name()="attributes"]/*[local-name()="field"][@name="item_type"]').getAttribute('coded-value');
1505 publisher = bibxml.findnodes('//*[@tag="260"]/*[@code="b"]').textContent;
1506 pubdate = bibxml.findnodes('//*[@tag="260"]/*[@code="c"]').textContent;
1507 isbn = bibxml.findnodes('//*[@tag="020"]/*[@code="a"]').textContent;
1511 Bib ID# [% bre_id %] ISBN: [% isbn %]<br />
1512 Title: [% title %]<br />
1513 Author: [% author %]<br />
1514 Publication Info: [% publisher %] [% pubdate %]<br/>
1515 Item Type: [% item_type %]
1523 IF FOUND THEN -- update
1525 INSERT INTO action_trigger.event_def_group_member (grp, name, event_def)
1526 SELECT 1, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print';
1528 INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def)
1529 SELECT 1, 'Full', TRUE, id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print';
1531 UPDATE action_trigger.event_definition SET template = $$
1533 <style> li { padding: 8px; margin 5px; }</style>
1535 [% FOR cbreb IN target;
1537 flesh_list = '{mra';
1538 IF user_data.0.type == 'full';
1539 flesh_list = flesh_list _ ',holdings_xml,acp';
1540 IF params.holdings_limit;
1541 flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit;
1544 flesh_list = flesh_list _ '}';
1546 item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir);
1547 FOR item IN item_list %]
1549 Bib ID# [% item.id %]<br />
1550 [% IF item.isbn %]ISBN: [% item.isbn %]<br />[% END %]
1551 [% IF item.issn %]ISSN: [% item.issn %]<br />[% END %]
1552 [% IF item.upc %]UPC: [% item.upc %]<br />[% END %]
1553 Title: [% item.title %]<br />
1554 [% IF item.author %] Author: [% item.author %]<br />[% END -%]
1555 Publication Info: [% item.publisher %] [% item.pubdate %]<br/>
1556 Item Type: [% item.item_type %]
1558 [% IF user_data.0.type == 'full' && item.holdings.size == 0 %]
1559 <li>No items for this record at the selected location</li>
1561 [% FOR cp IN item.holdings -%]
1563 Library: [% cp.circ_lib %]<br/>
1564 Location: [% cp.location %]<br/>
1565 Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]<br/>
1566 [% IF cp.parts %]Parts: [% cp.parts %]<br/>[% END %]
1567 Status: [% cp.status_label %]<br/>
1568 Barcode: [% cp.barcode %]
1577 $$ WHERE hook = 'biblio.format.record_entry.print' AND owner = 1 AND active;
1579 ELSE -- insert full and add brief
1581 INSERT INTO action_trigger.event_def_group_member (grp, name, event_def)
1582 SELECT 1, 'Brief', id FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print' AND active;
1584 INSERT INTO action_trigger.event_definition (
1600 'biblio.record_entry.print.full',
1601 'biblio.format.record_entry.print',
1604 'DeleteTempBiblioBucket',
1605 'DeleteTempBiblioBucket',
1611 <style> li { padding: 8px; margin 5px; }</style>
1613 [% FOR cbreb IN target;
1615 flesh_list = '{mra';
1616 IF user_data.0.type == 'full';
1617 flesh_list = flesh_list _ ',holdings_xml,acp';
1618 IF params.holdings_limit;
1619 flimit = 'acn=>' _ params.holdings_limit _ ',acp=>' _ params.holdings_limit;
1622 flesh_list = flesh_list _ '}';
1624 item_list = helpers.sort_bucket_unapi_bre(cbreb.items,{flesh => flesh_list, site => user_data.0.context_org, flesh_limit => flimit}, user_data.0.sort_by, user_data.0.sort_dir);
1625 FOR item IN item_list %]
1627 Bib ID# [% item.id %]<br />
1628 [% IF item.isbn %]ISBN: [% item.isbn %]<br />[% END %]
1629 [% IF item.issn %]ISSN: [% item.issn %]<br />[% END %]
1630 [% IF item.upc %]UPC: [% item.upc %]<br />[% END %]
1631 Title: [% item.title %]<br />
1632 [% IF item.author %] Author: [% item.author %]<br />[% END -%]
1633 Publication Info: [% item.publisher %] [% item.pubdate %]<br/>
1634 Item Type: [% item.item_type %]
1636 [% IF user_data.0.type == 'full' && item.holdings.size == 0 %]
1637 <li>No items for this record at the selected location</li>
1639 [% FOR cp IN item.holdings -%]
1641 Library: [% cp.circ_lib %]<br/>
1642 Location: [% cp.location %]<br/>
1643 Call Number: [% cp.prefix _ ' ' _ cp.callnumber _ ' ' _ cp.suffix %]<br/>
1644 [% IF cp.parts %]Parts: [% cp.parts %]<br/>[% END %]
1645 Status: [% cp.status_label %]<br/>
1646 Barcode: [% cp.barcode %]
1655 $$ FROM action_trigger.event_definition WHERE hook = 'biblio.format.record_entry.print' AND active;
1657 INSERT INTO action_trigger.event_def_group_member (grp, name, holdings, event_def)
1658 SELECT 1, 'Full', TRUE, id FROM action_trigger.event_definition WHERE name = 'biblio.record_entry.print.full' and active;
1666 SELECT evergreen.upgrade_deps_block_check('1232', :eg_version);
1668 CREATE TABLE asset.course_module_course (
1669 id SERIAL PRIMARY KEY,
1671 course_number TEXT NOT NULL,
1672 section_number TEXT,
1673 owning_lib INT REFERENCES actor.org_unit (id),
1674 is_archived BOOLEAN NOT NULL DEFAULT false
1677 CREATE TABLE asset.course_module_role (
1678 id SERIAL PRIMARY KEY,
1679 name TEXT UNIQUE NOT NULL,
1680 is_public BOOLEAN NOT NULL DEFAULT false
1683 CREATE TABLE asset.course_module_course_users (
1684 id SERIAL PRIMARY KEY,
1685 course INT NOT NULL REFERENCES asset.course_module_course (id),
1686 usr INT NOT NULL REFERENCES actor.usr (id),
1687 usr_role INT REFERENCES asset.course_module_role (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
1690 CREATE TABLE asset.course_module_course_materials (
1691 id SERIAL PRIMARY KEY,
1692 course INT NOT NULL REFERENCES asset.course_module_course (id),
1693 item INT REFERENCES asset.copy (id),
1695 record INT REFERENCES biblio.record_entry (id),
1696 temporary_record BOOLEAN,
1697 original_location INT REFERENCES asset.copy_location,
1698 original_status INT REFERENCES config.copy_status,
1699 original_circ_modifier TEXT, --REFERENCES config.circ_modifier
1700 original_callnumber INT REFERENCES asset.call_number,
1701 unique (course, item, record)
1704 CREATE TABLE asset.course_module_term (
1705 id SERIAL PRIMARY KEY,
1706 name TEXT UNIQUE NOT NULL,
1707 owning_lib INT REFERENCES actor.org_unit (id),
1708 start_date TIMESTAMP WITH TIME ZONE,
1709 end_date TIMESTAMP WITH TIME ZONE
1712 INSERT INTO asset.course_module_role (id, name, is_public) VALUES
1713 (1, oils_i18n_gettext(1, 'Instructor', 'acmr', 'name'), true),
1714 (2, oils_i18n_gettext(2, 'Teaching assistant', 'acmr', 'name'), true),
1715 (3, oils_i18n_gettext(2, 'Student', 'acmr', 'name'), false);
1717 SELECT SETVAL('asset.course_module_role_id_seq'::TEXT, 100);
1719 CREATE TABLE asset.course_module_term_course_map (
1720 id BIGSERIAL PRIMARY KEY,
1721 term INT NOT NULL REFERENCES asset.course_module_term (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1722 course INT NOT NULL REFERENCES asset.course_module_course (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
1725 INSERT INTO permission.perm_list(id, code, description)
1731 'Allows user to manage Courses, Course Materials, and associate Users with Courses.',
1737 INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
1739 pgt.id, perm.id, aout.depth, TRUE
1741 permission.grp_tree pgt,
1742 permission.perm_list perm,
1743 actor.org_unit_type aout
1745 pgt.name = 'Circulation Administrator' AND
1746 aout.name = 'Consortium' AND
1747 perm.code = 'MANAGE_RESERVES'
1750 INSERT INTO config.org_unit_setting_type
1751 (grp, name, datatype, label, description, fm_class)
1754 'circ.course_materials_opt_in', 'bool',
1756 'circ.course_materials_opt_in',
1757 'Opt Org Unit into the Course Materials Module',
1762 'circ.course_materials_opt_in',
1763 'If enabled, the Org Unit will utilize Course Material functionality.',
1769 'circ.course_materials_browse_by_instructor', 'bool',
1771 'circ.course_materials_browse_by_instructor',
1772 'Allow users to browse Courses by Instructor',
1777 'circ.course_materials_browse_by_instructor',
1778 'If enabled, the Org Unit will allow OPAC users to browse Courses by instructor name.',
1784 'circ.course_materials_brief_record_bib_source', 'link',
1786 'circ.course_materials_brief_record_bib_source',
1787 'Bib source for brief records created in the course materials module',
1791 'circ.course_materials_brief_record_bib_source',
1792 'The course materials module will use this bib source for any new brief bibliographic records made inside that module. For best results, use a transcendant bib source.',
1793 'coust', 'description'
1798 INSERT INTO config.bib_source (quality, source, transcendant) VALUES
1799 (1, oils_i18n_gettext(4, 'Course materials module', 'cbs', 'source'), TRUE);
1801 INSERT INTO actor.org_unit_setting (org_unit, name, value)
1802 SELECT 1, 'circ.course_materials_brief_record_bib_source', id
1803 FROM config.bib_source
1804 WHERE source='Course materials module';
1807 SELECT evergreen.upgrade_deps_block_check('1233', :eg_version);
1809 INSERT into config.workstation_setting_type (name, grp, datatype, label)
1811 'eg.grid.hopeless.wide_holds', 'gui', 'object',
1813 'eg.grid.hopeless.wide_holds',
1814 'Grid Config: hopeless.wide_holds',
1821 SELECT evergreen.upgrade_deps_block_check('1234', :eg_version);
1823 ALTER TABLE config.copy_status ADD COLUMN hopeless_prone BOOL NOT NULL DEFAULT FALSE; -- 002.schema.config.sql
1824 ALTER TABLE action.hold_request ADD COLUMN hopeless_date TIMESTAMP WITH TIME ZONE; -- 090.schema.action.sql
1826 INSERT INTO config.upgrade_log (version, applied_to) VALUES ('1235', :eg_version);
1828 CREATE TABLE action.curbside (
1829 id SERIAL PRIMARY KEY,
1830 patron INT NOT NULL REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1831 org INT NOT NULL REFERENCES actor.org_unit (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1834 stage_staff INT REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1835 arrival TIMESTAMPTZ,
1836 delivered TIMESTAMPTZ,
1837 delivery_staff INT REFERENCES actor.usr (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
1841 INSERT INTO config.org_unit_setting_type (name, label, grp, description, datatype)
1844 'Enable curbside pickup functionality at library.',
1846 'When set to TRUE, enable staff and public interfaces to schedule curbside pickup of holds that become available for pickup.',
1850 INSERT INTO config.org_unit_setting_type (name, label, grp, description, datatype)
1852 'circ.curbside.granularity',
1853 'Time interval between curbside appointments',
1855 'Time interval between curbside appointments',
1859 INSERT INTO config.org_unit_setting_type (name, label, grp, description, datatype)
1861 'circ.curbside.max_concurrent',
1862 'Maximum number of patrons that may select a particular curbside pickup time',
1864 'Maximum number of patrons that may select a particular curbside pickup time',
1868 INSERT INTO config.org_unit_setting_type (name, label, grp, description, datatype)
1870 'circ.curbside.disable_patron_input',
1871 'Disable patron modification of curbside appointments in public catalog',
1873 'When set to TRUE, patrons cannot use the My Account interface to select curbside pickup times',
1877 INSERT INTO actor.org_unit_setting (org_unit, name, value)
1878 SELECT id, 'circ.curbside', 'false' FROM actor.org_unit WHERE parent_ou IS NULL
1880 SELECT id, 'circ.curbside.max_concurrent', '10' FROM actor.org_unit WHERE parent_ou IS NULL
1882 SELECT id, 'circ.curbside.granularity', '"15 minutes"' FROM actor.org_unit WHERE parent_ou IS NULL
1885 INSERT INTO action_trigger.hook (key, core_type, description, passive)
1887 'hold.offer_curbside',
1890 'hold.offer_curbside',
1891 'Hook used to trigger the notification of an offer of curbside pickup',
1898 INSERT INTO action_trigger.hook (key, core_type, description, passive)
1900 'hold.confirm_curbside',
1903 'hold.confirm_curbside',
1904 'Hook used to trigger the notification of the creation or update of a curbside pickup appointment with an arrival URL',
1911 INSERT INTO action_trigger.reactor (module, description) VALUES (
1912 'CurbsideSlot', 'Create a curbside pickup appointment slot when necessary'
1915 INSERT INTO action_trigger.validator (module, description) VALUES (
1916 'Curbside', 'Confirm that curbside pickup is enabled for the hold pickup library'
1919 ------------------- Disabled example A/T defintions ------------------------------
1921 -- Create a "dummy" slot when applicable, and trigger the "offer curbside" events
1922 INSERT INTO action_trigger.event_definition (
1933 'Trigger curbside offer events and create a placeholder for the patron, where applicable',
1941 INSERT INTO action_trigger.event_definition (
1955 'Curbside offer Email notification, triggered by CurbsideSlot reactor on a definition attached to the hold.available hook',
1956 'hold.offer_curbside',
1964 [%- user = target.0.usr -%]
1965 To: [%- params.recipient_email || user.email %]
1966 From: [%- params.sender_email || default_sender %]
1967 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
1968 Subject: Curbside Pickup
1969 Auto-Submitted: auto-generated
1971 [% target.0.pickup_lib.name %] is now offering curbside delivery
1972 service. Please call [% target.0.pickup_lib.phone %] or visit the
1973 link below to schedule a pickup time.
1975 https://example.org/eg/opac/myopac/holds_curbside
1977 Stay safe! Wash your hands!
1980 INSERT INTO action_trigger.environment (
1984 currval('action_trigger.event_definition_id_seq'),
1987 currval('action_trigger.event_definition_id_seq'),
1991 INSERT INTO action_trigger.event_params (event_def, param, value)
1992 VALUES (currval('action_trigger.event_definition_id_seq'), 'check_email_notify', 1);
1995 INSERT INTO action_trigger.event_definition (
2009 'Curbside offer SMS notification, triggered by CurbsideSlot reactor on a definition attached to the hold.available hook',
2010 'hold.offer_curbside',
2017 [%- user = target.0.usr -%]
2018 From: [%- params.sender_email || default_sender %]
2019 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
2020 To: [%- params.recipient_email || helpers.get_sms_gateway_email(target.0.sms_carrier,target.0.sms_notify) %]
2021 Subject: Curbside Pickup
2022 Auto-Submitted: auto-generated
2024 [% target.0.pickup_lib.name %] offers curbside pickup.
2025 Call [% target.0.pickup_lib.phone %] or visit https://example.org/eg/opac/myopac/holds_curbside
2029 INSERT INTO action_trigger.environment (
2033 currval('action_trigger.event_definition_id_seq'),
2036 currval('action_trigger.event_definition_id_seq'),
2040 INSERT INTO action_trigger.event_params (event_def, param, value)
2041 VALUES (currval('action_trigger.event_definition_id_seq'), 'check_sms_notify', 1);
2043 -- Email confirmation
2044 INSERT INTO action_trigger.event_definition (
2056 'Curbside confirmation Email notification',
2057 'hold.confirm_curbside',
2063 [%- user = target.patron -%]
2064 To: [%- params.recipient_email || user.email %]
2065 From: [%- params.sender_email || default_sender %]
2066 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
2067 Subject: Curbside Pickup Confirmed
2068 Auto-Submitted: auto-generated
2070 This email is to confirm that you have scheduled a curbside item
2071 pickup at [% target.org.name %] for [% date.format(helpers.format_date(target.slot), '%a, %d %b %Y %T') %].
2073 You can cancel or change to your appointment, add vehicle description
2074 notes, and alert staff to your arrival by going to the link below.
2076 When you arrive, please call [% target.org.phone %] or visit the
2077 link below to let us know you are here.
2079 https://example.org/eg/opac/myopac/holds_curbside
2081 Stay safe! Wash your hands!
2084 INSERT INTO action_trigger.environment (
2088 currval('action_trigger.event_definition_id_seq'),
2091 currval('action_trigger.event_definition_id_seq'),
2095 -- We do /not/ add this by default, treating curbside request as implicit opt-in
2097 INSERT INTO action_trigger.event_params (event_def, param, value)
2098 VALUES (currval('action_trigger.event_definition_id_seq'), 'check_email_notify', 1);
2102 INSERT INTO action_trigger.event_definition (
2114 'Curbside confirmation SMS notification',
2115 'hold.confirm_curbside',
2120 [%- user = target.patron -%]
2121 From: [%- params.sender_email || default_sender %]
2122 Date: [%- date.format(date.now, '%a, %d %b %Y %T -0000', gmt => 1) %]
2123 To: [%- params.recipient_email || helpers.get_sms_gateway_email(helpers.get_user_setting(user.id, 'opac.default_sms_carrier'), helpers.get_user_setting(user.id, 'opac.default_sms_notify')) %]
2124 Subject: Curbside Pickup Confirmed
2125 Auto-Submitted: auto-generated
2127 Location: [% target.org.name %]
2128 Time: [% date.format(helpers.format_date(target.slot), '%a, %d %b %Y %T') %]
2129 Make changes at https://example.org/eg/opac/myopac/holds_curbside
2133 INSERT INTO action_trigger.environment (
2137 currval('action_trigger.event_definition_id_seq'),
2140 currval('action_trigger.event_definition_id_seq'),
2144 -- We do /not/ add this by default, treating curbside request as implicit opt-in
2146 INSERT INTO action_trigger.event_params (event_def, param, value)
2147 VALUES (currval('action_trigger.event_definition_id_seq'), 'check_sms_notify', 1);
2153 -- Update auditor tables to catch changes to source tables.
2154 -- Can be removed/skipped if there were no schema changes.
2155 SELECT auditor.update_auditors();