4701a904a304e310389e9f95f110475838c33754
[working/Evergreen.git] / Open-ILS / src / sql / Pg / 400.schema.action_trigger.sql
1 /*
2  * Copyright (C) 2009  Equinox Software, Inc.
3  * Mike Rylander <miker@esilibrary.com> 
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 DROP SCHEMA IF EXISTS action_trigger CASCADE;
18
19 BEGIN;
20
21 CREATE SCHEMA action_trigger;
22
23 CREATE TABLE action_trigger.hook (
24     key         TEXT    PRIMARY KEY,
25     core_type   TEXT    NOT NULL,
26     description TEXT,
27     passive     BOOL    NOT NULL DEFAULT FALSE
28 );
29 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkout','circ','Item checked out to user');
30 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkin','circ','Item checked in');
31 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost','circ','Circulating Item marked Lost');
32 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost.found','circ','Lost Circulating Item checked in');
33 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('lost.auto','circ','Circulating Item automatically marked lost');
34 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('claims_returned','circ','Circulating Item marked Claims Returned');
35 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('claims_returned.found','circ','Claims Returned Circulating Item is checked in');
36 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('missing','acp','Item marked Missing');
37 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('missing.found','acp','Missing Item checked in');
38 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('transit.start','acp','An Item is placed into transit');
39 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('transit.finish','acp','An Item is received from a transit');
40 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_request.success','ahr','A hold is successfully placed');
41 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_request.failure','ahr','A hold is attempted but not successfully placed');
42 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold.capture','ahr','A targeted Item is captured for a hold');
43 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold.available','ahr','A held item is ready for pickup');
44 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_transit.start','ahtc','A hold-captured Item is placed into transit');
45 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('hold_transit.finish','ahtc','A hold-captured Item is received from a transit');
46 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('checkout.due','circ','Checked out Item is Due',TRUE);
47 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_FINES','ausp','Patron has exceeded allowed fines',TRUE);
48 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_OVERDUE_COUNT','ausp','Patron has exceeded allowed overdue count',TRUE);
49 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_CHECKOUT_COUNT','ausp','Patron has exceeded allowed checkout count',TRUE);
50 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('penalty.PATRON_EXCEEDS_COLLECTIONS_WARNING','ausp','Patron has exceeded maximum fine amount for collections department warning',TRUE);
51 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('acqpo.activated','acqpo','Purchase order was activated',FALSE);
52 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('format.po.html','acqpo','Formats a Purchase Order as an HTML document',TRUE);
53 INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES ('format.po.pdf','acqpo','Formats a Purchase Order as a PDF document',TRUE);
54 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('damaged','acp','Item marked damaged');
55 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('checkout.damaged','circ','A circulating item is marked damaged and the patron is fined');
56 INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('renewal','circ','Item renewed to user');
57
58 -- and much more, I'm sure
59
60 -- Specialized collection modules.  Given an FM object, gather some info and return a scalar or ref.
61 CREATE TABLE action_trigger.collector (
62     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Collector:: namespace
63     description TEXT    
64 );
65 INSERT INTO action_trigger.collector (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
66 --INSERT INTO action_trigger.collector (module,description) VALUES ('CircCountsByCircMod','Count of Circulations for a User, broken down by circulation modifier');
67
68 -- Simple tests on an FM object from hook.core_type to test for "should we still do this."
69 CREATE TABLE action_trigger.validator (
70     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Validator:: namespace
71     description TEXT    
72 );
73 INSERT INTO action_trigger.validator (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
74 INSERT INTO action_trigger.validator (module,description) VALUES ('NOOP_True','Always returns true -- validation always passes');
75 INSERT INTO action_trigger.validator (module,description) VALUES ('NOOP_False','Always returns false -- validation always fails');
76 INSERT INTO action_trigger.validator (module,description) VALUES ('CircIsOpen','Check that the circulation is still open');
77 INSERT INTO action_trigger.validator (module,description) VALUES ('HoldIsAvailable','Check that an item is on the hold shelf');
78 INSERT INTO action_trigger.validator (module,description) VALUES ('CircIsOverdue','Check that the circulation is overdue');
79 INSERT INTO action_trigger.validator (module,description) VALUES ('MaxPassiveDelayAge','Check that the event is not too far past the delay_field time -- requires a max_delay_age interval parameter');
80 INSERT INTO action_trigger.validator (module,description) VALUES ('MinPassiveTargetAge','Check that the target is old enough to be used by this event -- requires a min_target_age interval parameter, and accepts an optional target_age_field to specify what time to use for offsetting');
81
82 -- After an event passes validation (action_trigger.validator), the reactor processes it.
83 CREATE TABLE action_trigger.reactor (
84     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Reactor:: namespace
85     description TEXT    
86 );
87
88 INSERT INTO action_trigger.reactor (module,description) VALUES
89 (   'fourty_two',
90     oils_i18n_gettext(
91         'fourty_two',
92         'Returns the answer to life, the universe and everything',
93         'atreact',
94         'description'
95     )
96 );
97 INSERT INTO action_trigger.reactor (module,description) VALUES
98 (   'NOOP_True',
99     oils_i18n_gettext(
100         'NOOP_True',
101         'Always returns true -- reaction always passes',
102         'atreact',
103         'description'
104     )
105 );
106 INSERT INTO action_trigger.reactor (module,description) VALUES
107 (   'NOOP_False',
108     oils_i18n_gettext(
109         'NOOP_False',
110         'Always returns false -- reaction always fails',
111         'atreact',
112         'description'
113     )
114 );
115 INSERT INTO action_trigger.reactor (module,description) VALUES
116 (   'SendEmail',
117     oils_i18n_gettext(
118         'SendEmail',
119         'Send an email based on a user-defined template',
120         'atreact',
121         'description'
122     )
123 );
124
125 -- TODO: build a PDF generator
126 --INSERT INTO action_trigger.reactor (module,description) VALUES
127 --(   'GenerateBatchOverduePDF',
128 --    oils_i18n_gettext(
129 --        'GenerateBatchOverduePDF',
130 --        'Output a batch PDF of overdue notices for printing',
131 --        'atreact',
132 --        'description'
133 --    )
134 --);
135
136 INSERT INTO action_trigger.reactor (module,description) VALUES
137 (   'MarkItemLost',
138     oils_i18n_gettext(
139         'MarkItemLost',
140         'Marks a circulation and associated item as lost',
141         'atreact',
142         'description'
143     )
144 );
145 INSERT INTO action_trigger.reactor (module,description) VALUES
146 (   'ApplyCircFee',
147     oils_i18n_gettext(
148         'ApplyCircFee',
149         'Applies a billing with a pre-defined amount to a circulation',
150         'atreact',
151         'description'
152     )
153 );
154 INSERT INTO action_trigger.reactor (module,description) VALUES
155 (   'ProcessTemplate',
156     oils_i18n_gettext(
157         'ProcessTemplate',
158         'Processes the configured template',
159         'atreact',
160         'description'
161     )
162 );
163
164 -- After an event is reacted to (either success or failure) a cleanup module is run against the resulting environment
165 CREATE TABLE action_trigger.cleanup (
166     module      TEXT    PRIMARY KEY, -- All live under the OpenILS::Trigger::Cleanup:: namespace
167     description TEXT    
168 );
169 INSERT INTO action_trigger.cleanup (module,description) VALUES ('fourty_two','Returns the answer to life, the universe and everything');
170 INSERT INTO action_trigger.cleanup (module,description) VALUES ('NOOP_True','Always returns true -- cleanup always passes');
171 INSERT INTO action_trigger.cleanup (module,description) VALUES ('NOOP_False','Always returns false -- cleanup always fails');
172 INSERT INTO action_trigger.cleanup (module,description) VALUES ('ClearAllPending','Remove all future, pending notifications for this target');
173
174 CREATE TABLE action_trigger.event_definition (
175     id              SERIAL      PRIMARY KEY,
176     active          BOOL        NOT NULL DEFAULT TRUE,
177     owner           INT         NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
178     name            TEXT        NOT NULL,
179     hook            TEXT        NOT NULL REFERENCES action_trigger.hook (key) DEFERRABLE INITIALLY DEFERRED,
180     validator       TEXT        NOT NULL REFERENCES action_trigger.validator (module) DEFERRABLE INITIALLY DEFERRED,
181     reactor         TEXT        NOT NULL REFERENCES action_trigger.reactor (module) DEFERRABLE INITIALLY DEFERRED,
182     cleanup_success TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
183     cleanup_failure TEXT        REFERENCES action_trigger.cleanup (module) DEFERRABLE INITIALLY DEFERRED,
184     delay           INTERVAL    NOT NULL DEFAULT '5 minutes',
185     max_delay       INTERVAL,
186     usr_field       TEXT,
187     opt_in_setting  TEXT        REFERENCES config.usr_setting_type (name) DEFERRABLE INITIALLY DEFERRED,
188     delay_field     TEXT,                 -- for instance, xact_start on a circ hook ... look for fields on hook.core_type where datatype=timestamp? If not set, delay from now()
189     group_field     TEXT,                 -- field from this.hook.core_type to batch event targets together on, fed into reactor a group at a time.
190     template        TEXT,                 -- the TT block.  will have an 'environment' hash (or array of hashes, grouped events) built up by validator and collector(s), which can be modified.
191     granularity     TEXT,   -- could specify a batch which is the only time these events should actually run
192     CONSTRAINT ev_def_owner_hook_val_react_clean_delay_once UNIQUE (owner, hook, validator, reactor, delay, delay_field),
193     CONSTRAINT ev_def_name_owner_once UNIQUE (owner, name)
194 );
195
196 CREATE TABLE action_trigger.environment (
197     id          SERIAL  PRIMARY KEY,
198     event_def   INT     NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
199     path        TEXT,       -- fields to flesh. given a hook with a core_type of circ, imagine circ_lib.parent_ou expanding to
200                             -- {flesh: 2, flesh_fields: {circ: ['circ_lib'], aou: ['parent_ou']}} ... default is to flesh all
201                             -- at flesh depth 1
202     collector   TEXT    REFERENCES action_trigger.collector (module) DEFERRABLE INITIALLY DEFERRED, -- if set, given the object at 'path', return some data
203                                                                       -- to be stashed at environment.<label>
204     label       TEXT    CHECK (label NOT IN ('result','target','event')),
205     CONSTRAINT env_event_label_once UNIQUE (event_def,label)
206 );
207
208 CREATE TABLE action_trigger.event_output (
209     id              BIGSERIAL   PRIMARY KEY,
210     create_time     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
211     is_error        BOOLEAN     NOT NULL DEFAULT FALSE,
212     data            TEXT        NOT NULL
213 );
214
215 CREATE TABLE action_trigger.event (
216     id              BIGSERIAL   PRIMARY KEY,
217     target          BIGINT      NOT NULL, -- points at the id from class defined by event_def.hook.core_type
218     event_def       INT         REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
219     add_time        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
220     run_time        TIMESTAMPTZ NOT NULL,
221     start_time      TIMESTAMPTZ,
222     update_time     TIMESTAMPTZ,
223     complete_time   TIMESTAMPTZ,
224     update_process  INT,
225     state           TEXT        NOT NULL DEFAULT 'pending' CHECK (state IN ('pending','invalid','found','collecting','collected','validating','valid','reacting','reacted','cleaning','complete','error')),
226     user_data       TEXT        CHECK (user_data IS NULL OR is_json( user_data )),
227     template_output BIGINT      REFERENCES action_trigger.event_output (id),
228     error_output    BIGINT      REFERENCES action_trigger.event_output (id),
229     async_output    BIGINT      REFERENCES action_trigger.event_output (id)
230 );
231 CREATE INDEX atev_target_def_idx ON action_trigger.event (target,event_def);
232
233 CREATE TABLE action_trigger.event_params (
234     id          BIGSERIAL   PRIMARY KEY,
235     event_def   INT         NOT NULL REFERENCES action_trigger.event_definition (id) DEFERRABLE INITIALLY DEFERRED,
236     param       TEXT        NOT NULL, -- the key under environment.event.params to store the output of ...
237     value       TEXT        NOT NULL, -- ... the eval() output of this.  Has access to environment (and, well, all of perl)
238     CONSTRAINT event_params_event_def_param_once UNIQUE (event_def,param)
239 );
240
241 COMMIT;
242