]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/sql/Pg/reporter-schema.sql
LP1937294: Fix metarecord master record choice predictability issue
[Evergreen.git] / Open-ILS / src / sql / Pg / reporter-schema.sql
1 /*
2  * Copyright (C) 2004-2008  Georgia Public Library Service
3  * Copyright (C) 2007-2008  Equinox Software, Inc.
4  * Mike Rylander <miker@esilibrary.com> 
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17
18 DROP SCHEMA IF EXISTS reporter CASCADE;
19
20 BEGIN;
21
22 CREATE SCHEMA reporter;
23
24 CREATE TABLE reporter.template_folder (
25         id              SERIAL                          PRIMARY KEY,
26         parent          INT                             REFERENCES reporter.template_folder (id) DEFERRABLE INITIALLY DEFERRED,
27         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
28         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
29         name            TEXT                            NOT NULL,
30         shared          BOOL                            NOT NULL DEFAULT FALSE,
31         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
32 );
33 CREATE INDEX rpt_tmpl_fldr_owner_idx ON reporter.template_folder (owner);
34 CREATE UNIQUE INDEX rpt_template_folder_once_parent_idx ON reporter.template_folder (name,parent);
35 CREATE UNIQUE INDEX rpt_template_folder_once_idx ON reporter.template_folder (name,owner) WHERE parent IS NULL;
36
37 CREATE TABLE reporter.report_folder (
38         id              SERIAL                          PRIMARY KEY,
39         parent          INT                             REFERENCES reporter.report_folder (id) DEFERRABLE INITIALLY DEFERRED,
40         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
41         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
42         name            TEXT                            NOT NULL,
43         shared          BOOL                            NOT NULL DEFAULT FALSE,
44         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
45 );
46 CREATE INDEX rpt_rpt_fldr_owner_idx ON reporter.report_folder (owner);
47 CREATE UNIQUE INDEX rpt_report_folder_once_parent_idx ON reporter.report_folder (name,parent);
48 CREATE UNIQUE INDEX rpt_report_folder_once_idx ON reporter.report_folder (name,owner) WHERE parent IS NULL;
49
50 CREATE TABLE reporter.output_folder (
51         id              SERIAL                          PRIMARY KEY,
52         parent          INT                             REFERENCES reporter.output_folder (id) DEFERRABLE INITIALLY DEFERRED,
53         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
54         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
55         name            TEXT                            NOT NULL,
56         shared          BOOL                            NOT NULL DEFAULT FALSE,
57         share_with      INT                             REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED
58 );
59 CREATE INDEX rpt_output_fldr_owner_idx ON reporter.output_folder (owner);
60 CREATE UNIQUE INDEX rpt_output_folder_once_parent_idx ON reporter.output_folder (name,parent);
61 CREATE UNIQUE INDEX rpt_output_folder_once_idx ON reporter.output_folder (name,owner) WHERE parent IS NULL;
62
63
64 CREATE TABLE reporter.template (
65         id              SERIAL                          PRIMARY KEY,
66         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
67         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
68         name            TEXT                            NOT NULL,
69         description     TEXT                            NOT NULL DEFAULT '',
70         data            TEXT                            NOT NULL,
71         folder          INT                             NOT NULL REFERENCES reporter.template_folder (id) DEFERRABLE INITIALLY DEFERRED
72 );
73 CREATE INDEX rpt_tmpl_owner_idx ON reporter.template (owner);
74 CREATE INDEX rpt_tmpl_fldr_idx ON reporter.template (folder);
75 CREATE UNIQUE INDEX rtp_template_folder_once_idx ON reporter.template (name,folder);
76
77 CREATE TABLE reporter.report (
78         id              SERIAL                          PRIMARY KEY,
79         owner           INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
80         create_time     TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
81         name            TEXT                            NOT NULL DEFAULT '',
82         description     TEXT                            NOT NULL DEFAULT '',
83         template        INT                             NOT NULL REFERENCES reporter.template (id) DEFERRABLE INITIALLY DEFERRED,
84         data            TEXT                            NOT NULL,
85         folder          INT                             NOT NULL REFERENCES reporter.report_folder (id) DEFERRABLE INITIALLY DEFERRED,
86         recur           BOOL                            NOT NULL DEFAULT FALSE,
87         recurrence      INTERVAL
88 );
89 CREATE INDEX rpt_rpt_owner_idx ON reporter.report (owner);
90 CREATE INDEX rpt_rpt_fldr_idx ON reporter.report (folder);
91 CREATE UNIQUE INDEX rtp_report_folder_once_idx ON reporter.report (name,folder);
92
93 CREATE TABLE reporter.schedule (
94         id              SERIAL                          PRIMARY KEY,
95         report          INT                             NOT NULL REFERENCES reporter.report (id) DEFERRABLE INITIALLY DEFERRED,
96         folder          INT                             NOT NULL REFERENCES reporter.output_folder (id) DEFERRABLE INITIALLY DEFERRED,
97         runner          INT                             NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
98         run_time        TIMESTAMP WITH TIME ZONE        NOT NULL DEFAULT NOW(),
99         start_time      TIMESTAMP WITH TIME ZONE,
100         complete_time   TIMESTAMP WITH TIME ZONE,
101         email           TEXT,
102         excel_format    BOOL                            NOT NULL DEFAULT TRUE,
103         html_format     BOOL                            NOT NULL DEFAULT TRUE,
104         csv_format      BOOL                            NOT NULL DEFAULT TRUE,
105         chart_pie       BOOL                            NOT NULL DEFAULT FALSE,
106         chart_bar       BOOL                            NOT NULL DEFAULT FALSE,
107         chart_line      BOOL                            NOT NULL DEFAULT FALSE,
108         error_code      INT,
109         error_text      TEXT
110 );
111 CREATE INDEX rpt_sched_runner_idx ON reporter.schedule (runner);
112 CREATE INDEX rpt_sched_folder_idx ON reporter.schedule (folder);
113 CREATE UNIQUE INDEX rpt_sched_recurrence_once_idx ON reporter.schedule (report,folder,runner,run_time,COALESCE(email,''));
114
115 CREATE OR REPLACE VIEW reporter.simple_record AS
116 SELECT  r.id,
117         s.metarecord,
118         r.fingerprint,
119         r.quality,
120         r.tcn_source,
121         r.tcn_value,
122         title.value AS title,
123         uniform_title.value AS uniform_title,
124         author.value AS author,
125         publisher.value AS publisher,
126         SUBSTRING(pubdate.value FROM $$\d+$$) AS pubdate,
127         series_title.value AS series_title,
128         series_statement.value AS series_statement,
129         summary.value AS summary,
130         ARRAY_AGG( DISTINCT REPLACE(SUBSTRING(isbn.value FROM $$^\S+$$), '-', '') ) AS isbn,
131         ARRAY_AGG( DISTINCT REGEXP_REPLACE(issn.value, E'^\\S*(\\d{4})[-\\s](\\d{3,4}x?)', E'\\1 \\2') ) AS issn,
132         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '650' AND subfield = 'a' AND record = r.id)) AS topic_subject,
133         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '651' AND subfield = 'a' AND record = r.id)) AS geographic_subject,
134         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '655' AND subfield = 'a' AND record = r.id)) AS genre,
135         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '600' AND subfield = 'a' AND record = r.id)) AS name_subject,
136         ARRAY((SELECT DISTINCT value FROM metabib.full_rec WHERE tag = '610' AND subfield = 'a' AND record = r.id)) AS corporate_subject,
137         ARRAY((SELECT value FROM metabib.full_rec WHERE tag = '856' AND subfield IN ('3','y','u') AND record = r.id ORDER BY CASE WHEN subfield IN ('3','y') THEN 0 ELSE 1 END)) AS external_uri
138   FROM  biblio.record_entry r
139         JOIN metabib.metarecord_source_map s ON (s.source = r.id)
140         LEFT JOIN metabib.full_rec uniform_title ON (r.id = uniform_title.record AND uniform_title.tag = '240' AND uniform_title.subfield = 'a')
141         LEFT JOIN metabib.full_rec title ON (r.id = title.record AND title.tag = '245' AND title.subfield = 'a')
142         LEFT JOIN metabib.full_rec author ON (r.id = author.record AND author.tag = '100' AND author.subfield = 'a')
143         LEFT JOIN metabib.full_rec publisher ON (r.id = publisher.record AND (publisher.tag = '260' OR (publisher.tag = '264' AND publisher.ind2 = '1')) AND publisher.subfield = 'b')
144         LEFT JOIN metabib.full_rec pubdate ON (r.id = pubdate.record AND (pubdate.tag = '260' OR (pubdate.tag = '264' AND pubdate.ind2 = '1')) AND pubdate.subfield = 'c')
145         LEFT JOIN metabib.full_rec isbn ON (r.id = isbn.record AND isbn.tag IN ('024', '020') AND isbn.subfield IN ('a','z'))
146         LEFT JOIN metabib.full_rec issn ON (r.id = issn.record AND issn.tag = '022' AND issn.subfield = 'a')
147         LEFT JOIN metabib.full_rec series_title ON (r.id = series_title.record AND series_title.tag IN ('830','440') AND series_title.subfield = 'a')
148         LEFT JOIN metabib.full_rec series_statement ON (r.id = series_statement.record AND series_statement.tag = '490' AND series_statement.subfield = 'a')
149         LEFT JOIN metabib.full_rec summary ON (r.id = summary.record AND summary.tag = '520' AND summary.subfield = 'a')
150   GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14;
151
152 CREATE OR REPLACE VIEW reporter.old_super_simple_record AS
153 SELECT  r.id,
154     r.fingerprint,
155     r.quality,
156     r.tcn_source,
157     r.tcn_value,
158     evergreen.oils_json_to_text(d.title) AS title,
159     evergreen.oils_json_to_text(d.author) AS author,
160     evergreen.oils_json_to_text(d.publisher) AS publisher,
161     evergreen.oils_json_to_text(d.pubdate) AS pubdate,
162     CASE WHEN d.isbn = 'null'
163         THEN NULL
164         ELSE (SELECT ARRAY(SELECT json_array_elements_text(d.isbn::JSON)))
165     END AS isbn,
166     CASE WHEN d.issn = 'null'
167         THEN NULL
168         ELSE (SELECT ARRAY(SELECT json_array_elements_text(d.issn::JSON)))
169     END AS issn
170   FROM  biblio.record_entry r
171         JOIN metabib.wide_display_entry d ON (r.id = d.source);
172
173 CREATE TABLE reporter.materialized_simple_record AS SELECT * FROM reporter.old_super_simple_record WHERE 1=0;
174 ALTER TABLE reporter.materialized_simple_record ADD PRIMARY KEY (id);
175
176 CREATE VIEW reporter.super_simple_record AS SELECT * FROM reporter.materialized_simple_record;
177
178 CREATE OR REPLACE FUNCTION reporter.simple_rec_update (r_id BIGINT, deleted BOOL) RETURNS BOOL AS $$
179 BEGIN
180
181     DELETE FROM reporter.materialized_simple_record WHERE id = r_id;
182
183     IF NOT deleted THEN
184         INSERT INTO reporter.materialized_simple_record SELECT DISTINCT ON (id) * FROM reporter.old_super_simple_record WHERE id = r_id;
185     END IF;
186
187     RETURN TRUE;
188
189 END;
190 $$ LANGUAGE PLPGSQL;
191
192 CREATE OR REPLACE FUNCTION reporter.simple_rec_update (r_id BIGINT) RETURNS BOOL AS $$
193     SELECT reporter.simple_rec_update($1, FALSE);
194 $$ LANGUAGE SQL;
195
196 CREATE OR REPLACE FUNCTION reporter.simple_rec_delete (r_id BIGINT) RETURNS BOOL AS $$
197     SELECT reporter.simple_rec_update($1, TRUE);
198 $$ LANGUAGE SQL;
199
200 CREATE OR REPLACE FUNCTION reporter.simple_rec_trigger () RETURNS TRIGGER AS $func$
201 BEGIN
202     IF TG_OP = 'DELETE' THEN
203         PERFORM reporter.simple_rec_delete(NEW.id);
204     ELSE
205         PERFORM reporter.simple_rec_update(NEW.id);
206     END IF;
207
208     RETURN NEW;
209 END;
210 $func$ LANGUAGE PLPGSQL;
211
212 CREATE OR REPLACE FUNCTION reporter.disable_materialized_simple_record_trigger () RETURNS VOID AS $$
213     DROP TRIGGER IF EXISTS bbb_simple_rec_trigger ON biblio.record_entry;
214 $$ LANGUAGE SQL;
215
216 CREATE OR REPLACE FUNCTION reporter.enable_materialized_simple_record_trigger () RETURNS VOID AS $$
217
218     TRUNCATE TABLE reporter.materialized_simple_record;
219
220     INSERT INTO reporter.materialized_simple_record
221         (id,fingerprint,quality,tcn_source,tcn_value,title,author,publisher,pubdate,isbn,issn)
222         SELECT DISTINCT ON (id) * FROM reporter.old_super_simple_record;
223
224     CREATE TRIGGER bbb_simple_rec_trigger
225         AFTER INSERT OR UPDATE OR DELETE ON biblio.record_entry
226         FOR EACH ROW EXECUTE PROCEDURE reporter.simple_rec_trigger();
227
228 $$ LANGUAGE SQL;
229
230 CREATE OR REPLACE FUNCTION reporter.refresh_materialized_simple_record () RETURNS VOID AS $$
231     SELECT reporter.disable_materialized_simple_record_trigger();
232     SELECT reporter.enable_materialized_simple_record_trigger();
233 $$ LANGUAGE SQL;
234
235 CREATE OR REPLACE VIEW reporter.asset_call_number_dewey AS
236   SELECT id AS call_number,
237     call_number_dewey(label) AS dewey,
238     CASE WHEN call_number_dewey(label) ~ '^[0-9]+\.?[0-9]*$'::text
239       THEN btrim(to_char(10::double precision * floor(call_number_dewey(label)::double precision / 10::double precision), '000'::text))
240       ELSE NULL::text
241     END AS dewey_block_tens,
242     CASE WHEN call_number_dewey(label) ~ '^[0-9]+\.?[0-9]*$'::text
243       THEN btrim(to_char(100::double precision * floor(call_number_dewey(label)::double precision / 100::double precision), '000'::text))
244       ELSE NULL::text
245     END AS dewey_block_hundreds,
246     CASE WHEN call_number_dewey(label) ~ '^[0-9]+\.?[0-9]*$'::text
247       THEN (btrim(to_char(10::double precision * floor(call_number_dewey(label)::double precision / 10::double precision), '000'::text)) || '-'::text)
248       || btrim(to_char(10::double precision * floor(call_number_dewey(label)::double precision / 10::double precision) + 9::double precision, '000'::text))
249       ELSE NULL::text
250     END AS dewey_range_tens,
251     CASE WHEN call_number_dewey(label) ~ '^[0-9]+\.?[0-9]*$'::text
252       THEN (btrim(to_char(100::double precision * floor(call_number_dewey(label)::double precision / 100::double precision), '000'::text)) || '-'::text)
253       || btrim(to_char(100::double precision * floor(call_number_dewey(label)::double precision / 100::double precision) + 99::double precision, '000'::text))
254       ELSE NULL::text
255     END AS dewey_range_hundreds
256   FROM asset.call_number
257   WHERE call_number_dewey(label) ~ '^[0-9]'::text;
258
259 CREATE OR REPLACE VIEW reporter.demographic AS
260 SELECT  u.id,
261     u.dob,
262     CASE
263         WHEN u.dob IS NULL
264             THEN 'Adult'
265         WHEN AGE(u.dob) > '18 years'::INTERVAL
266             THEN 'Adult'
267         ELSE 'Juvenile'
268     END AS general_division,
269     CASE
270         WHEN u.dob IS NULL
271             THEN 'No Date of Birth Entered'::text
272         WHEN age(u.dob::timestamp with time zone) >= '0 years'::interval and age(u.dob::timestamp with time zone) < '6 years'::interval
273             THEN 'Child 0-5 Years Old'::text
274         WHEN age(u.dob::timestamp with time zone) >= '6 years'::interval and age(u.dob::timestamp with time zone) < '13 years'::interval
275             THEN 'Child 6-12 Years Old'::text
276         WHEN age(u.dob::timestamp with time zone) >= '13 years'::interval and age(u.dob::timestamp with time zone) < '18 years'::interval
277             THEN 'Teen 13-17 Years Old'::text
278         WHEN age(u.dob::timestamp with time zone) >= '18 years'::interval and age(u.dob::timestamp with time zone) < '26 years'::interval
279             THEN 'Adult 18-25 Years Old'::text
280         WHEN age(u.dob::timestamp with time zone) >= '26 years'::interval and age(u.dob::timestamp with time zone) < '50 years'::interval
281             THEN 'Adult 26-49 Years Old'::text
282         WHEN age(u.dob::timestamp with time zone) >= '50 years'::interval and age(u.dob::timestamp with time zone) < '60 years'::interval
283             THEN 'Adult 50-59 Years Old'::text
284         WHEN age(u.dob::timestamp with time zone) >= '60 years'::interval and age(u.dob::timestamp with time zone) < '70  years'::interval
285             THEN 'Adult 60-69 Years Old'::text
286         WHEN age(u.dob::timestamp with time zone) >= '70 years'::interval
287             THEN 'Adult 70+'::text
288         ELSE NULL::text
289     END AS age_division
290     FROM actor.usr u;
291
292 CREATE OR REPLACE VIEW reporter.circ_type AS
293 SELECT  id,
294         CASE WHEN opac_renewal OR phone_renewal OR desk_renewal OR auto_renewal
295                 THEN 'RENEWAL'
296                 ELSE 'CHECKOUT'
297         END AS "type"
298   FROM  action.circulation;
299
300 -- rhrr needs to be a real table, so it can be fast. To that end, we use
301 -- a materialized view updated via a trigger.
302 CREATE TABLE reporter.hold_request_record  AS
303 SELECT  id,
304         target,
305         hold_type,
306         CASE
307                 WHEN hold_type = 'T'
308                         THEN target
309                 WHEN hold_type = 'I'
310                         THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = ahr.target)
311                 WHEN hold_type = 'V'
312                         THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = ahr.target)
313                 WHEN hold_type IN ('C','R','F')
314                         THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = ahr.target)
315                 WHEN hold_type = 'M'
316                         THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = ahr.target)
317                 WHEN hold_type = 'P'
318                         THEN (SELECT bmp.record FROM biblio.monograph_part bmp WHERE bmp.id = ahr.target)
319         END AS bib_record
320   FROM  action.hold_request ahr;
321
322 CREATE UNIQUE INDEX reporter_hold_request_record_pkey_idx ON reporter.hold_request_record (id);
323 CREATE INDEX reporter_hold_request_record_bib_record_idx ON reporter.hold_request_record (bib_record);
324
325 ALTER TABLE reporter.hold_request_record ADD PRIMARY KEY USING INDEX reporter_hold_request_record_pkey_idx;
326
327 CREATE OR REPLACE FUNCTION reporter.hold_request_record_mapper () RETURNS TRIGGER AS $$
328 BEGIN
329     IF TG_OP = 'INSERT' THEN
330         INSERT INTO reporter.hold_request_record (id, target, hold_type, bib_record)
331         SELECT  NEW.id,
332                 NEW.target,
333                 NEW.hold_type,
334                 CASE
335                     WHEN NEW.hold_type = 'T'
336                         THEN NEW.target
337                     WHEN NEW.hold_type = 'I'
338                         THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = NEW.target)
339                     WHEN NEW.hold_type = 'V'
340                         THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = NEW.target)
341                     WHEN NEW.hold_type IN ('C','R','F')
342                         THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = NEW.target)
343                     WHEN NEW.hold_type = 'M'
344                         THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = NEW.target)
345                     WHEN NEW.hold_type = 'P'
346                         THEN (SELECT bmp.record FROM biblio.monograph_part bmp WHERE bmp.id = NEW.target)
347                 END AS bib_record;
348     ELSIF TG_OP = 'UPDATE' AND (OLD.target <> NEW.target OR OLD.hold_type <> NEW.hold_type) THEN
349         UPDATE  reporter.hold_request_record
350           SET   target = NEW.target,
351                 hold_type = NEW.hold_type,
352                 bib_record = CASE
353                     WHEN NEW.hold_type = 'T'
354                         THEN NEW.target
355                     WHEN NEW.hold_type = 'I'
356                         THEN (SELECT ssub.record_entry FROM serial.subscription ssub JOIN serial.issuance si ON (si.subscription = ssub.id) WHERE si.id = NEW.target)
357                     WHEN NEW.hold_type = 'V'
358                         THEN (SELECT cn.record FROM asset.call_number cn WHERE cn.id = NEW.target)
359                     WHEN NEW.hold_type IN ('C','R','F')
360                         THEN (SELECT cn.record FROM asset.call_number cn JOIN asset.copy cp ON (cn.id = cp.call_number) WHERE cp.id = NEW.target)
361                     WHEN NEW.hold_type = 'M'
362                         THEN (SELECT mr.master_record FROM metabib.metarecord mr WHERE mr.id = NEW.target)
363                     WHEN NEW.hold_type = 'P'
364                         THEN (SELECT bmp.record FROM biblio.monograph_part bmp WHERE bmp.id = NEW.target)
365                 END
366          WHERE  id = NEW.id;
367     END IF;
368     RETURN NEW;
369 END;
370 $$ LANGUAGE PLPGSQL;
371
372 CREATE TRIGGER reporter_hold_request_record_trigger AFTER INSERT OR UPDATE ON action.hold_request
373     FOR EACH ROW EXECUTE PROCEDURE reporter.hold_request_record_mapper();
374
375 CREATE OR REPLACE VIEW reporter.xact_billing_totals AS
376 SELECT  b.xact,
377         SUM( CASE WHEN b.voided THEN 0 ELSE amount END ) as unvoided,
378         SUM( CASE WHEN b.voided THEN amount ELSE 0 END ) as voided,
379         SUM( amount ) as total
380   FROM  money.billing b
381   GROUP BY 1;
382
383 CREATE OR REPLACE VIEW reporter.xact_paid_totals AS
384 SELECT  b.xact,
385         SUM( CASE WHEN b.voided THEN 0 ELSE amount END ) as unvoided,
386         SUM( CASE WHEN b.voided THEN amount ELSE 0 END ) as voided,
387         SUM( amount ) as total
388   FROM  money.payment b
389   GROUP BY 1;
390
391 CREATE OR REPLACE VIEW reporter.overdue_circs AS
392 SELECT  *
393   FROM  "action".circulation
394   WHERE checkin_time is null
395         AND (stop_fines NOT IN ('LOST','CLAIMSRETURNED') OR stop_fines IS NULL)
396         AND due_date < now();
397
398 CREATE OR REPLACE VIEW reporter.overdue_reports AS
399  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
400    FROM reporter.schedule s
401    JOIN reporter.report r ON r.id = s.report
402    JOIN actor.usr u ON s.runner = u.id
403    JOIN actor.card c ON c.id = u.card
404   WHERE s.start_time IS NULL AND s.run_time < now();
405
406 CREATE OR REPLACE VIEW reporter.pending_reports AS
407  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
408    FROM reporter.schedule s
409    JOIN reporter.report r ON r.id = s.report
410    JOIN actor.usr u ON s.runner = u.id
411    JOIN actor.card c ON c.id = u.card
412   WHERE s.start_time IS NULL;
413
414 CREATE OR REPLACE VIEW reporter.currently_running AS
415  SELECT s.id, c.barcode AS runner_barcode, r.name, s.run_time, s.run_time - now() AS scheduled_wait_time
416    FROM reporter.schedule s
417    JOIN reporter.report r ON r.id = s.report
418    JOIN actor.usr u ON s.runner = u.id
419    JOIN actor.card c ON c.id = u.card
420   WHERE s.start_time IS NOT NULL AND s.complete_time IS NULL;
421
422 COMMIT;
423