LP#1379815 Add code to assign stat cats on Vandelay imported items
authorRemington Steed <rjs7@calvin.edu>
Mon, 8 Sep 2014 20:11:37 +0000 (16:11 -0400)
committerBen Shum <bshum@biblio.org>
Wed, 19 Aug 2015 16:14:02 +0000 (12:14 -0400)
This commit adds columns to the Holdings Import Profile table
(vandelay.import_item_attr_definition) and to vandelay.import_item, adds
the corresponding fields to the IDL, modifies the database functions
which import the data (ingest_bib_items() and ingest_items()) and adds
code that parses the imported stat cat data (of the form
CAT 1|VALUE 1||CAT 2|VALUE 2) and applies it to the appropriate
overlayed copy.

Signed-off-by: Remington Steed <rjs7@calvin.edu>
Signed-off-by: Dan Wells <dbw2@calvin.edu>
Signed-off-by: Ben Shum <bshum@biblio.org>

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Vandelay.pm
Open-ILS/src/sql/Pg/012.schema.vandelay.sql
Open-ILS/src/sql/Pg/999.functions.global.sql
Open-ILS/src/sql/Pg/upgrade/YYYY.schema.vandelay-stat-cat-import.sql [new file with mode: 0644]

index 9776286..4686e7f 100644 (file)
@@ -294,6 +294,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Private Note" name="priv_note" reporter:datatype="text"/>
                        <field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="bool"/>
                        <field reporter:label="Overlay Match ID" name="internal_id" reporter:datatype="int"/>
+                       <field reporter:label="Stat Cat Data" name="stat_cat_data" reporter:datatype="text"/>
                </fields>
                <links>
                        <link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
@@ -346,6 +347,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="OPAC Visible" name="opac_visible" reporter:datatype="text"/>
                        <field reporter:label="Copy Number" name="copy_number" reporter:datatype="text"/>
                        <field reporter:label="Overlay Match ID" name="internal_id" reporter:datatype="text"/>
+                       <field reporter:label="Stat Cat Data" name="stat_cat_data" reporter:datatype="text"/>
                </fields>
                <links>
                        <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
index 588642d..b5ae6f6 100644 (file)
@@ -1916,6 +1916,33 @@ sub import_record_asset_list_impl {
                 }
             }
 
+            if ($item->stat_cat_data) {
+                $logger->info("vl: parsing stat cat data: " . $item->stat_cat_data);
+                my @stat_cat_pairs = split('\|\|', $item->stat_cat_data);
+                my $stat_cat_entries = [];
+                # lookup stat cats
+                foreach my $stat_cat_pair (@stat_cat_pairs) {
+                    my ($stat_cat, $stat_cat_entry);
+                    my @pair_pieces = split('\|', $stat_cat_pair);
+                    if (@pair_pieces == 2) {
+                        $stat_cat = $e->search_asset_stat_cat({name=>$pair_pieces[0]})->[0];
+                        if ($stat_cat) {
+                            $stat_cat_entry = $e->search_asset_stat_cat_entry({'value' => $pair_pieces[1], 'stat_cat' => $stat_cat->id})->[0];
+                            push (@$stat_cat_entries, $stat_cat_entry) if $stat_cat_entry;
+                        }
+                    } else {
+                        $logger->warn("Bad format for stat cat data ($stat_cat_pair), should be like: CAT 1|VALUE 1");
+                    }
+
+                    if (!$stat_cat or !$stat_cat_entry) {
+                        $logger->warn("Invalid stat cat data ($stat_cat_pair): " . $e->die_event);
+                    }
+                }
+                $copy->stat_cat_entries( $stat_cat_entries );
+                $copy->ischanged(1);
+                $evt = OpenILS::Application::Cat::AssetCommon->update_copy_stat_entries($e, $copy, 0, 1); #delete_stats=0, add_or_update_only=1
+            }
+
             # set the import data on the import item
             $item->imported_as($copy->id); # $copy->id is set by create_copy() ^--
             $item->import_time('now');
index 27eb528..f750adb 100644 (file)
@@ -107,6 +107,7 @@ CREATE TABLE vandelay.import_item_attr_definition (
     priv_note_title TEXT,
     priv_note       TEXT,
     internal_id     TEXT,
+    stat_cat_data   TEXT,
        CONSTRAINT vand_import_item_attr_def_idx UNIQUE (owner,name)
 );
 
