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
107 CREATE TABLE acq.fund (
108 id SERIAL PRIMARY KEY,
109 org INT NOT NULL REFERENCES actor.org_unit (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
111 year INT NOT NULL DEFAULT EXTRACT( YEAR FROM NOW() ),
112 currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
114 CONSTRAINT name_once_per_org_year UNIQUE (org,name,year),
115 CONSTRAINT code_once_per_org_year UNIQUE (org, code, year)
118 CREATE TABLE acq.fund_debit (
119 id SERIAL PRIMARY KEY,
120 fund INT NOT NULL REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
121 origin_amount NUMERIC NOT NULL, -- pre-exchange-rate amount
122 origin_currency_type TEXT NOT NULL REFERENCES acq.currency_type (code) DEFERRABLE INITIALLY DEFERRED,
123 amount NUMERIC NOT NULL,
124 encumbrance BOOL NOT NULL DEFAULT TRUE,
125 debit_type TEXT NOT NULL,
126 xfer_destination INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
127 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
130 CREATE TABLE acq.fund_allocation (
131 id SERIAL PRIMARY KEY,
132 funding_source INT NOT NULL REFERENCES acq.funding_source (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
133 fund INT NOT NULL REFERENCES acq.fund (id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
134 amount NUMERIC NOT NULL,
135 allocator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
137 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
139 CREATE INDEX fund_alloc_allocator_idx ON acq.fund_allocation ( allocator );
141 CREATE TABLE acq.picklist (
142 id SERIAL PRIMARY KEY,
143 owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
144 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
145 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
146 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
148 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
149 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
150 CONSTRAINT name_once_per_owner UNIQUE (name,owner)
152 CREATE INDEX acq_picklist_owner_idx ON acq.picklist ( owner );
153 CREATE INDEX acq_picklist_creator_idx ON acq.picklist ( creator );
154 CREATE INDEX acq_picklist_editor_idx ON acq.picklist ( editor );
156 CREATE TABLE acq.purchase_order (
157 id SERIAL PRIMARY KEY,
158 owner INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
159 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
160 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
161 ordering_agency INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
162 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
163 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
164 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
165 state TEXT NOT NULL DEFAULT 'new',
166 order_date TIMESTAMP WITH TIME ZONE,
169 CREATE INDEX po_owner_idx ON acq.purchase_order (owner);
170 CREATE INDEX po_provider_idx ON acq.purchase_order (provider);
171 CREATE INDEX po_state_idx ON acq.purchase_order (state);
172 CREATE INDEX po_creator_idx ON acq.purchase_order ( creator );
173 CREATE INDEX po_editor_idx ON acq.purchase_order ( editor );
174 CREATE INDEX acq_po_org_name_order_date_idx ON acq.purchase_order( ordering_agency, name, order_date );
176 -- The name should default to the id, as text. We can't reference a column
177 -- in a DEFAULT clause, so we use a trigger:
179 CREATE OR REPLACE FUNCTION acq.purchase_order_name_default () RETURNS TRIGGER
182 IF NEW.name IS NULL THEN
183 NEW.name := NEW.id::TEXT;
190 CREATE TRIGGER po_name_default_trg
191 BEFORE INSERT OR UPDATE ON acq.purchase_order
192 FOR EACH ROW EXECUTE PROCEDURE acq.purchase_order_name_default ();
194 -- The order name should be unique for a given ordering agency on a given order date
195 -- (truncated to midnight), but only where the order_date is not NULL. Conceptually
196 -- this rule requires a check constraint with a subquery. However you can't have a
197 -- subquery in a CHECK constraint, so we fake it with a trigger.
199 CREATE OR REPLACE FUNCTION acq.po_org_name_date_unique () RETURNS TRIGGER
205 -- If order_date is not null, then make sure we don't have a collision
206 -- on order_date (truncated to day), org, and name
208 IF NEW.order_date IS NULL THEN
212 -- In the WHERE clause, we compare the order_dates without regard to time of day.
213 -- We use a pair of inequalities instead of comparing truncated dates so that the
214 -- query can do an indexed range scan.
216 SELECT 1 INTO collision
217 FROM acq.purchase_order
219 ordering_agency = NEW.ordering_agency
221 AND order_date >= date_trunc( 'day', NEW.order_date )
222 AND order_date < date_trunc( 'day', NEW.order_date ) + '1 day'::INTERVAL
225 IF collision IS NULL THEN
226 -- okay, no collision
229 -- collision; nip it in the bud
230 RAISE EXCEPTION 'Colliding purchase orders: ordering_agency %, date %, name ''%''',
231 NEW.ordering_agency, NEW.order_date, NEW.name;
236 CREATE TRIGGER po_org_name_date_unique_trg
237 BEFORE INSERT OR UPDATE ON acq.purchase_order
238 FOR EACH ROW EXECUTE PROCEDURE acq.po_org_name_date_unique ();
240 CREATE TABLE acq.po_note (
241 id SERIAL PRIMARY KEY,
242 purchase_order INT NOT NULL REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
243 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
244 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
245 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
246 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
249 CREATE INDEX po_note_po_idx ON acq.po_note (purchase_order);
250 CREATE INDEX acq_po_note_creator_idx ON acq.po_note ( creator );
251 CREATE INDEX acq_po_note_editor_idx ON acq.po_note ( editor );
253 CREATE TABLE acq.lineitem (
254 id BIGSERIAL PRIMARY KEY,
255 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
256 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
257 selector INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
258 provider INT REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED,
259 purchase_order INT REFERENCES acq.purchase_order (id) DEFERRABLE INITIALLY DEFERRED,
260 picklist INT REFERENCES acq.picklist (id) DEFERRABLE INITIALLY DEFERRED,
261 expected_recv_time TIMESTAMP WITH TIME ZONE,
262 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
263 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
265 eg_bib_id INT REFERENCES biblio.record_entry (id) DEFERRABLE INITIALLY DEFERRED,
267 item_count INT NOT NULL DEFAULT 0,
268 state TEXT NOT NULL DEFAULT 'new',
269 CONSTRAINT picklist_or_po CHECK (picklist IS NOT NULL OR purchase_order IS NOT NULL)
271 CREATE INDEX li_po_idx ON acq.lineitem (purchase_order);
272 CREATE INDEX li_pl_idx ON acq.lineitem (picklist);
273 CREATE INDEX li_creator_idx ON acq.lineitem ( creator );
274 CREATE INDEX li_editor_idx ON acq.lineitem ( editor );
275 CREATE INDEX li_selector_idx ON acq.lineitem ( selector );
277 CREATE TABLE acq.lineitem_note (
278 id SERIAL PRIMARY KEY,
279 lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
280 creator INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
281 editor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
282 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
283 edit_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
286 CREATE INDEX li_note_li_idx ON acq.lineitem_note (lineitem);
287 CREATE INDEX li_note_creator_idx ON acq.lineitem_note ( creator );
288 CREATE INDEX li_note_editor_idx ON acq.lineitem_note ( editor );
290 CREATE TABLE acq.lineitem_detail (
291 id BIGSERIAL PRIMARY KEY,
292 lineitem INT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
293 fund INT REFERENCES acq.fund (id) DEFERRABLE INITIALLY DEFERRED,
294 fund_debit INT REFERENCES acq.fund_debit (id) DEFERRABLE INITIALLY DEFERRED,
295 eg_copy_id BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
299 collection_code TEXT,
300 circ_modifier TEXT REFERENCES config.circ_modifier (code) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
301 owning_lib INT REFERENCES actor.org_unit (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
302 location INT REFERENCES asset.copy_location (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
303 recv_time TIMESTAMP WITH TIME ZONE
306 CREATE INDEX li_detail_li_idx ON acq.lineitem_detail (lineitem);
308 CREATE TABLE acq.lineitem_attr_definition (
309 id BIGSERIAL PRIMARY KEY,
311 description TEXT NOT NULL,
312 remove TEXT NOT NULL DEFAULT '',
313 ident BOOL NOT NULL DEFAULT FALSE
316 CREATE TABLE acq.lineitem_marc_attr_definition (
317 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
319 ) INHERITS (acq.lineitem_attr_definition);
321 CREATE TABLE acq.lineitem_provider_attr_definition (
322 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
324 provider INT NOT NULL REFERENCES acq.provider (id) DEFERRABLE INITIALLY DEFERRED
325 ) INHERITS (acq.lineitem_attr_definition);
327 CREATE TABLE acq.lineitem_generated_attr_definition (
328 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
330 ) INHERITS (acq.lineitem_attr_definition);
332 CREATE TABLE acq.lineitem_usr_attr_definition (
333 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq'),
334 usr INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED
335 ) INHERITS (acq.lineitem_attr_definition);
336 CREATE INDEX li_usr_attr_def_usr_idx ON acq.lineitem_usr_attr_definition ( usr );
338 CREATE TABLE acq.lineitem_local_attr_definition (
339 id BIGINT PRIMARY KEY DEFAULT NEXTVAL('acq.lineitem_attr_definition_id_seq')
340 ) INHERITS (acq.lineitem_attr_definition);
342 CREATE TABLE acq.lineitem_attr (
343 id BIGSERIAL PRIMARY KEY,
344 definition BIGINT NOT NULL,
345 lineitem BIGINT NOT NULL REFERENCES acq.lineitem (id) DEFERRABLE INITIALLY DEFERRED,
346 attr_type TEXT NOT NULL,
347 attr_name TEXT NOT NULL,
348 attr_value TEXT NOT NULL
351 CREATE INDEX li_attr_li_idx ON acq.lineitem_attr (lineitem);
352 CREATE INDEX li_attr_value_idx ON acq.lineitem_attr (attr_value);
353 CREATE INDEX li_attr_definition_idx ON acq.lineitem_attr (definition);
359 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('title','Title of work','//*[@tag="245"]/*[contains("abcmnopr",@code)]');
360 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)]');
361 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('language','Language of work','//*[@tag="240"]/*[@code="l"][1]');
362 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pagination','Pagination','//*[@tag="300"]/*[@code="a"][1]');
363 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('isbn','ISBN','//*[@tag="020"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
364 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath, remove ) VALUES ('issn','ISSN','//*[@tag="022"]/*[@code="a"]', $r$(?:-|\s.+$)$r$);
365 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('price','Price','//*[@tag="020" or @tag="022"]/*[@code="c"][1]');
366 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('identifier','Identifier','//*[@tag="001"]');
367 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('publisher','Publisher','//*[@tag="260"]/*[@code="b"][1]');
368 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('pubdate','Publication Date','//*[@tag="260"]/*[@code="c"][1]');
369 INSERT INTO acq.lineitem_marc_attr_definition ( code, description, xpath ) VALUES ('edition','Edition','//*[@tag="250"]/*[@code="a"][1]');
371 INSERT INTO acq.lineitem_local_attr_definition ( code, description ) VALUES ('estimated_price', 'Estimated Price');
374 CREATE TABLE acq.distribution_formula (
375 id SERIAL PRIMARY KEY,
377 REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
379 skip_count INT NOT NULL DEFAULT 0,
380 CONSTRAINT acqdf_name_once_per_owner UNIQUE (name, owner)
383 CREATE TABLE acq.distribution_formula_entry (
384 id SERIAL PRIMARY KEY,
385 formula INTEGER NOT NULL REFERENCES acq.distribution_formula(id)
387 DEFERRABLE INITIALLY DEFERRED,
388 position INTEGER NOT NULL,
389 item_count INTEGER NOT NULL,
390 owning_lib INTEGER REFERENCES actor.org_unit(id)
391 DEFERRABLE INITIALLY DEFERRED,
392 location INTEGER REFERENCES asset.copy_location(id),
393 CONSTRAINT acqdfe_lib_once_per_formula UNIQUE( formula, position ),
394 CONSTRAINT acqdfe_must_be_somewhere
395 CHECK( owning_lib IS NOT NULL OR location IS NOT NULL )
398 CREATE TABLE acq.fund_tag (
399 id SERIAL PRIMARY KEY,
401 REFERENCES actor.org_unit(id) DEFERRABLE INITIALLY DEFERRED,
403 CONSTRAINT acqft_tag_once_per_owner UNIQUE (name, owner)
406 CREATE TABLE acq.fund_tag_map (
407 id SERIAL PRIMARY KEY,
408 fund INTEGER NOT NULL REFERENCES acq.fund(id)
409 DEFERRABLE INITIALLY DEFERRED,
410 tag INTEGER REFERENCES acq.fund_tag(id)
412 DEFERRABLE INITIALLY DEFERRED,
413 CONSTRAINT acqftm_fund_once_per_tag UNIQUE( fund, tag )
416 CREATE TABLE acq.fund_transfer (
417 id SERIAL PRIMARY KEY,
418 src_fund INT NOT NULL REFERENCES acq.fund( id )
419 DEFERRABLE INITIALLY DEFERRED,
420 src_amount NUMERIC NOT NULL,
421 dest_fund INT NOT NULL REFERENCES acq.fund( id )
422 DEFERRABLE INITIALLY DEFERRED,
423 dest_amount NUMERIC NOT NULL,
424 transfer_time TIMESTAMPTZ NOT NULL DEFAULT now(),
425 transfer_user INT NOT NULL REFERENCES actor.usr( id )
426 DEFERRABLE INITIALLY DEFERRED,
430 CREATE INDEX acqftr_usr_idx
431 ON acq.fund_transfer( transfer_user );
433 COMMENT ON TABLE acq.fund_transfer IS $$
435 * Copyright (C) 2009 Georgia Public Library Service
436 * Scott McKellar <scott@esilibrary.com>
440 * Each row represents the transfer of money from a source fund
441 * to a destination fund. There should be corresponding entries
442 * in acq.fund_allocation. The purpose of acq.fund_transfer is
443 * to record how much money moved from which fund to which other
446 * The presence of two amount fields, rather than one, reflects
447 * the possibility that the two funds are denominated in different
448 * currencies. If they use the same currency type, the two
449 * amounts should be the same.
453 * This program is free software; you can redistribute it and/or
454 * modify it under the terms of the GNU General Public License
455 * as published by the Free Software Foundation; either version 2
456 * of the License, or (at your option) any later version.
458 * This program is distributed in the hope that it will be useful,
459 * but WITHOUT ANY WARRANTY; without even the implied warranty of
460 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
461 * GNU General Public License for more details.
465 CREATE TABLE acq.fiscal_calendar (
466 id SERIAL PRIMARY KEY,
470 -- Create a default calendar (though we don't specify its contents).
471 -- Create a foreign key in actor.org_unit, initially pointing to
472 -- the default calendar.
474 INSERT INTO acq.fiscal_calendar (
481 ALTER TABLE actor.org_unit
482 ADD COLUMN fiscal_calendar INT NOT NULL
483 REFERENCES acq.fiscal_calendar( id )
484 DEFERRABLE INITIALLY DEFERRED
487 CREATE TABLE acq.fiscal_year (
488 id SERIAL PRIMARY KEY,
489 calendar INT NOT NULL
490 REFERENCES acq.fiscal_calendar
492 DEFERRABLE INITIALLY DEFERRED,
494 year_begin TIMESTAMPTZ NOT NULL,
495 year_end TIMESTAMPTZ NOT NULL,
496 CONSTRAINT acq_fy_logical_key UNIQUE ( calendar, year ),
497 CONSTRAINT acq_fy_physical_key UNIQUE ( calendar, year_begin )
502 CREATE TYPE acq.flat_lineitem_holding_subfield AS (lineitem int, holding int, subfield text, data text);
503 CREATE OR REPLACE FUNCTION acq.extract_holding_attr_table (lineitem int, tag text) RETURNS SETOF acq.flat_lineitem_holding_subfield AS $$
506 lida acq.flat_lineitem_holding_subfield%ROWTYPE;
509 SELECT COUNT(*) INTO counter
514 '//*[@tag="' || tag || '"]',
516 ) as t(i int,c text);
518 FOR i IN 1 .. counter LOOP
521 FROM ( SELECT id,i,t,v
526 '//*[@tag="' || tag || '"][position()=' || i || ']/*/@code|' ||
527 '//*[@tag="' || tag || '"][position()=' || i || ']/*[@code]',
529 ) as t(id int,t text,v text)
540 CREATE TYPE acq.flat_lineitem_detail AS (lineitem int, holding int, attr text, data text);
541 CREATE OR REPLACE FUNCTION acq.extract_provider_holding_data ( lineitem_i int ) RETURNS SETOF acq.flat_lineitem_detail AS $$
545 lida acq.flat_lineitem_detail%ROWTYPE;
547 SELECT provider INTO prov_i FROM acq.lineitem WHERE id = lineitem_i;
548 IF NOT FOUND THEN RETURN; END IF;
550 SELECT holding_tag INTO tag_t FROM acq.provider WHERE id = prov_i;
551 IF NOT FOUND OR tag_t IS NULL THEN RETURN; END IF;
558 FROM acq.extract_holding_attr_table( lineitem_i, tag_t ) h
559 JOIN acq.provider_holding_subfield_map a USING (subfield)
560 WHERE a.provider = prov_i
569 -- select * from acq.extract_provider_holding_data(699);
571 CREATE OR REPLACE FUNCTION public.extract_acq_marc_field ( BIGINT, TEXT, TEXT) RETURNS TEXT AS $$
572 SELECT public.extract_marc_field('acq.lineitem', $1, $2, $3);
576 CREATE OR REPLACE FUNCTION public.extract_bib_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
577 SELECT public.extract_marc_field('biblio.record_entry', $1, $2);
580 CREATE OR REPLACE FUNCTION public.extract_authority_marc_field ( BIGINT, TEXT ) RETURNS TEXT AS $$
581 SELECT public.extract_marc_field('authority.record_entry', $1, $2);
585 -- INSERT INTO acq.lineitem_provider_attr_definition ( provider, code, description, xpath ) VALUES (1,'price','Price','//*[@tag="020" or @tag="022"]/*[@code="a"][1]');
588 Suggested vendor fields:
596 CREATE OR REPLACE FUNCTION public.ingest_acq_marc ( ) RETURNS TRIGGER AS $$
604 FOR adef IN SELECT *,tableoid FROM acq.lineitem_attr_definition LOOP
606 SELECT relname::TEXT INTO atype FROM pg_class WHERE oid = adef.tableoid;
608 IF (atype NOT IN ('lineitem_usr_attr_definition','lineitem_local_attr_definition')) THEN
609 IF (atype = 'lineitem_provider_attr_definition') THEN
610 SELECT provider INTO prov FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
611 CONTINUE WHEN NEW.provider IS NULL OR prov <> NEW.provider;
614 IF (atype = 'lineitem_provider_attr_definition') THEN
615 SELECT xpath INTO xpath_string FROM acq.lineitem_provider_attr_definition WHERE id = adef.id;
616 ELSIF (atype = 'lineitem_marc_attr_definition') THEN
617 SELECT xpath INTO xpath_string FROM acq.lineitem_marc_attr_definition WHERE id = adef.id;
618 ELSIF (atype = 'lineitem_generated_attr_definition') THEN
619 SELECT xpath INTO xpath_string FROM acq.lineitem_generated_attr_definition WHERE id = adef.id;
622 SELECT extract_acq_marc_field(id, xpath_string, adef.remove) INTO value FROM acq.lineitem WHERE id = NEW.id;
624 IF (value IS NOT NULL AND value <> '') THEN
625 INSERT INTO acq.lineitem_attr (lineitem, definition, attr_type, attr_name, attr_value)
626 VALUES (NEW.id, adef.id, atype, adef.code, value);
637 CREATE OR REPLACE FUNCTION public.cleanup_acq_marc ( ) RETURNS TRIGGER AS $$
639 IF TG_OP = 'UPDATE' THEN
640 DELETE FROM acq.lineitem_attr
641 WHERE lineitem = OLD.id AND attr_type IN ('lineitem_provider_attr_definition', 'lineitem_marc_attr_definition','lineitem_generated_attr_definition');
644 DELETE FROM acq.lineitem_attr WHERE lineitem = OLD.id;
650 CREATE TRIGGER cleanup_lineitem_trigger
651 BEFORE UPDATE OR DELETE ON acq.lineitem
652 FOR EACH ROW EXECUTE PROCEDURE public.cleanup_acq_marc();
654 CREATE TRIGGER ingest_lineitem_trigger
655 AFTER INSERT OR UPDATE ON acq.lineitem
656 FOR EACH ROW EXECUTE PROCEDURE public.ingest_acq_marc();
658 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( from_ex TEXT, to_ex TEXT ) RETURNS NUMERIC AS $$
662 IF from_ex = to_ex THEN
666 SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = from_ex AND to_currency = to_ex;
671 SELECT ratio INTO rat FROM acq.exchange_rate WHERE from_currency = to_ex AND to_currency = from_ex;
682 CREATE OR REPLACE FUNCTION acq.exchange_ratio ( TEXT, TEXT, NUMERIC ) RETURNS NUMERIC AS $$
683 SELECT $3 * acq.exchange_ratio($1, $2);
686 CREATE OR REPLACE FUNCTION acq.find_bad_fy()
688 Examine the acq.fiscal_year table, comparing successive years.
689 Report any inconsistencies, i.e. years that overlap, have gaps
690 between them, or are out of sequence.
692 RETURNS SETOF RECORD AS $$
716 ELSIF curr_year.calendar = prev_year.calendar THEN
717 IF curr_year.year_begin > prev_year.year_end THEN
718 -- This ugly kludge works around the fact that older
719 -- versions of PostgreSQL don't support RETURN QUERY SELECT
720 FOR return_rec IN SELECT
723 'Gap between fiscal years'::TEXT
725 RETURN NEXT return_rec;
727 ELSIF curr_year.year_begin < prev_year.year_end THEN
728 FOR return_rec IN SELECT
731 'Overlapping fiscal years'::TEXT
733 RETURN NEXT return_rec;
735 ELSIF curr_year.year < prev_year.year THEN
736 FOR return_rec IN SELECT
739 'Fiscal years out of order'::TEXT
741 RETURN NEXT return_rec;
746 prev_year := curr_year;
753 CREATE OR REPLACE VIEW acq.funding_source_credit_total AS
754 SELECT funding_source,
755 SUM(amount) AS amount
756 FROM acq.funding_source_credit
759 CREATE OR REPLACE VIEW acq.funding_source_allocation_total AS
760 SELECT funding_source,
761 SUM(a.amount)::NUMERIC(100,2) AS amount
762 FROM acq.fund_allocation a
765 CREATE OR REPLACE VIEW acq.funding_source_balance AS
766 SELECT COALESCE(c.funding_source, a.funding_source) AS funding_source,
767 SUM(COALESCE(c.amount,0.0) - COALESCE(a.amount,0.0))::NUMERIC(100,2) AS amount
768 FROM acq.funding_source_credit_total c
769 FULL JOIN acq.funding_source_allocation_total a USING (funding_source)
772 CREATE OR REPLACE VIEW acq.fund_allocation_total AS
774 SUM(a.amount * acq.exchange_ratio(s.currency_type, f.currency_type))::NUMERIC(100,2) AS amount
775 FROM acq.fund_allocation a
776 JOIN acq.fund f ON (a.fund = f.id)
777 JOIN acq.funding_source s ON (a.funding_source = s.id)
780 CREATE OR REPLACE VIEW acq.fund_debit_total AS
783 SUM(amount) AS amount
787 CREATE OR REPLACE VIEW acq.fund_encumbrance_total AS
789 SUM(amount) AS amount
790 FROM acq.fund_debit_total
791 WHERE encumbrance IS TRUE
794 CREATE OR REPLACE VIEW acq.fund_spent_total AS
796 SUM(amount) AS amount
797 FROM acq.fund_debit_total
798 WHERE encumbrance IS FALSE
801 CREATE OR REPLACE VIEW acq.fund_combined_balance AS
803 c.amount - COALESCE(d.amount,0.0) AS amount
804 FROM acq.fund_allocation_total c
805 LEFT JOIN acq.fund_debit_total d USING (fund);
807 CREATE OR REPLACE VIEW acq.fund_spent_balance AS
809 c.amount - COALESCE(d.amount,0.0) AS amount
810 FROM acq.fund_allocation_total c
811 LEFT JOIN acq.fund_spent_total d USING (fund);