great shuffling, not finished
[migration-tools.git] / sql / base / 07-eg-specific.sql
1
2 CREATE OR REPLACE FUNCTION migration_tools.create_staff_user(
3     username TEXT,
4     password TEXT,
5     org TEXT,
6     perm_group TEXT,
7     first_name TEXT DEFAULT '',
8     last_name TEXT DEFAULT ''
9 ) RETURNS VOID AS $func$
10 BEGIN
11     RAISE NOTICE '%', org ;
12     INSERT INTO actor.usr (usrname, passwd, ident_type, first_given_name, family_name, home_ou, profile)
13     SELECT username, password, 1, first_name, last_name, aou.id, pgt.id
14     FROM   actor.org_unit aou, permission.grp_tree pgt
15     WHERE  aou.shortname = org
16     AND    pgt.name = perm_group;
17 END
18 $func$
19 LANGUAGE PLPGSQL;
20
21 -- FIXME: testing for STAFF_LOGIN perm is probably better
22 CREATE OR REPLACE FUNCTION migration_tools.is_staff_profile (INT) RETURNS BOOLEAN AS $$
23   DECLARE
24     profile ALIAS FOR $1;
25   BEGIN
26     RETURN CASE WHEN 'Staff' IN (select (permission.grp_ancestors(profile)).name) THEN TRUE ELSE FALSE END;
27   END;
28 $$ LANGUAGE PLPGSQL STRICT STABLE;
29
30 -- TODO: make another version of the procedure below that can work with specified copy staging tables
31 -- The following should track the logic of OpenILS::Application::AppUtils::get_copy_price
32 CREATE OR REPLACE FUNCTION migration_tools.get_copy_price( item BIGINT ) RETURNS NUMERIC AS $$
33 DECLARE
34     context_lib             INT;
35     charge_lost_on_zero     BOOLEAN;
36     min_price               NUMERIC;
37     max_price               NUMERIC;
38     default_price           NUMERIC;
39     working_price           NUMERIC;
40
41 BEGIN
42
43     SELECT INTO context_lib CASE WHEN call_number = -1 THEN circ_lib ELSE owning_lib END
44         FROM asset.copy ac, asset.call_number acn WHERE ac.call_number = acn.id AND ac.id = item;
45
46     SELECT INTO charge_lost_on_zero value
47         FROM actor.org_unit_ancestor_setting('circ.charge_lost_on_zero',context_lib);
48
49     SELECT INTO min_price value
50         FROM actor.org_unit_ancestor_setting('circ.min_item_price',context_lib);
51
52     SELECT INTO max_price value
53         FROM actor.org_unit_ancestor_setting('circ.max_item_price',context_lib);
54
55     SELECT INTO default_price value
56         FROM actor.org_unit_ancestor_setting('cat.default_item_price',context_lib);
57
58     SELECT INTO working_price price FROM asset.copy WHERE id = item;
59
60     IF (working_price IS NULL OR (working_price = 0 AND charge_lost_on_zero)) THEN
61         working_price := default_price;
62     END IF;
63
64     IF (max_price IS NOT NULL AND working_price > max_price) THEN
65         working_price := max_price;
66     END IF;
67
68     IF (min_price IS NOT NULL AND working_price < min_price) THEN
69         IF (working_price <> 0 OR charge_lost_on_zero IS NULL OR charge_lost_on_zero) THEN
70             working_price := min_price;
71         END IF;
72     END IF;
73
74     RETURN working_price;
75
76 END;
77
78 $$ LANGUAGE plpgsql;
79
80 CREATE OR REPLACE FUNCTION migration_tools.apply_circ_matrix( tablename TEXT ) RETURNS VOID AS $$
81
82 -- Usage:
83 --
84 --   First make sure the circ matrix is loaded and the circulations
85 --   have been staged to the extent possible (but at the very least
86 --   circ_lib, target_copy, usr, and *_renewal).  User profiles and
87 --   circ modifiers must also be in place.
88 --
89 --   SELECT migration_tools.apply_circ_matrix('m_pioneer.action_circulation');
90 --
91
92 DECLARE
93   circ_lib             INT;
94   target_copy          INT;
95   usr                  INT;
96   is_renewal           BOOLEAN;
97   this_duration_rule   INT;
98   this_fine_rule       INT;
99   this_max_fine_rule   INT;
100   rcd                  config.rule_circ_duration%ROWTYPE;
101   rrf                  config.rule_recurring_fine%ROWTYPE;
102   rmf                  config.rule_max_fine%ROWTYPE;
103   circ                 INT;
104   n                    INT := 0;
105   n_circs              INT;
106   
107 BEGIN
108
109   EXECUTE 'SELECT COUNT(*) FROM ' || tablename || ';' INTO n_circs;
110
111   FOR circ IN EXECUTE ('SELECT id FROM ' || tablename) LOOP
112
113     -- Fetch the correct rules for this circulation
114     EXECUTE ('
115       SELECT
116         circ_lib,
117         target_copy,
118         usr,
119         CASE
120           WHEN phone_renewal OR desk_renewal OR opac_renewal THEN TRUE
121           ELSE FALSE
122         END
123       FROM ' || tablename || ' WHERE id = ' || circ || ';')
124       INTO circ_lib, target_copy, usr, is_renewal ;
125     SELECT
126       INTO this_duration_rule,
127            this_fine_rule,
128            this_max_fine_rule
129       duration_rule,
130       recurring_fine_rule,
131       max_fine_rule
132       FROM action.item_user_circ_test(
133         circ_lib,
134         target_copy,
135         usr,
136         is_renewal
137         );
138     SELECT INTO rcd * FROM config.rule_circ_duration
139       WHERE id = this_duration_rule;
140     SELECT INTO rrf * FROM config.rule_recurring_fine
141       WHERE id = this_fine_rule;
142     SELECT INTO rmf * FROM config.rule_max_fine
143       WHERE id = this_max_fine_rule;
144
145     -- Apply the rules to this circulation
146     EXECUTE ('UPDATE ' || tablename || ' c
147     SET
148       duration_rule = rcd.name,
149       recurring_fine_rule = rrf.name,
150       max_fine_rule = rmf.name,
151       duration = rcd.normal,
152       recurring_fine = rrf.normal,
153       max_fine =
154         CASE rmf.is_percent
155           WHEN TRUE THEN (rmf.amount / 100.0) * ac.price
156           ELSE rmf.amount
157         END,
158       renewal_remaining = rcd.max_renewals
159     FROM
160       config.rule_circ_duration rcd,
161       config.rule_recurring_fine rrf,
162       config.rule_max_fine rmf,
163                         asset.copy ac
164     WHERE
165       rcd.id = ' || this_duration_rule || ' AND
166       rrf.id = ' || this_fine_rule || ' AND
167       rmf.id = ' || this_max_fine_rule || ' AND
168                         ac.id = c.target_copy AND
169       c.id = ' || circ || ';');
170
171     -- Keep track of where we are in the process
172     n := n + 1;
173     IF (n % 100 = 0) THEN
174       RAISE INFO '%', n || ' of ' || n_circs
175         || ' (' || (100*n/n_circs) || '%) circs updated.';
176     END IF;
177
178   END LOOP;
179
180   RETURN;
181 END;
182
183 $$ LANGUAGE plpgsql;
184
185 CREATE OR REPLACE FUNCTION migration_tools.apply_circ_matrix_to_specific_circ( tablename TEXT, circ BIGINT ) RETURNS VOID AS $$
186
187 -- Usage:
188 --
189 --   First make sure the circ matrix is loaded and the circulations
190 --   have been staged to the extent possible (but at the very least
191 --   circ_lib, target_copy, usr, and *_renewal).  User profiles and
192 --   circ modifiers must also be in place.
193 --
194 --   SELECT migration_tools.apply_circ_matrix_to_specific_circ('m_nwrl.action_circulation', 18391960);
195 --
196
197 DECLARE
198   circ_lib             INT;
199   target_copy          INT;
200   usr                  INT;
201   is_renewal           BOOLEAN;
202   this_duration_rule   INT;
203   this_fine_rule       INT;
204   this_max_fine_rule   INT;
205   rcd                  config.rule_circ_duration%ROWTYPE;
206   rrf                  config.rule_recurring_fine%ROWTYPE;
207   rmf                  config.rule_max_fine%ROWTYPE;
208   n                    INT := 0;
209   n_circs              INT := 1;
210   
211 BEGIN
212
213   --EXECUTE 'SELECT COUNT(*) FROM ' || tablename || ';' INTO n_circs;
214
215   --FOR circ IN EXECUTE ('SELECT id FROM ' || tablename) LOOP
216
217     -- Fetch the correct rules for this circulation
218     EXECUTE ('
219       SELECT
220         circ_lib,
221         target_copy,
222         usr,
223         CASE
224           WHEN phone_renewal OR desk_renewal OR opac_renewal THEN TRUE
225           ELSE FALSE
226         END
227       FROM ' || tablename || ' WHERE id = ' || circ || ';')
228       INTO circ_lib, target_copy, usr, is_renewal ;
229     SELECT
230       INTO this_duration_rule,
231            this_fine_rule,
232            this_max_fine_rule
233       (matchpoint).duration_rule,
234       (matchpoint).recurring_fine_rule,
235       (matchpoint).max_fine_rule
236       FROM action.find_circ_matrix_matchpoint(
237         circ_lib,
238         target_copy,
239         usr,
240         is_renewal
241         );
242     SELECT INTO rcd * FROM config.rule_circ_duration
243       WHERE id = this_duration_rule;
244     SELECT INTO rrf * FROM config.rule_recurring_fine
245       WHERE id = this_fine_rule;
246     SELECT INTO rmf * FROM config.rule_max_fine
247       WHERE id = this_max_fine_rule;
248
249     -- Apply the rules to this circulation
250     EXECUTE ('UPDATE ' || tablename || ' c
251     SET
252       duration_rule = rcd.name,
253       recurring_fine_rule = rrf.name,
254       max_fine_rule = rmf.name,
255       duration = rcd.normal,
256       recurring_fine = rrf.normal,
257       max_fine =
258         CASE rmf.is_percent
259           WHEN TRUE THEN (rmf.amount / 100.0) * migration_tools.get_copy_price(ac.id)
260           ELSE rmf.amount
261         END,
262       renewal_remaining = rcd.max_renewals,
263       grace_period = rrf.grace_period
264     FROM
265       config.rule_circ_duration rcd,
266       config.rule_recurring_fine rrf,
267       config.rule_max_fine rmf,
268                         asset.copy ac
269     WHERE
270       rcd.id = ' || this_duration_rule || ' AND
271       rrf.id = ' || this_fine_rule || ' AND
272       rmf.id = ' || this_max_fine_rule || ' AND
273                         ac.id = c.target_copy AND
274       c.id = ' || circ || ';');
275
276     -- Keep track of where we are in the process
277     n := n + 1;
278     IF (n % 100 = 0) THEN
279       RAISE INFO '%', n || ' of ' || n_circs
280         || ' (' || (100*n/n_circs) || '%) circs updated.';
281     END IF;
282
283   --END LOOP;
284
285   RETURN;
286 END;
287
288 $$ LANGUAGE plpgsql;
289
290 CREATE OR REPLACE FUNCTION migration_tools.assign_standing_penalties ( ) RETURNS VOID AS $$
291
292 -- USAGE: Once circulation data has been loaded, and group penalty thresholds have been set up, run this.
293 --        This will assign standing penalties as needed.
294
295 DECLARE
296   org_unit  INT;
297   usr       INT;
298
299 BEGIN
300
301   FOR org_unit IN EXECUTE ('SELECT DISTINCT org_unit FROM permission.grp_penalty_threshold;') LOOP
302
303     FOR usr IN EXECUTE ('SELECT id FROM actor.usr WHERE NOT deleted;') LOOP
304   
305       EXECUTE('SELECT actor.calculate_system_penalties(' || usr || ', ' || org_unit || ');');
306
307     END LOOP;
308
309   END LOOP;
310
311   RETURN;
312
313 END;
314
315 $$ LANGUAGE plpgsql;
316
317 CREATE OR REPLACE FUNCTION migration_tools.insert_metarecords_for_pristine_database () RETURNS VOID AS $$
318
319 BEGIN
320   INSERT INTO metabib.metarecord (fingerprint, master_record)
321     SELECT  DISTINCT ON (b.fingerprint) b.fingerprint, b.id
322       FROM  biblio.record_entry b
323       WHERE NOT b.deleted
324         AND b.id IN (SELECT r.id FROM biblio.record_entry r LEFT JOIN metabib.metarecord_source_map k ON (k.source = r.id) WHERE k.id IS NULL AND r.fingerprint IS NOT NULL)
325         AND NOT EXISTS ( SELECT 1 FROM metabib.metarecord WHERE fingerprint = b.fingerprint )
326       ORDER BY b.fingerprint, b.quality DESC;
327   INSERT INTO metabib.metarecord_source_map (metarecord, source)
328     SELECT  m.id, r.id
329       FROM  biblio.record_entry r
330       JOIN  metabib.metarecord m USING (fingerprint)
331      WHERE  NOT r.deleted;
332 END;
333   
334 $$ LANGUAGE plpgsql;
335
336
337 CREATE OR REPLACE FUNCTION migration_tools.insert_metarecords_for_incumbent_database () RETURNS VOID AS $$
338
339 BEGIN
340   INSERT INTO metabib.metarecord (fingerprint, master_record)
341     SELECT  DISTINCT ON (b.fingerprint) b.fingerprint, b.id
342       FROM  biblio.record_entry b
343       WHERE NOT b.deleted
344         AND b.id IN (SELECT r.id FROM biblio.record_entry r LEFT JOIN metabib.metarecord_source_map k ON (k.source = r.id) WHERE k.id IS NULL AND r.fingerprint IS NOT NULL)
345         AND NOT EXISTS ( SELECT 1 FROM metabib.metarecord WHERE fingerprint = b.fingerprint )
346       ORDER BY b.fingerprint, b.quality DESC;
347   INSERT INTO metabib.metarecord_source_map (metarecord, source)
348     SELECT  m.id, r.id
349       FROM  biblio.record_entry r
350         JOIN metabib.metarecord m USING (fingerprint)
351       WHERE NOT r.deleted
352         AND r.id IN (SELECT b.id FROM biblio.record_entry b LEFT JOIN metabib.metarecord_source_map k ON (k.source = b.id) WHERE k.id IS NULL);
353 END;
354     
355 $$ LANGUAGE plpgsql;
356
357 CREATE OR REPLACE FUNCTION migration_tools.create_cards( schemaname TEXT ) RETURNS VOID AS $$
358
359 -- USAGE: Make sure the patrons are staged in schemaname.actor_usr_legacy and have 'usrname' assigned.
360 --        Then SELECT migration_tools.create_cards('m_foo');
361
362 DECLARE
363         u                    TEXT := schemaname || '.actor_usr_legacy';
364         c                    TEXT := schemaname || '.actor_card';
365   
366 BEGIN
367
368         EXECUTE ('DELETE FROM ' || c || ';');
369         EXECUTE ('INSERT INTO ' || c || ' (usr, barcode) SELECT id, usrname FROM ' || u || ';');
370         EXECUTE ('UPDATE ' || u || ' u SET card = c.id FROM ' || c || ' c WHERE c.usr = u.id;');
371
372   RETURN;
373
374 END;
375
376 $$ LANGUAGE plpgsql;
377
378 CREATE OR REPLACE FUNCTION migration_tools.check_ou_depth ( ) RETURNS VOID AS $$
379
380 DECLARE
381   ou  INT;
382         org_unit_depth INT;
383         ou_parent INT;
384         parent_depth INT;
385   errors_found BOOLEAN;
386         ou_shortname TEXT;
387         parent_shortname TEXT;
388         ou_type_name TEXT;
389         parent_type TEXT;
390         type_id INT;
391         type_depth INT;
392         type_parent INT;
393         type_parent_depth INT;
394         proper_parent TEXT;
395
396 BEGIN
397
398         errors_found := FALSE;
399
400 -- Checking actor.org_unit_type
401
402         FOR type_id IN EXECUTE ('SELECT id FROM actor.org_unit_type ORDER BY id;') LOOP
403
404                 SELECT depth FROM actor.org_unit_type WHERE id = type_id INTO type_depth;
405                 SELECT parent FROM actor.org_unit_type WHERE id = type_id INTO type_parent;
406
407                 IF type_parent IS NOT NULL THEN
408
409                         SELECT depth FROM actor.org_unit_type WHERE id = type_parent INTO type_parent_depth;
410
411                         IF type_depth - type_parent_depth <> 1 THEN
412                                 SELECT name FROM actor.org_unit_type WHERE id = type_id INTO ou_type_name;
413                                 SELECT name FROM actor.org_unit_type WHERE id = type_parent INTO parent_type;
414                                 RAISE INFO 'The % org unit type has a depth of %, but its parent org unit type, %, has a depth of %.',
415                                         ou_type_name, type_depth, parent_type, type_parent_depth;
416                                 errors_found := TRUE;
417
418                         END IF;
419
420                 END IF;
421
422         END LOOP;
423
424 -- Checking actor.org_unit
425
426   FOR ou IN EXECUTE ('SELECT id FROM actor.org_unit ORDER BY shortname;') LOOP
427
428                 SELECT parent_ou FROM actor.org_unit WHERE id = ou INTO ou_parent;
429                 SELECT t.depth FROM actor.org_unit_type t, actor.org_unit o WHERE o.ou_type = t.id and o.id = ou INTO org_unit_depth;
430                 SELECT t.depth FROM actor.org_unit_type t, actor.org_unit o WHERE o.ou_type = t.id and o.id = ou_parent INTO parent_depth;
431                 SELECT shortname FROM actor.org_unit WHERE id = ou INTO ou_shortname;
432                 SELECT shortname FROM actor.org_unit WHERE id = ou_parent INTO parent_shortname;
433                 SELECT t.name FROM actor.org_unit_type t, actor.org_unit o WHERE o.ou_type = t.id and o.id = ou INTO ou_type_name;
434                 SELECT t.name FROM actor.org_unit_type t, actor.org_unit o WHERE o.ou_type = t.id and o.id = ou_parent INTO parent_type;
435
436                 IF ou_parent IS NOT NULL THEN
437
438                         IF      (org_unit_depth - parent_depth <> 1) OR (
439                                 (SELECT parent FROM actor.org_unit_type WHERE name = ou_type_name) <> (SELECT id FROM actor.org_unit_type WHERE name = parent_type)
440                         ) THEN
441                                 RAISE INFO '% (org unit %) is a % (depth %) but its parent, % (org unit %), is a % (depth %).', 
442                                         ou_shortname, ou, ou_type_name, org_unit_depth, parent_shortname, ou_parent, parent_type, parent_depth;
443                                 errors_found := TRUE;
444                         END IF;
445
446                 END IF;
447
448   END LOOP;
449
450         IF NOT errors_found THEN
451                 RAISE INFO 'No errors found.';
452         END IF;
453
454   RETURN;
455
456 END;
457
458 $$ LANGUAGE plpgsql;
459
460 CREATE OR REPLACE FUNCTION migration_tools.simple_export_library_config(dir TEXT, orgs INT[]) RETURNS VOID AS $FUNC$
461 BEGIN
462    EXECUTE $$COPY (SELECT * FROM actor.hours_of_operation WHERE id IN ($$ ||
463            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
464            $$) TO '$$ ||  dir || $$/actor_hours_of_operation'$$;
465    EXECUTE $$COPY (SELECT org_unit, close_start, close_end, reason FROM actor.org_unit_closed WHERE org_unit IN ($$ ||
466            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
467            $$) TO '$$ ||  dir || $$/actor_org_unit_closed'$$;
468    EXECUTE $$COPY (SELECT org_unit, name, value FROM actor.org_unit_setting WHERE org_unit IN ($$ ||
469            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
470            $$) TO '$$ ||  dir || $$/actor_org_unit_setting'$$;
471    EXECUTE $$COPY (SELECT name, owning_lib, holdable, hold_verify, opac_visible, circulate FROM asset.copy_location WHERE owning_lib IN ($$ ||
472            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
473            $$) TO '$$ ||  dir || $$/asset_copy_location'$$;
474    EXECUTE $$COPY (SELECT grp, org_unit, penalty, threshold FROM permission.grp_penalty_threshold WHERE org_unit IN ($$ ||
475            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
476            $$) TO '$$ ||  dir || $$/permission_grp_penalty_threshold'$$;
477    EXECUTE $$COPY (SELECT owning_lib, label, label_sortkey FROM asset.call_number_prefix WHERE owning_lib IN ($$ ||
478            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
479            $$) TO '$$ ||  dir || $$/asset_call_number_prefix'$$;
480    EXECUTE $$COPY (SELECT owning_lib, label, label_sortkey FROM asset.call_number_suffix WHERE owning_lib IN ($$ ||
481            ARRAY_TO_STRING(orgs, ',') || $$)$$ ||
482            $$) TO '$$ ||  dir || $$/asset_call_number_suffix'$$;
483    EXECUTE $$COPY config.rule_circ_duration TO '$$ ||  dir || $$/config_rule_circ_duration'$$;
484    EXECUTE $$COPY config.rule_age_hold_protect TO '$$ ||  dir || $$/config_rule_age_hold_protect'$$;
485    EXECUTE $$COPY config.rule_max_fine TO '$$ ||  dir || $$/config_rule_max_fine'$$;
486    EXECUTE $$COPY config.rule_recurring_fine TO '$$ ||  dir || $$/config_rule_recurring_fine'$$;
487    EXECUTE $$COPY permission.grp_tree TO '$$ ||  dir || $$/permission_grp_tree'$$;
488 END;
489 $FUNC$ LANGUAGE PLPGSQL;
490
491 CREATE OR REPLACE FUNCTION migration_tools.simple_import_library_config(dir TEXT) RETURNS VOID AS $FUNC$
492 BEGIN
493    EXECUTE $$COPY actor.hours_of_operation FROM '$$ ||  dir || $$/actor_hours_of_operation'$$;
494    EXECUTE $$COPY actor.org_unit_closed (org_unit, close_start, close_end, reason) FROM '$$ ||  dir || $$/actor_org_unit_closed'$$;
495    EXECUTE $$COPY actor.org_unit_setting (org_unit, name, value) FROM '$$ ||  dir || $$/actor_org_unit_setting'$$;
496    EXECUTE $$COPY asset.copy_location (name, owning_lib, holdable, hold_verify, opac_visible, circulate) FROM '$$ ||  dir || $$/asset_copy_location'$$;
497    EXECUTE $$COPY permission.grp_penalty_threshold (grp, org_unit, penalty, threshold) FROM '$$ ||  dir || $$/permission_grp_penalty_threshold'$$;
498    EXECUTE $$COPY asset.call_number_prefix (owning_lib, label, label_sortkey) FROM '$$ ||  dir || $$/asset_call_number_prefix'$$;
499    EXECUTE $$COPY asset.call_number_suffix (owning_lib, label, label_sortkey) FROM '$$ ||  dir || $$/asset_call_number_suffix'$$;
500
501    -- import any new circ rules
502    PERFORM migration_tools.simple_import_new_rows_by_value(dir, 'config', 'rule_circ_duration', 'id', 'name');
503    PERFORM migration_tools.simple_import_new_rows_by_value(dir, 'config', 'rule_age_hold_protect', 'id', 'name');
504    PERFORM migration_tools.simple_import_new_rows_by_value(dir, 'config', 'rule_max_fine', 'id', 'name');
505    PERFORM migration_tools.simple_import_new_rows_by_value(dir, 'config', 'rule_recurring_fine', 'id', 'name');
506
507    -- and permission groups
508    PERFORM migration_tools.simple_import_new_rows_by_value(dir, 'permission', 'grp_tree', 'id', 'name');
509
510 END;
511 $FUNC$ LANGUAGE PLPGSQL;
512
513 -- example: SELECT * FROM migration_tools.duplicate_template(5,'{3,4}');
514 CREATE OR REPLACE FUNCTION migration_tools.duplicate_template (INTEGER, INTEGER[]) RETURNS VOID AS $$
515     DECLARE
516         target_event_def ALIAS FOR $1;
517         orgs ALIAS FOR $2;
518     BEGIN
519         DROP TABLE IF EXISTS new_atevdefs;
520         CREATE TEMP TABLE new_atevdefs (atevdef INTEGER);
521         FOR i IN array_lower(orgs,1) .. array_upper(orgs,1) LOOP
522             INSERT INTO action_trigger.event_definition (
523                 active
524                 ,owner
525                 ,name
526                 ,hook
527                 ,validator
528                 ,reactor
529                 ,cleanup_success
530                 ,cleanup_failure
531                 ,delay
532                 ,max_delay
533                 ,usr_field
534                 ,opt_in_setting
535                 ,delay_field
536                 ,group_field
537                 ,template
538                 ,granularity
539                 ,repeat_delay
540             ) SELECT
541                 'f'
542                 ,orgs[i]
543                 ,name || ' (clone of '||target_event_def||')'
544                 ,hook
545                 ,validator
546                 ,reactor
547                 ,cleanup_success
548                 ,cleanup_failure
549                 ,delay
550                 ,max_delay
551                 ,usr_field
552                 ,opt_in_setting
553                 ,delay_field
554                 ,group_field
555                 ,template
556                 ,granularity
557                 ,repeat_delay
558             FROM
559                 action_trigger.event_definition
560             WHERE
561                 id = target_event_def
562             ;
563             RAISE INFO 'created atevdef with id = %', currval('action_trigger.event_definition_id_seq');
564             INSERT INTO new_atevdefs SELECT currval('action_trigger.event_definition_id_seq');
565             INSERT INTO action_trigger.environment (
566                 event_def
567                 ,path
568                 ,collector
569                 ,label
570             ) SELECT
571                 currval('action_trigger.event_definition_id_seq')
572                 ,path
573                 ,collector
574                 ,label
575             FROM
576                 action_trigger.environment
577             WHERE
578                 event_def = target_event_def
579             ;
580             INSERT INTO action_trigger.event_params (
581                 event_def
582                 ,param
583                 ,value
584             ) SELECT
585                 currval('action_trigger.event_definition_id_seq')
586                 ,param
587                 ,value
588             FROM
589                 action_trigger.event_params
590             WHERE
591                 event_def = target_event_def
592             ;
593         END LOOP;
594         RAISE INFO '-- UPDATE action_trigger.event_definition SET active = CASE WHEN id = % THEN FALSE ELSE TRUE END WHERE id in (%,%);', target_event_def, target_event_def, (SELECT array_to_string(array_agg(atevdef),',') from new_atevdefs);
595     END;
596 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
597
598 -- example: SELECT * FROM migration_tools.duplicate_template_but_change_delay(5,'{3,4}','00:30:00'::INTERVAL);
599 CREATE OR REPLACE FUNCTION migration_tools.duplicate_template_but_change_delay (INTEGER, INTEGER[], INTERVAL) RETURNS VOID AS $$
600     DECLARE
601         target_event_def ALIAS FOR $1;
602         orgs ALIAS FOR $2;
603         new_interval ALIAS FOR $3;
604     BEGIN
605         DROP TABLE IF EXISTS new_atevdefs;
606         CREATE TEMP TABLE new_atevdefs (atevdef INTEGER);
607         FOR i IN array_lower(orgs,1) .. array_upper(orgs,1) LOOP
608             INSERT INTO action_trigger.event_definition (
609                 active
610                 ,owner
611                 ,name
612                 ,hook
613                 ,validator
614                 ,reactor
615                 ,cleanup_success
616                 ,cleanup_failure
617                 ,delay
618                 ,max_delay
619                 ,usr_field
620                 ,opt_in_setting
621                 ,delay_field
622                 ,group_field
623                 ,template
624                 ,granularity
625                 ,repeat_delay
626             ) SELECT
627                 'f'
628                 ,orgs[i]
629                 ,name || ' (clone of '||target_event_def||')'
630                 ,hook
631                 ,validator
632                 ,reactor
633                 ,cleanup_success
634                 ,cleanup_failure
635                 ,new_interval
636                 ,max_delay
637                 ,usr_field
638                 ,opt_in_setting
639                 ,delay_field
640                 ,group_field
641                 ,template
642                 ,granularity
643                 ,repeat_delay
644             FROM
645                 action_trigger.event_definition
646             WHERE
647                 id = target_event_def
648             ;
649             RAISE INFO 'created atevdef with id = %', currval('action_trigger.event_definition_id_seq');
650             INSERT INTO new_atevdefs SELECT currval('action_trigger.event_definition_id_seq');
651             INSERT INTO action_trigger.environment (
652                 event_def
653                 ,path
654                 ,collector
655                 ,label
656             ) SELECT
657                 currval('action_trigger.event_definition_id_seq')
658                 ,path
659                 ,collector
660                 ,label
661             FROM
662                 action_trigger.environment
663             WHERE
664                 event_def = target_event_def
665             ;
666             INSERT INTO action_trigger.event_params (
667                 event_def
668                 ,param
669                 ,value
670             ) SELECT
671                 currval('action_trigger.event_definition_id_seq')
672                 ,param
673                 ,value
674             FROM
675                 action_trigger.event_params
676             WHERE
677                 event_def = target_event_def
678             ;
679         END LOOP;
680         RAISE INFO '-- UPDATE action_trigger.event_definition SET active = CASE WHEN id = % THEN FALSE ELSE TRUE END WHERE id in (%,%);', target_event_def, target_event_def, (SELECT array_to_string(array_agg(atevdef),',') from new_atevdefs);
681     END;
682 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
683
684 -- example: SELECT * FROM migration_tools.duplicate_templates(3,'{5,6}');
685 CREATE OR REPLACE FUNCTION migration_tools.duplicate_templates (INTEGER, INTEGER[]) RETURNS VOID AS $$
686     DECLARE
687         org ALIAS FOR $1;
688         target_event_defs ALIAS FOR $2;
689     BEGIN
690         DROP TABLE IF EXISTS new_atevdefs;
691         CREATE TEMP TABLE new_atevdefs (atevdef INTEGER);
692         FOR i IN array_lower(target_event_defs,1) .. array_upper(target_event_defs,1) LOOP
693             INSERT INTO action_trigger.event_definition (
694                 active
695                 ,owner
696                 ,name
697                 ,hook
698                 ,validator
699                 ,reactor
700                 ,cleanup_success
701                 ,cleanup_failure
702                 ,delay
703                 ,max_delay
704                 ,usr_field
705                 ,opt_in_setting
706                 ,delay_field
707                 ,group_field
708                 ,template
709                 ,granularity
710                 ,repeat_delay
711             ) SELECT
712                 'f'
713                 ,org
714                 ,name || ' (clone of '||target_event_defs[i]||')'
715                 ,hook
716                 ,validator
717                 ,reactor
718                 ,cleanup_success
719                 ,cleanup_failure
720                 ,delay
721                 ,max_delay
722                 ,usr_field
723                 ,opt_in_setting
724                 ,delay_field
725                 ,group_field
726                 ,template
727                 ,granularity
728                 ,repeat_delay
729             FROM
730                 action_trigger.event_definition
731             WHERE
732                 id = target_event_defs[i]
733             ;
734             RAISE INFO 'created atevdef with id = %', currval('action_trigger.event_definition_id_seq');
735             INSERT INTO new_atevdefs SELECT currval('action_trigger.event_definition_id_seq');
736             INSERT INTO action_trigger.environment (
737                 event_def
738                 ,path
739                 ,collector
740                 ,label
741             ) SELECT
742                 currval('action_trigger.event_definition_id_seq')
743                 ,path
744                 ,collector
745                 ,label
746             FROM
747                 action_trigger.environment
748             WHERE
749                 event_def = target_event_defs[i]
750             ;
751             INSERT INTO action_trigger.event_params (
752                 event_def
753                 ,param
754                 ,value
755             ) SELECT
756                 currval('action_trigger.event_definition_id_seq')
757                 ,param
758                 ,value
759             FROM
760                 action_trigger.event_params
761             WHERE
762                 event_def = target_event_defs[i]
763             ;
764         END LOOP;
765         RAISE INFO '-- UPDATE action_trigger.event_definition SET active = TRUE WHERE id in (%);', (SELECT array_to_string(array_agg(atevdef),',') from new_atevdefs);
766     END;
767 $$ LANGUAGE PLPGSQL STRICT VOLATILE;
768
769 CREATE OR REPLACE FUNCTION migration_tools.reset_event (BIGINT) RETURNS VOID AS $$
770     UPDATE
771         action_trigger.event
772     SET
773          start_time = NULL
774         ,update_time = NULL
775         ,complete_time = NULL
776         ,update_process = NULL
777         ,state = 'pending'
778         ,template_output = NULL
779         ,error_output = NULL
780         ,async_output = NULL
781     WHERE
782         id = $1;
783 $$ LANGUAGE SQL;
784
785 CREATE OR REPLACE FUNCTION migration_tools.find_hold_matrix_matchpoint (INTEGER) RETURNS INTEGER AS $$
786     SELECT action.find_hold_matrix_matchpoint(
787         (SELECT pickup_lib FROM action.hold_request WHERE id = $1),
788         (SELECT request_lib FROM action.hold_request WHERE id = $1),
789         (SELECT current_copy FROM action.hold_request WHERE id = $1),
790         (SELECT usr FROM action.hold_request WHERE id = $1),
791         (SELECT requestor FROM action.hold_request WHERE id = $1)
792     );
793 $$ LANGUAGE SQL;
794
795 CREATE OR REPLACE FUNCTION migration_tools.find_hold_matrix_matchpoint2 (INTEGER) RETURNS SETOF action.matrix_test_result AS $$
796     SELECT action.hold_request_permit_test(
797         (SELECT pickup_lib FROM action.hold_request WHERE id = $1),
798         (SELECT request_lib FROM action.hold_request WHERE id = $1),
799         (SELECT current_copy FROM action.hold_request WHERE id = $1),
800         (SELECT usr FROM action.hold_request WHERE id = $1),
801         (SELECT requestor FROM action.hold_request WHERE id = $1)
802     );
803 $$ LANGUAGE SQL;
804
805 CREATE OR REPLACE FUNCTION migration_tools.find_circ_matrix_matchpoint (INTEGER) RETURNS SETOF action.found_circ_matrix_matchpoint AS $$
806     SELECT action.find_circ_matrix_matchpoint(
807         (SELECT circ_lib FROM action.circulation WHERE id = $1),
808         (SELECT target_copy FROM action.circulation WHERE id = $1),
809         (SELECT usr FROM action.circulation WHERE id = $1),
810         (SELECT COALESCE(
811                 NULLIF(phone_renewal,false),
812                 NULLIF(desk_renewal,false),
813                 NULLIF(opac_renewal,false),
814                 false
815             ) FROM action.circulation WHERE id = $1
816         )
817     );
818 $$ LANGUAGE SQL;