@@ -178,6 +179,7 @@ CREATE TABLE vandelay.import_item (
     alert_message   TEXT,
     pub_note        TEXT,
     priv_note       TEXT,
+    stat_cat_data   TEXT,
     opac_visible    BOOL,
     internal_id     BIGINT -- queue_type == 'acq' ? acq.lineitem_detail.id : asset.copy.id
 );
index bb87a6b..589eab8 100644 (file)
@@ -1724,12 +1724,13 @@ DECLARE
     pub_note        TEXT;
     priv_note       TEXT;
     internal_id     TEXT;
+    stat_cat_data   TEXT;
 
     attr_def        RECORD;
     tmp_attr_set    RECORD;
     attr_set        vandelay.import_item%ROWTYPE;
 
-    xpath           TEXT;
+    xpaths          TEXT[];
     tmp_str         TEXT;
 
 BEGIN
@@ -1745,172 +1746,162 @@ BEGIN
         owning_lib :=
             CASE
                 WHEN attr_def.owning_lib IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.owning_lib
+                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '*[@code="' || attr_def.owning_lib || '"]'
+                ELSE '*' || attr_def.owning_lib
             END;
 
         circ_lib :=
             CASE
                 WHEN attr_def.circ_lib IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_lib || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_lib
+                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '*[@code="' || attr_def.circ_lib || '"]'
+                ELSE '*' || attr_def.circ_lib
             END;
 
         call_number :=
             CASE
                 WHEN attr_def.call_number IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.call_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.call_number || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.call_number
+                WHEN LENGTH( attr_def.call_number ) = 1 THEN '*[@code="' || attr_def.call_number || '"]'
+                ELSE '*' || attr_def.call_number
             END;
 
         copy_number :=
             CASE
                 WHEN attr_def.copy_number IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.copy_number || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.copy_number
+                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '*[@code="' || attr_def.copy_number || '"]'
+                ELSE '*' || attr_def.copy_number
             END;
 
         status :=
             CASE
                 WHEN attr_def.status IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.status ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.status || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.status
+                WHEN LENGTH( attr_def.status ) = 1 THEN '*[@code="' || attr_def.status || '"]'
+                ELSE '*' || attr_def.status
             END;
 
         location :=
             CASE
                 WHEN attr_def.location IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.location ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.location || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.location
+                WHEN LENGTH( attr_def.location ) = 1 THEN '*[@code="' || attr_def.location || '"]'
+                ELSE '*' || attr_def.location
             END;
 
         circulate :=
             CASE
                 WHEN attr_def.circulate IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circulate ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circulate || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circulate
+                WHEN LENGTH( attr_def.circulate ) = 1 THEN '*[@code="' || attr_def.circulate || '"]'
+                ELSE '*' || attr_def.circulate
             END;
 
         deposit :=
             CASE
                 WHEN attr_def.deposit IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.deposit ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit
+                WHEN LENGTH( attr_def.deposit ) = 1 THEN '*[@code="' || attr_def.deposit || '"]'
+                ELSE '*' || attr_def.deposit
             END;
 
         deposit_amount :=
             CASE
                 WHEN attr_def.deposit_amount IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.deposit_amount || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.deposit_amount
+                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '*[@code="' || attr_def.deposit_amount || '"]'
+                ELSE '*' || attr_def.deposit_amount
             END;
 
         ref :=
             CASE
                 WHEN attr_def.ref IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.ref ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.ref || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.ref
+                WHEN LENGTH( attr_def.ref ) = 1 THEN '*[@code="' || attr_def.ref || '"]'
+                ELSE '*' || attr_def.ref
             END;
 
         holdable :=
             CASE
                 WHEN attr_def.holdable IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.holdable ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.holdable || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.holdable
+                WHEN LENGTH( attr_def.holdable ) = 1 THEN '*[@code="' || attr_def.holdable || '"]'
+                ELSE '*' || attr_def.holdable
             END;
 
         price :=
             CASE
                 WHEN attr_def.price IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.price ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.price || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.price
+                WHEN LENGTH( attr_def.price ) = 1 THEN '*[@code="' || attr_def.price || '"]'
+                ELSE '*' || attr_def.price
             END;
 
         barcode :=
             CASE
                 WHEN attr_def.barcode IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.barcode ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.barcode || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.barcode
