1 DROP SCHEMA acq CASCADE;
11 CREATE TABLE acq.currency_type (
12 code TEXT PRIMARY KEY,
16 -- Use the ISO 4217 abbreviations for currency codes
17 INSERT INTO acq.currency_type (code, label) VALUES ('USD','US Dollars');
18 INSERT INTO acq.currency_type (code, label) VALUES ('CAN','Canadian Dollars');
19 INSERT INTO acq.currency_type (code, label) VALUES ('EUR','Euros');
21 CREATE TABLE acq.exchange_rate (
22 id SERIAL PRIMARY KEY,
23 from_currency TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
24 to_currency TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
25 ratio NUMERIC NOT NULL,
26 CONSTRAINT exchange_rate_from_to_once UNIQUE (from_currency,to_currency)
29 INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','CAN',1.2);
30 INSERT INTO acq.exchange_rate (from_currency,to_currency,ratio) VALUES ('USD','EUR',0.5);
32 CREATE TABLE acq.provider (
33 id SERIAL PRIMARY KEY,
35 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
36 currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
40 CONSTRAINT provider_name_once_per_owner UNIQUE (name,owner),
41 CONSTRAINT code_once_per_owner UNIQUE (code, owner)
44 CREATE TABLE acq.provider_holding_subfield_map (
45 id SERIAL PRIMARY KEY,
46 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
47 name TEXT NOT NULL, -- barcode, price, etc
48 subfield TEXT NOT NULL,
49 CONSTRAINT name_once_per_provider UNIQUE (provider,name)
52 CREATE TABLE acq.provider_address (
53 id SERIAL PRIMARY KEY,
54 valid BOOL NOT NULL DEFAULT TRUE,
56 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
57 street1 TEXT NOT NULL,
62 country TEXT NOT NULL,
63 post_code TEXT NOT NULL
66 CREATE TABLE acq.provider_contact (
67 id SERIAL PRIMARY KEY,
68 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
70 role TEXT, -- free-form.. e.g. "our sales guy"
75 CREATE TABLE acq.provider_contact_address (
76 id SERIAL PRIMARY KEY,
77 valid BOOL NOT NULL DEFAULT TRUE,
79 contact INT NOT NULL REFERENCES acq.provider_contact (id) DEFERRABLE INITIALLY DEFERRED,
80 street1 TEXT NOT NULL,
85 country TEXT NOT NULL,
86 post_code TEXT NOT NULL
90 CREATE TABLE acq.funding_source (
91 id SERIAL PRIMARY KEY,
93 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
94 currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
96 CONSTRAINT funding_source_name_once_per_owner UNIQUE (name,owner)
99 CREATE TABLE acq.funding_source_credit (
100 id SERIAL PRIMARY KEY,
101 funding_source INT NOT NULL REFERENCES acq.funding_source (id) DEFERRABLE INITIALLY DEFERRED,
102 amount NUMERIC NOT NULL,
104 deadline_date TIMESTAMPTZ,
105 effective_date TIMESTAMPTZ NOT NULL default now()
108 CREATE TABLE acq.fund (
109 id SERIAL PRIMARY KEY,
110 org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
112 year INT NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ),
113 currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
115 rollover BOOL NOT NULL DEFAULT FALSE,
116 CONSTRAINT name_once_per_org_year UNIQUE (org,name,year),
117 CONSTRAINT code_once_per_org_year UNIQUE (org, code, year)
120 CREATE TABLE acq.fund_debit (
121 id SERIAL PRIMARY KEY,
122 fund INT NOT NULL REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
123 origin_amount NUMERIC NOT NULL, -- pre-exchange-rate amount
124 origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
125 amount NUMERIC NOT NULL,
126 encumbrance BOOL NOT NULL DEFAULT TRUE,
127 debit_type TEXT NOT NULL,
128 xfer_destination INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
129 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
132 CREATE TABLE acq.fund_allocation (
133 id SERIAL PRIMARY KEY,
134 funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
135 fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
136 amount NUMERIC NOT NULL,
137 allocator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
139 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
141 CREATE INDEX fund_alloc_allocator_idx ON acq.fund_allocation ( allocator );
143 CREATE TABLE acq.picklist (
144 id SERIAL PRIMARY KEY,
145 owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
146 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
147 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
148 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
150 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
151 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
152 CONSTRAINT name_once_per_owner UNIQUE (name,owner)
154 CREATE INDEX acq_picklist_owner_idx ON acq.picklist ( owner );
155 CREATE INDEX acq_picklist_creator_idx ON acq.picklist ( creator );
156 CREATE INDEX acq_picklist_editor_idx ON acq.picklist ( editor );
158 CREATE TABLE acq.purchase_order (
159 id SERIAL PRIMARY KEY,
160 owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
161 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
162 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
163 ordering_agency INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
164 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
165 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
166 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
167 state TEXT NOT NULL DEFAULT 'new',
168 order_date TIMESTAMP WITH TIME ZONE,
171 CREATE INDEX po_owner_idx ON acq.purchase_order (owner);
172 CREATE INDEX po_provider_idx ON acq.purchase_order (provider);
173 CREATE INDEX po_state_idx ON acq.purchase_order (state);
174 CREATE INDEX po_creator_idx ON acq.purchase_order ( creator );
175 CREATE INDEX po_editor_idx ON acq.purchase_order ( editor );
176 CREATE INDEX acq_po_org_name_order_date_idx ON acq.purchase_order( ordering_agency, name, order_date );
178 -- The name should default to the id, as text. We can't reference a column
179 -- in a DEFAULT clause, so we use a trigger:
181 CREATE OR REPLACE FUNCTION acq.purchase_order_name_default () RETURNS TRIGGER
184 IF NEW.name IS NULL THEN
185 NEW.name := NEW.id::TEXT;
192 CREATE TRIGGER po_name_default_trg
193 BEFORE INSERT OR UPDATE ON acq.purchase_order
194 FOR EACH ROW EXECUTE PROCEDURE acq.purchase_order_name_default ();
196 -- The order name should be unique for a given ordering agency on a given order date
197 -- (truncated to midnight), but only where the order_date is not NULL. Conceptually
198 -- this rule requires a check constraint with a subquery. However you can't have a
199 -- subquery in a CHECK constraint, so we fake it with a trigger.
201 CREATE OR REPLACE FUNCTION acq.po_org_name_date_unique () RETURNS TRIGGER
207 -- If order_date is not null, then make sure we don't have a collision
208 -- on order_date (truncated to day), org, and name
210 IF NEW.order_date IS NULL THEN
214 -- In the WHERE clause, we compare the order_dates without regard to time of day.
215 -- We use a pair of inequalities instead of comparing truncated dates so that the
216 -- query can do an indexed range scan.
218 SELECT 1 INTO collision
219 FROM acq.purchase_order
221 ordering_agency = NEW.ordering_agency
223 AND order_date >= date_trunc( 'day', NEW.order_date )
224 AND order_date < date_trunc( 'day', NEW.order_date ) + '1 day'::INTERVAL
227 IF collision IS NULL THEN
228 -- okay, no collision
231 -- collision; nip it in the bud
232 RAISE EXCEPTION 'Colliding purchase orders: ordering_agency %, date %, name ''%''',
233 NEW.ordering_agency, NEW.order_date, NEW.name;
238 CREATE TRIGGER po_org_name_date_unique_trg
239 BEFORE INSERT OR UPDATE ON acq.purchase_order
240 FOR EACH ROW EXECUTE PROCEDURE acq.po_org_name_date_unique ();
242 CREATE TABLE acq.po_note (
243 id SERIAL PRIMARY KEY,
244 purchase_order INT NOT NULL REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
245 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
246 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
247 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
248 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
251 CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order);
252 CREATE INDEX acq_po_note_creator_idx ON acq.po_note ( creator );
253 CREATE INDEX acq_po_note_editor_idx ON acq.po_note ( editor );
255 CREATE TABLE acq.lineitem (
256 id BIGSERIAL PRIMARY KEY,
257 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
258 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
259 selector INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
260 provider INT REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
261 purchase_order INT REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
262 picklist INT REFERENCES acq.picklist (id) DEFERRABLE INITIALLY DEFERRED,
263 expected_recv_time TIMESTAMP WITH TIME ZONE,
264 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
265 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
267 eg_bib_id INT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
269 item_count INT NOT NULL DEFAULT 0,
270 state TEXT NOT NULL DEFAULT 'new',
271 CONSTRAINT picklist_or_po CHECK (picklist IS NOT NULL OR purchase_order IS NOT NULL)
273 CREATE INDEX li_po_idx ON acq.lineitem (purchase_order);
274 CREATE INDEX li_pl_idx ON acq.lineitem (picklist);
275 CREATE INDEX li_creator_idx ON acq.lineitem ( creator );
276 CREATE INDEX li_editor_idx ON acq.lineitem ( editor );
277 CREATE INDEX li_selector_idx ON acq.lineitem ( selector );
279 CREATE TABLE acq.lineitem_note (
280 id SERIAL PRIMARY KEY,
281 lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
282 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
283 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
284 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
285 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
288 CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem);
289 CREATE INDEX li_note_creator_idx ON acq.lineitem_note ( creator );
290 CREATE INDEX li_note_editor_idx ON acq.lineitem_note ( editor );
292 CREATE TABLE acq.lineitem_detail (
293 id BIGSERIAL PRIMARY KEY,
294 lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
295 fund INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
296 fund_debit INT REFERENCES acq.fund_debit (id) DEFERRABLE INITIALLY DEFERRED,
297 eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
301 collection_code TEXT,
302 circ_modifier TEXT REFERENCES config.circ_modifier (code) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
303 owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
304 location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
305 recv_time TIMESTAMP WITH TIME ZONE
308 CREATE INDEX li_detail_li_idx ON acq.lineitem_detail (lineitem);
310 CREATE TABLE acq.lineitem_attr_definition (
311 id BIGSERIAL PRIMARY KEY,
313 description TEXT NOT NULL,
314 remove TEXT NOT NULL DEFAULT '',
315 ident BOOL NOT NULL DEFAULT FALSE
318 CREATE TABLE acq.lineitem_marc_attr_definition (
319 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
321 ) INHERITS (acq.lineitem_attr_definition);
323 CREATE TABLE acq.lineitem_provider_attr_definition (
324 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
326 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED
327 ) INHERITS (acq.lineitem_attr_definition);
329 CREATE TABLE acq.lineitem_generated_attr_definition (
330 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
332 ) INHERITS (acq.lineitem_attr_definition);
334 CREATE TABLE acq.lineitem_usr_attr_definition (
335 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
336 usr INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED
337 ) INHERITS (acq.lineitem_attr_definition);
338 CREATE INDEX li_usr_attr_def_usr_idx ON acq.lineitem_usr_attr_definition ( usr );
340 CREATE TABLE acq.lineitem_local_attr_definition (
341 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq')
342 ) INHERITS (acq.lineitem_attr_definition);
344 CREATE TABLE acq.lineitem_attr (
345 id BIGSERIAL PRIMARY KEY,
346 definition BIGINT NOT NULL,
347 lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
348 attr_type TEXT NOT NULL,
349 attr_name TEXT NOT NULL,
350 attr_value TEXT NOT NULL
353 CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem);
354 CREATE INDEX li_attr_value_idx ON acq.lineitem_attr (attr_value);
355 CREATE INDEX li_attr_definition_idx ON acq.lineitem_attr (definition);
361 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]');
362 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('author','Author of work','//*[@tag="100" or @tag="110" or @tag="113"]/*[contains("ad",@code)]');
363 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('language','Language of work','//*[@tag="240"]/*[@code="l"][1]');
364 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]');
365 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
366 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
367 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
368 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('identifier','Identifier','//*[@tag="001"]');
369 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]');
370 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]');
371 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]');
373 INSERT INTO acq.lineitem_local_attr_definition ( code, description ) VALUES ('estimated_price', 'Estimated Price');
376 CREATE TABLE acq.distribution_formula (
377 id SERIAL PRIMARY KEY,
379 REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
381 skip_count INT NOT NULL DEFAULT 0,
382 CONSTRAINT acqdf_name_once_per_owner UNIQUE (name, owner)
385 CREATE TABLE acq.distribution_formula_entry (
386 id SERIAL PRIMARY KEY,
387 formula INTEGER NOT NULL REFERENCES acq.distribution_formula(id)
389 DEFERRABLE INITIALLY DEFERRED,
390 position INTEGER NOT NULL,
391 item_count INTEGER NOT NULL,
392 owning_lib INTEGER REFERENCES actor.org_unit(id)
393 DEFERRABLE INITIALLY DEFERRED,
394 location INTEGER REFERENCES asset.copy_location(id),
395 CONSTRAINT acqdfe_lib_once_per_formula UNIQUE( formula, position ),
396 CONSTRAINT acqdfe_must_be_somewhere
397 CHECK( owning_lib IS NOT NULL OR location IS NOT NULL )
400 CREATE TABLE acq.fund_tag (
401 id SERIAL PRIMARY KEY,
403 REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
405 CONSTRAINT acqft_tag_once_per_owner UNIQUE (name, owner)
408 CREATE TABLE acq.fund_tag_map (
409 id SERIAL PRIMARY KEY,
410 fund INTEGER NOT NULL REFERENCES acq.fund(id)
411 DEFERRABLE INITIALLY DEFERRED,
412 tag INTEGER REFERENCES acq.fund_tag(id)
414 DEFERRABLE INITIALLY DEFERRED,
415 CONSTRAINT acqftm_fund_once_per_tag UNIQUE( fund, tag )
418 CREATE TABLE acq.fund_transfer (
419 id SERIAL PRIMARY KEY,
420 src_fund INT NOT NULL REFERENCES acq.fund( id )
421 DEFERRABLE INITIALLY DEFERRED,
422 src_amount NUMERIC NOT NULL,
423 dest_fund INT NOT NULL REFERENCES acq.fund( id )
424 DEFERRABLE INITIALLY DEFERRED,
425 dest_amount NUMERIC NOT NULL,
426 transfer_time TIMESTAMPTZ NOT NULL DEFAULT now(),
427 transfer_user INT NOT NULL REFERENCES actor.usr( id )
428 DEFERRABLE INITIALLY DEFERRED,
432 CREATE INDEX acqftr_usr_idx
433 ON acq.fund_transfer( transfer_user );
435 COMMENT ON TABLE acq.fund_transfer IS $$
437 * Copyright (C) 2009 Georgia Public Library Service
438 * Scott McKellar <scott@esilibrary.com>
442 * Each row represents the transfer of money from a source fund
443 * to a destination fund. There should be corresponding entries
444 * in acq.fund_allocation. The purpose of acq.fund_transfer is
445 * to record how much money moved from which fund to which other
448 * The presence of two amount fields, rather than one, reflects
449 * the possibility that the two funds are denominated in different
450 * currencies. If they use the same currency type, the two
451 * amounts should be the same.
455 * This program is free software; you can redistribute it and/or
456 * modify it under the terms of the GNU General Public License
457 * as published by the Free Software Foundation; either version 2
458 * of the License, or (at your option) any later version.
460 * This program is distributed in the hope that it will be useful,
461 * but WITHOUT ANY WARRANTY; without even the implied warranty of
462 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
463 * GNU General Public License for more details.
467 CREATE TABLE acq.fiscal_calendar (
468 id SERIAL PRIMARY KEY,
472 -- Create a default calendar (though we don't specify its contents).
473 -- Create a foreign key in actor.org_unit, initially pointing to
474 -- the default calendar.
476 INSERT INTO acq.fiscal_calendar (
483 ALTER TABLE actor.org_unit
484 ADD COLUMN fiscal_calendar INT NOT NULL
485 REFERENCES acq.fiscal_calendar( id )
486 DEFERRABLE INITIALLY DEFERRED
489 CREATE TABLE acq.fiscal_year (
490 id SERIAL PRIMARY KEY,
491 calendar INT NOT NULL
492 REFERENCES acq.fiscal_calendar
494 DEFERRABLE INITIALLY DEFERRED,
496 year_begin TIMESTAMPTZ NOT NULL,
497 year_end TIMESTAMPTZ NOT NULL,
498 CONSTRAINT acq_fy_logical_key UNIQUE ( calendar, year ),
499 CONSTRAINT acq_fy_physical_key UNIQUE ( calendar, year_begin )
504 CREATE TYPE acq.flat_lineitem_holding_subfield AS (lineitem int, holding int, subfield text, data text);
505 CREATE OR REPLACE FUNCTION acq.extract_holding_attr_table (lineitem int, tag text) RETURNS SETOF acq.flat_lineitem_holding_subfield AS $$
508 lida acq.flat_lineitem_holding_subfield%ROWTYPE;
511 SELECT COUNT(*) INTO counter
516 '//*[@tag="' || tag || '"]',
518 ) as t(i int,c text);
520 FOR i IN 1 .. counter LOOP
523 FROM ( SELECT id,i,t,v
528 '//*[@tag="' || tag || '"][position()=' || i || ']/*/@code|' ||
529 '//*[@tag="' || tag || '"][position()=' || i || ']/*[@code]',
531 ) as t(id int,t text,v text)
542 CREATE TYPE acq.flat_lineitem_detail AS (lineitem int, holding int, attr text, data text);
543 CREATE OR REPLACE FUNCTION acq.extract_provider_holding_data ( lineitem_i int ) RETURNS SETOF acq.flat_lineitem_detail AS $$
547 lida acq.flat_lineitem_detail%ROWTYPE;
549 SELECT provider INTO prov_i FROM acq.lineitem WHERE id = lineitem_i;
550 IF NOT FOUND THEN RETURN; END IF;
552 SELECT holding_tag INTO tag_t FROM acq.provider WHERE id = prov_i;
553 IF NOT FOUND OR tag_t IS NULL THEN RETURN; END IF;
560 FROM acq.extract_holding_attr_table( lineitem_i, tag_t ) h
561 JOIN acq.provider_holding_subfield_map a USING (subfield)
562 WHERE a.provider = prov_i
571 -- select * from acq.extract_provider_holding_data(699);
573 CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) RETURNS TEXT AS $$
574 SELECT public.extract_marc_field('acq.lineitem', $1, $2, $3);
578 CREATE OR REPLACE FUNCTION public.extract_bib_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
579 SELECT public.extract_marc_field('biblio.record_entry', $1, $2);
582 CREATE OR REPLACE FUNCTION public.extract_authority_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
583 SELECT public.extract_marc_field('authority.record_entry', $1, $2);
587 -- INSERT INTO acq.lineitem_provider_attr_definition ( provider, code, description, xpath ) VALUES (1,'price','Price','//*[@tag="020" or @tag="022"]/*[@code="a"][1]');
590 Suggested vendor fields:
598 CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $$
606 FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP
608 SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid;
610 IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN
611 IF (atype = 'lineitem_provider_attr_definition') THEN
612 SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
613 CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider;
616 IF (atype = 'lineitem_provider_attr_definition') THEN
617 SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
618 ELSIF (atype = 'lineitem_marc_attr_definition') THEN
619 SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id;
620 ELSIF (atype = 'lineitem_generated_attr_definition') THEN
621 SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id;
624 SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
626 IF (value IS NOT NULL AND value <> '') THEN
627 INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
628 VALUES (NEW.id, adef.id, atype, adef.code, value);
639 CREATE OR REPLACE FUNCTION public.cleanup_acq_marc ( ) RETURNS TRIGGER AS $$
641 IF TG_OP = 'UPDATE' THEN
642 DELETE FROM acq.lineitem_attr
643 WHERE lineitem = OLD.id AND attr_type IN ('lineitem_provider_attr_definition', 'lineitem_marc_attr_definition','lineitem_generated_attr_definition');
646 DELETE FROM acq.lineitem_attr WHERE lineitem = OLD.id;
652 CREATE TRIGGER cleanup_lineitem_trigger
653 BEFORE UPDATE OR DELETE ON acq.lineitem
654 FOR EACH ROW EXECUTE PROCEDURE public.cleanup_acq_marc();
656 CREATE TRIGGER ingest_lineitem_trigger
657 AFTER INSERT OR UPDATE ON acq.lineitem
658 FOR EACH ROW EXECUTE PROCEDURE public.ingest_acq_marc();
660 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( from_ex TEXT, to_ex TEXT ) RETURNS NUMERIC AS $$
664 IF from_ex = to_ex THEN
668 SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = from_ex AND to_currency = to_ex;
673 SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = to_ex AND to_currency = from_ex;
684 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( TEXT, TEXT, NUMERIC ) RETURNS NUMERIC AS $$
685 SELECT $3 * acq.exchange_ratio($1, $2);
688 CREATE OR REPLACE FUNCTION acq.find_bad_fy()
690 Examine the acq.fiscal_year table, comparing successive years.
691 Report any inconsistencies, i.e. years that overlap, have gaps
692 between them, or are out of sequence.
694 RETURNS SETOF RECORD AS $$
718 ELSIF curr_year.calendar = prev_year.calendar THEN
719 IF curr_year.year_begin > prev_year.year_end THEN
720 -- This ugly kludge works around the fact that older
721 -- versions of PostgreSQL don't support RETURN QUERY SELECT
722 FOR return_rec IN SELECT
725 'Gap between fiscal years'::TEXT
727 RETURN NEXT return_rec;
729 ELSIF curr_year.year_begin < prev_year.year_end THEN
730 FOR return_rec IN SELECT
733 'Overlapping fiscal years'::TEXT
735 RETURN NEXT return_rec;
737 ELSIF curr_year.year < prev_year.year THEN
738 FOR return_rec IN SELECT
741 'Fiscal years out of order'::TEXT
743 RETURN NEXT return_rec;
748 prev_year := curr_year;
755 CREATE OR REPLACE VIEW acq.funding_source_credit_total AS
756 SELECT funding_source,
757 SUM(amount) AS amount
758 FROM acq.funding_source_credit
761 CREATE OR REPLACE VIEW acq.funding_source_allocation_total AS
762 SELECT funding_source,
763 SUM(a.amount)::NUMERIC(100,2) AS amount
764 FROM acq.fund_allocation a
767 CREATE OR REPLACE VIEW acq.funding_source_balance AS
768 SELECT COALESCE(c.funding_source, a.funding_source) AS funding_source,
769 SUM(COALESCE(c.amount,0.0) - COALESCE(a.amount,0.0))::NUMERIC(100,2) AS amount
770 FROM acq.funding_source_credit_total c
771 FULL JOIN acq.funding_source_allocation_total a USING (funding_source)
774 CREATE OR REPLACE VIEW acq.fund_allocation_total AS
776 SUM(a.amount * acq.exchange_ratio(s.currency_type, f.currency_type))::NUMERIC(100,2) AS amount
777 FROM acq.fund_allocation a
778 JOIN acq.fund f ON (a.fund = f.id)
779 JOIN acq.funding_source s ON (a.funding_source = s.id)
782 CREATE OR REPLACE VIEW acq.fund_debit_total AS
785 SUM(amount) AS amount
789 CREATE OR REPLACE VIEW acq.fund_encumbrance_total AS
791 SUM(amount) AS amount
792 FROM acq.fund_debit_total
793 WHERE encumbrance IS TRUE
796 CREATE OR REPLACE VIEW acq.fund_spent_total AS
798 SUM(amount) AS amount
799 FROM acq.fund_debit_total
800 WHERE encumbrance IS FALSE
803 CREATE OR REPLACE VIEW acq.fund_combined_balance AS
805 c.amount - COALESCE(d.amount,0.0) AS amount
806 FROM acq.fund_allocation_total c
807 LEFT JOIN acq.fund_debit_total d USING (fund);
809 CREATE OR REPLACE VIEW acq.fund_spent_balance AS
811 c.amount - COALESCE(d.amount,0.0) AS amount
812 FROM acq.fund_allocation_total c
813 LEFT JOIN acq.fund_spent_total d USING (fund);