+                WHEN LENGTH( attr_def.barcode ) = 1 THEN '*[@code="' || attr_def.barcode || '"]'
+                ELSE '*' || attr_def.barcode
             END;
 
         circ_modifier :=
             CASE
                 WHEN attr_def.circ_modifier IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_modifier || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_modifier
+                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '*[@code="' || attr_def.circ_modifier || '"]'
+                ELSE '*' || attr_def.circ_modifier
             END;
 
         circ_as_type :=
             CASE
                 WHEN attr_def.circ_as_type IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.circ_as_type || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.circ_as_type
+                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '*[@code="' || attr_def.circ_as_type || '"]'
+                ELSE '*' || attr_def.circ_as_type
             END;
 
         alert_message :=
             CASE
                 WHEN attr_def.alert_message IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.alert_message || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.alert_message
+                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '*[@code="' || attr_def.alert_message || '"]'
+                ELSE '*' || attr_def.alert_message
             END;
 
         opac_visible :=
             CASE
                 WHEN attr_def.opac_visible IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.opac_visible || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.opac_visible
+                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '*[@code="' || attr_def.opac_visible || '"]'
+                ELSE '*' || attr_def.opac_visible
             END;
 
         pub_note :=
             CASE
                 WHEN attr_def.pub_note IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.pub_note || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.pub_note
+                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '*[@code="' || attr_def.pub_note || '"]'
+                ELSE '*' || attr_def.pub_note
             END;
         priv_note :=
             CASE
                 WHEN attr_def.priv_note IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.priv_note || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.priv_note
+                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '*[@code="' || attr_def.priv_note || '"]'
+                ELSE '*' || attr_def.priv_note
             END;
 
         internal_id :=
             CASE
                 WHEN attr_def.internal_id IS NULL THEN 'null()'
-                WHEN LENGTH( attr_def.internal_id ) = 1 THEN '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.internal_id || '"]'
-                ELSE '//*[@tag="' || attr_def.tag || '"]/*' || attr_def.internal_id
+                WHEN LENGTH( attr_def.internal_id ) = 1 THEN '*[@code="' || attr_def.internal_id || '"]'
+                ELSE '*' || attr_def.internal_id
+            END;
+
+        stat_cat_data :=
+            CASE
+                WHEN attr_def.stat_cat_data IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.stat_cat_data ) = 1 THEN '*[@code="' || attr_def.stat_cat_data || '"]'
+                ELSE '*' || attr_def.stat_cat_data
             END;
 
 
 
-        xpath :=
-            owning_lib      || '|' ||
-            circ_lib        || '|' ||
-            call_number     || '|' ||
-            copy_number     || '|' ||
-            status          || '|' ||
-            location        || '|' ||
-            circulate       || '|' ||
-            deposit         || '|' ||
-            deposit_amount  || '|' ||
-            ref             || '|' ||
-            holdable        || '|' ||
-            price           || '|' ||
-            barcode         || '|' ||
-            circ_modifier   || '|' ||
-            circ_as_type    || '|' ||
-            alert_message   || '|' ||
-            pub_note        || '|' ||
-            priv_note       || '|' ||
-            internal_id     || '|' ||
-            opac_visible;
+        xpaths := ARRAY[owning_lib, circ_lib, call_number, copy_number, status, location, circulate,
+                        deposit, deposit_amount, ref, holdable, price, barcode, circ_modifier, circ_as_type,
+                        alert_message, pub_note, priv_note, internal_id, stat_cat_data, opac_visible];
 
         FOR tmp_attr_set IN
                 SELECT  *
-                  FROM  oils_xpath_table( 'id', 'marc', 'vandelay.queued_bib_record', xpath, 'id = ' || import_id )
-                            AS t( id INT, ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
+                  FROM  oils_xpath_tag_to_table( (SELECT marc FROM vandelay.queued_bib_record WHERE id = import_id), attr_def.tag, xpaths)
+                            AS t( ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
                                   dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
-                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, internal_id TEXT, opac_vis TEXT )
+                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, internal_id TEXT,
+                                  stat_cat_data TEXT, opac_vis TEXT )
         LOOP
 
             attr_set.import_error := NULL;
@@ -2092,6 +2083,7 @@ BEGIN
             attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
             attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
             attr_set.internal_id    := tmp_attr_set.internal_id::BIGINT;
+            attr_set.stat_cat_data  := tmp_attr_set.stat_cat_data; -- TEXT,
 
             RETURN NEXT attr_set;
 
@@ -2143,6 +2135,7 @@ BEGIN
             priv_note,
             internal_id,
             opac_visible,
+            stat_cat_data,
             import_error,
             error_detail
         ) VALUES (
@@ -2168,6 +2161,7 @@ BEGIN
             item_data.priv_note,
             item_data.internal_id,
             item_data.opac_visible,
+            item_data.stat_cat_data,
             item_data.import_error,
             item_data.error_detail
         );
diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.vandelay-stat-cat-import.sql b/Open-ILS/src/sql/Pg/upgrade/YYYY.schema.vandelay-stat-cat-import.sql
new file mode 100644 (file)
index 0000000..0256b1b
--- /dev/null
@@ -0,0 +1,476 @@
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+ALTER TABLE vandelay.import_item_attr_definition
+    ADD COLUMN stat_cat_data TEXT;
+
+ALTER TABLE vandelay.import_item
+    ADD COLUMN stat_cat_data TEXT;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_items ( import_id BIGINT, attr_def_id BIGINT ) RETURNS SETOF vandelay.import_item AS $$
+DECLARE
+
+    owning_lib      TEXT;
+    circ_lib        TEXT;
+    call_number     TEXT;
+    copy_number     TEXT;
+    status          TEXT;
+    location        TEXT;
+    circulate       TEXT;
+    deposit         TEXT;
+    deposit_amount  TEXT;
+    ref             TEXT;
+    holdable        TEXT;
+    price           TEXT;
+    barcode         TEXT;
+    circ_modifier   TEXT;
+    circ_as_type    TEXT;
+    alert_message   TEXT;
+    opac_visible    TEXT;
+    pub_note        TEXT;
+    priv_note       TEXT;
+    internal_id     TEXT;
+    stat_cat_data   TEXT;
+
+    attr_def        RECORD;
+    tmp_attr_set    RECORD;
+    attr_set        vandelay.import_item%ROWTYPE;
+
+    xpaths          TEXT[];
+    tmp_str         TEXT;
+
+BEGIN
+
+    SELECT * INTO attr_def FROM vandelay.import_item_attr_definition WHERE id = attr_def_id;
+
+    IF FOUND THEN
+
+        attr_set.definition := attr_def.id;
+
+        -- Build the combined XPath
+
+        owning_lib :=
+            CASE
+                WHEN attr_def.owning_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.owning_lib ) = 1 THEN '*[@code="' || attr_def.owning_lib || '"]'
+                ELSE '*' || attr_def.owning_lib
+            END;
+
+        circ_lib :=
+            CASE
+                WHEN attr_def.circ_lib IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_lib ) = 1 THEN '*[@code="' || attr_def.circ_lib || '"]'
+                ELSE '*' || attr_def.circ_lib
+            END;
+
+        call_number :=
+            CASE
+                WHEN attr_def.call_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.call_number ) = 1 THEN '*[@code="' || attr_def.call_number || '"]'
+                ELSE '*' || attr_def.call_number
+            END;
+
+        copy_number :=
+            CASE
+                WHEN attr_def.copy_number IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.copy_number ) = 1 THEN '*[@code="' || attr_def.copy_number || '"]'
+                ELSE '*' || attr_def.copy_number
+            END;
+
+        status :=
+            CASE
+                WHEN attr_def.status IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.status ) = 1 THEN '*[@code="' || attr_def.status || '"]'
+                ELSE '*' || attr_def.status
+            END;
+
+        location :=
+            CASE
+                WHEN attr_def.location IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.location ) = 1 THEN '*[@code="' || attr_def.location || '"]'
+                ELSE '*' || attr_def.location
+            END;
+
+        circulate :=
+            CASE
+                WHEN attr_def.circulate IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circulate ) = 1 THEN '*[@code="' || attr_def.circulate || '"]'
+                ELSE '*' || attr_def.circulate
+            END;
+
+        deposit :=
+            CASE
+                WHEN attr_def.deposit IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit ) = 1 THEN '*[@code="' || attr_def.deposit || '"]'
+                ELSE '*' || attr_def.deposit
+            END;
+
+        deposit_amount :=
+            CASE
+                WHEN attr_def.deposit_amount IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.deposit_amount ) = 1 THEN '*[@code="' || attr_def.deposit_amount || '"]'
+                ELSE '*' || attr_def.deposit_amount
+            END;
+
+        ref :=
+            CASE
+                WHEN attr_def.ref IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.ref ) = 1 THEN '*[@code="' || attr_def.ref || '"]'
+                ELSE '*' || attr_def.ref
+            END;
+
+        holdable :=
+            CASE
+                WHEN attr_def.holdable IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.holdable ) = 1 THEN '*[@code="' || attr_def.holdable || '"]'
+                ELSE '*' || attr_def.holdable
+            END;
+
+        price :=
+            CASE
+                WHEN attr_def.price IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.price ) = 1 THEN '*[@code="' || attr_def.price || '"]'
+                ELSE '*' || attr_def.price
+            END;
+
+        barcode :=
+            CASE
+                WHEN attr_def.barcode IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.barcode ) = 1 THEN '*[@code="' || attr_def.barcode || '"]'
+                ELSE '*' || attr_def.barcode
+            END;
+
+        circ_modifier :=
+            CASE
+                WHEN attr_def.circ_modifier IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_modifier ) = 1 THEN '*[@code="' || attr_def.circ_modifier || '"]'
+                ELSE '*' || attr_def.circ_modifier
+            END;
+
+        circ_as_type :=
+            CASE
+                WHEN attr_def.circ_as_type IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.circ_as_type ) = 1 THEN '*[@code="' || attr_def.circ_as_type || '"]'
+                ELSE '*' || attr_def.circ_as_type
+            END;
+
+        alert_message :=
+            CASE
+                WHEN attr_def.alert_message IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.alert_message ) = 1 THEN '*[@code="' || attr_def.alert_message || '"]'
+                ELSE '*' || attr_def.alert_message
+            END;
+
+        opac_visible :=
+            CASE
+                WHEN attr_def.opac_visible IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.opac_visible ) = 1 THEN '*[@code="' || attr_def.opac_visible || '"]'
+                ELSE '*' || attr_def.opac_visible
+            END;
+
+        pub_note :=
+            CASE
+                WHEN attr_def.pub_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.pub_note ) = 1 THEN '*[@code="' || attr_def.pub_note || '"]'
+                ELSE '*' || attr_def.pub_note
+            END;
+        priv_note :=
+            CASE
+                WHEN attr_def.priv_note IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.priv_note ) = 1 THEN '*[@code="' || attr_def.priv_note || '"]'
+                ELSE '*' || attr_def.priv_note
+            END;
+
+        internal_id :=
+            CASE
+                WHEN attr_def.internal_id IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.internal_id ) = 1 THEN '*[@code="' || attr_def.internal_id || '"]'
+                ELSE '*' || attr_def.internal_id
+            END;
+
+        stat_cat_data :=
+            CASE
+                WHEN attr_def.stat_cat_data IS NULL THEN 'null()'
+                WHEN LENGTH( attr_def.stat_cat_data ) = 1 THEN '*[@code="' || attr_def.stat_cat_data || '"]'
+                ELSE '*' || attr_def.stat_cat_data
+            END;
+
+
+
+        xpaths := ARRAY[owning_lib, circ_lib, call_number, copy_number, status, location, circulate,
+                        deposit, deposit_amount, ref, holdable, price, barcode, circ_modifier, circ_as_type,
+                        alert_message, pub_note, priv_note, internal_id, stat_cat_data, opac_visible];
+
+        FOR tmp_attr_set IN
+                SELECT  *
+                  FROM  oils_xpath_tag_to_table( (SELECT marc FROM vandelay.queued_bib_record WHERE id = import_id), attr_def.tag, xpaths)
+                            AS t( ol TEXT, clib TEXT, cn TEXT, cnum TEXT, cs TEXT, cl TEXT, circ TEXT,
+                                  dep TEXT, dep_amount TEXT, r TEXT, hold TEXT, pr TEXT, bc TEXT, circ_mod TEXT,
+                                  circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, internal_id TEXT,
+                                  stat_cat_data TEXT, opac_vis TEXT )
+        LOOP
+
+            attr_set.import_error := NULL;
+            attr_set.error_detail := NULL;
+            attr_set.deposit_amount := NULL;
+            attr_set.copy_number := NULL;
+            attr_set.price := NULL;
+            attr_set.circ_modifier := NULL;
+            attr_set.location := NULL;
+            attr_set.barcode := NULL;
+            attr_set.call_number := NULL;
+
+            IF tmp_attr_set.pr != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.pr, E'[^0-9\\.]', '', 'g');
+                IF tmp_str = '' THEN
+                    attr_set.import_error := 'import.item.invalid.price';
+                    attr_set.error_detail := tmp_attr_set.pr; -- original value
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+                attr_set.price := tmp_str::NUMERIC(8,2);
+            END IF;
+
+            IF tmp_attr_set.dep_amount != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.dep_amount, E'[^0-9\\.]', '', 'g');
+                IF tmp_str = '' THEN
+                    attr_set.import_error := 'import.item.invalid.deposit_amount';
+                    attr_set.error_detail := tmp_attr_set.dep_amount;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+                attr_set.deposit_amount := tmp_str::NUMERIC(8,2);
+            END IF;
+
+            IF tmp_attr_set.cnum != '' THEN
+                tmp_str = REGEXP_REPLACE(tmp_attr_set.cnum, E'[^0-9]', '', 'g');
+                IF tmp_str = '' THEN
+                    attr_set.import_error := 'import.item.invalid.copy_number';
+                    attr_set.error_detail := tmp_attr_set.cnum;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+                attr_set.copy_number := tmp_str::INT;
+            END IF;
+
+            IF tmp_attr_set.ol != '' THEN
+                SELECT id INTO attr_set.owning_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.ol); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.owning_lib';
+                    attr_set.error_detail := tmp_attr_set.ol;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.clib != '' THEN
+                SELECT id INTO attr_set.circ_lib FROM actor.org_unit WHERE shortname = UPPER(tmp_attr_set.clib); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_lib';
+                    attr_set.error_detail := tmp_attr_set.clib;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.cs != '' THEN
+                SELECT id INTO attr_set.status FROM config.copy_status WHERE LOWER(name) = LOWER(tmp_attr_set.cs); -- INT
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.status';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            IF COALESCE(tmp_attr_set.circ_mod, '') = '' THEN
+
+                -- no circ mod defined, see if we should apply a default
+                SELECT INTO attr_set.circ_modifier TRIM(BOTH '"' FROM value)
+                    FROM actor.org_unit_ancestor_setting(
+                        'vandelay.item.circ_modifier.default',
+                        attr_set.owning_lib
+                    );
+
+                -- make sure the value from the org setting is still valid
+                PERFORM 1 FROM config.circ_modifier WHERE code = attr_set.circ_modifier;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_modifier';
+                    attr_set.error_detail := tmp_attr_set.circ_mod;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+
+            ELSE
+
+                SELECT code INTO attr_set.circ_modifier FROM config.circ_modifier WHERE code = tmp_attr_set.circ_mod;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_modifier';
+                    attr_set.error_detail := tmp_attr_set.circ_mod;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            IF tmp_attr_set.circ_as != '' THEN
+                SELECT code INTO attr_set.circ_as_type FROM config.coded_value_map WHERE ctype = 'item_type' AND code = tmp_attr_set.circ_as;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.circ_as_type';
+                    attr_set.error_detail := tmp_attr_set.circ_as;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            IF COALESCE(tmp_attr_set.cl, '') = '' THEN
+                -- no location specified, see if we should apply a default
+
+                SELECT INTO attr_set.location TRIM(BOTH '"' FROM value)
+                    FROM actor.org_unit_ancestor_setting(
+                        'vandelay.item.copy_location.default',
+                        attr_set.owning_lib
+                    );
+
+                -- make sure the value from the org setting is still valid
+                PERFORM 1 FROM asset.copy_location WHERE id = attr_set.location;
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.location';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            ELSE
+
+                -- search up the org unit tree for a matching copy location
+                WITH RECURSIVE anscestor_depth AS (
+                    SELECT  ou.id,
+                        out.depth AS depth,
+                        ou.parent_ou
+                    FROM  actor.org_unit ou
+                        JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                    WHERE ou.id = COALESCE(attr_set.owning_lib, attr_set.circ_lib)
+                        UNION ALL
+                    SELECT  ou.id,
+                        out.depth,
+                        ou.parent_ou
+                    FROM  actor.org_unit ou
+                        JOIN actor.org_unit_type out ON (out.id = ou.ou_type)
+                        JOIN anscestor_depth ot ON (ot.parent_ou = ou.id)
+                ) SELECT  cpl.id INTO attr_set.location
+                    FROM  anscestor_depth a
+                        JOIN asset.copy_location cpl ON (cpl.owning_lib = a.id)
+                    WHERE LOWER(cpl.name) = LOWER(tmp_attr_set.cl)
+                    ORDER BY a.depth DESC
+                    LIMIT 1;
+
+                IF NOT FOUND THEN
+                    attr_set.import_error := 'import.item.invalid.location';
+                    attr_set.error_detail := tmp_attr_set.cs;
+                    RETURN NEXT attr_set; CONTINUE;
+                END IF;
+            END IF;
+
+            attr_set.circulate      :=
+                LOWER( SUBSTRING( tmp_attr_set.circ, 1, 1)) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.circ) = 'circulating'; -- BOOL
+
+            attr_set.deposit        :=
+                LOWER( SUBSTRING( tmp_attr_set.dep, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.dep) = 'deposit'; -- BOOL
+
+            attr_set.holdable       :=
+                LOWER( SUBSTRING( tmp_attr_set.hold, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.hold) = 'holdable'; -- BOOL
+
+            attr_set.opac_visible   :=
+                LOWER( SUBSTRING( tmp_attr_set.opac_vis, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.opac_vis) = 'visible'; -- BOOL
+
+            attr_set.ref            :=
+                LOWER( SUBSTRING( tmp_attr_set.r, 1, 1 ) ) IN ('t','y','1')
+                OR LOWER(tmp_attr_set.r) = 'reference'; -- BOOL
+
+            attr_set.call_number    := tmp_attr_set.cn; -- TEXT
+            attr_set.barcode        := tmp_attr_set.bc; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+            attr_set.pub_note       := tmp_attr_set.note; -- TEXT,
+            attr_set.priv_note      := tmp_attr_set.pnote; -- TEXT,
+            attr_set.alert_message  := tmp_attr_set.amessage; -- TEXT,
+            attr_set.internal_id    := tmp_attr_set.internal_id::BIGINT;
+            attr_set.stat_cat_data  := tmp_attr_set.stat_cat_data; -- TEXT,
+
+            RETURN NEXT attr_set;
+
+        END LOOP;
+
+    END IF;
+
+    RETURN;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+CREATE OR REPLACE FUNCTION vandelay.ingest_bib_items ( ) RETURNS TRIGGER AS $func$
+DECLARE
+    attr_def    BIGINT;
+    item_data   vandelay.import_item%ROWTYPE;
+BEGIN
+
+    IF TG_OP IN ('INSERT','UPDATE') AND NEW.imported_as IS NOT NULL THEN
+        RETURN NEW;
+    END IF;
+
+    SELECT item_attr_def INTO attr_def FROM vandelay.bib_queue WHERE id = NEW.queue;
+
+    FOR item_data IN SELECT * FROM vandelay.ingest_items( NEW.id::BIGINT, attr_def ) LOOP
+        INSERT INTO vandelay.import_item (
+            record,
+            definition,
+            owning_lib,
+            circ_lib,
+            call_number,
+            copy_number,
+            status,
+            location,
+            circulate,
+            deposit,
+            deposit_amount,
+            ref,
+            holdable,
+            price,
+            barcode,
+            circ_modifier,
+            circ_as_type,
+            alert_message,
+            pub_note,
+            priv_note,
+            internal_id,
+            opac_visible,
+            stat_cat_data,
+            import_error,
+            error_detail
+        ) VALUES (
+            NEW.id,
+            item_data.definition,
+            item_data.owning_lib,
+            item_data.circ_lib,
+            item_data.call_number,
+            item_data.copy_number,
+            item_data.status,
+            item_data.location,
+            item_data.circulate,
+            item_data.deposit,
+            item_data.deposit_amount,
+            item_data.ref,
+            item_data.holdable,
+            item_data.price,
+            item_data.barcode,
+            item_data.circ_modifier,
+            item_data.circ_as_type,
+            item_data.alert_message,
+            item_data.pub_note,
+            item_data.priv_note,
+            item_data.internal_id,
+            item_data.opac_visible,
+            item_data.stat_cat_data,
+            item_data.import_error,
+            item_data.error_detail
+        );
+    END LOOP;
+
+    RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+COMMIT;