<field reporter:label="Public Note" name="pub_note" reporter:datatype="text"/>
<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"/>
</fields>
<links>
<link field="import_error" reltype="has_a" key="code" map="" class="vie"/>
<field reporter:label="Private Note" name="priv_note" reporter:datatype="text"/>
<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"/>
</fields>
<links>
<link field="owner" reltype="has_a" key="id" map="" class="aou"/>
for my $item_id (@$item_ids) {
my $e = new_editor(requestor => $requestor, xact => 1);
my $item = $e->retrieve_vandelay_import_item($item_id);
+ my ($copy, $vol, $evt);
+
$$report_args{import_item} = $item;
$$report_args{e} = $e;
$$report_args{import_error} = undef;
$$report_args{evt} = undef;
- # --------------------------------------------------------------------------------
- # Find or create the volume
- # --------------------------------------------------------------------------------
- my ($vol, $evt) =
- OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
- $e, $item->call_number, $rec->imported_as, $item->owning_lib);
+ if (my $copy_id = $item->internal_id) { # assignment
+ # copy matches an existing copy. Overlay instead of create.
- if($evt) {
+ $logger->info("vl: performing copy overlay for internal_id=$copy_id");
- $$report_args{evt} = $evt;
- respond_with_status($report_args);
- next;
- }
+ my $qt = $e->json_query({
+ select => {vbq => ['queue_type']},
+ from => {vqbr => 'vbq'},
+ where => {'+vqbr' => {id => $rec_id}}
+ })->[0]->{queue_type};
- # --------------------------------------------------------------------------------
- # Create the new copy
- # --------------------------------------------------------------------------------
- my $copy = Fieldmapper::asset::copy->new;
- $copy->loan_duration(2);
- $copy->fine_level(2);
- $copy->barcode($item->barcode);
- $copy->location($item->location);
- $copy->circ_lib($item->circ_lib || $item->owning_lib);
- $copy->status( defined($item->status) ? $item->status : OILS_COPY_STATUS_IN_PROCESS );
- $copy->circulate($item->circulate);
- $copy->deposit($item->deposit);
- $copy->deposit_amount($item->deposit_amount);
- $copy->ref($item->ref);
- $copy->holdable($item->holdable);
- $copy->price($item->price);
- $copy->circ_as_type($item->circ_as_type);
- $copy->alert_message($item->alert_message);
- $copy->opac_visible($item->opac_visible);
- $copy->circ_modifier($item->circ_modifier);
+ if ($qt eq 'acq') {
+ # internal_id for ACQ queues refers to acq.lineitem_detail.id
+ # pull the real copy id from the acq LID
- # --------------------------------------------------------------------------------
- # Check for dupe barcode
- # --------------------------------------------------------------------------------
- if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
- $$report_args{evt} = $evt;
- $$report_args{import_error} = 'import.item.duplicate.barcode'
- if $evt->{textcode} eq 'ITEM_BARCODE_EXISTS';
- respond_with_status($report_args);
- next;
- }
+ my $lid = $e->retrieve_acq_lineitem_detail($copy_id);
+ if (!$lid) {
+ $$report_args{evt} = $e->die_event;
+ respond_with_status($report_args);
+ next;
+ }
+ $copy_id = $lid->eg_copy_id;
+ $logger->info("vl: performing ACQ copy overlay for copy $copy_id");
+ }
- # --------------------------------------------------------------------------------
- # create copy notes
- # --------------------------------------------------------------------------------
- $evt = OpenILS::Application::Cat::AssetCommon->create_copy_note(
- $e, $copy, '', $item->pub_note, 1) if $item->pub_note;
+ $copy = $e->search_asset_copy([
+ {id => $copy_id, deleted => 'f'},
+ {flesh => 1, flesh_fields => {acp => ['call_number']}}
+ ])->[0];
- if($evt) {
- $$report_args{evt} = $evt;
- respond_with_status($report_args);
- next;
- }
+ if (!$copy) {
+ $$report_args{evt} = $e->die_event;
+ respond_with_status($report_args);
+ next;
+ }
- $evt = OpenILS::Application::Cat::AssetCommon->create_copy_note(
- $e, $copy, '', $item->priv_note) if $item->priv_note;
+ # prevent update of unrelated copies
+ if ($copy->call_number->record != $rec->imported_as) {
+ $logger->info("vl: attempt to overlay unrelated copy=$copy_id; rec=".$rec->imported_as);
- if($evt) {
- $$report_args{evt} = $evt;
- respond_with_status($report_args);
- next;
+ $evt = OpenILS::Event->new('INVALID_IMPORT_COPY_ID',
+ note => 'Cannot overlay copies for unlinked bib',
+ bre => $rec->imported_as,
+ copy_id => $copy_id
+ );
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
+
+ # overlaying copies requires an extra permission
+ if (!$e->allowed("IMPORT_OVERLAY_COPY", $copy->call_number->owning_lib)) {
+ $$report_args{evt} = $e->die_event;
+ respond_with_status($report_args);
+ next;
+ }
+
+ # are we updating the call-number?
+ if ($item->call_number and $item->call_number ne $copy->call_number->label) {
+
+ my $count = $e->json_query({
+ select => {acp => [{
+ alias => 'count',
+ column => 'id',
+ transform => 'count',
+ aggregate => 1
+ }]},
+ from => 'acp',
+ where => {
+ deleted => 'f',
+ call_number => $copy->call_number->id
+ }
+ })->[0]->{count};
+
+ if ($count == 1) {
+ # if this is the only copy attached to this
+ # callnumber, just update the callnumber
+
+ $logger->info("vl: updating callnumber label in copy overlay");
+
+ $copy->call_number->label($item->call_number);
+ if (!$e->update_asset_call_number($copy->call_number)) {
+ $$report_args{evt} = $e->die_event;
+ respond_with_status($report_args);
+ next;
+ }
+
+ } else {
+
+ # otherwise, move the copy to a new/existing
+ # call-number with the given label/owner
+ # note that overlay does not allow the owning_lib
+ # to be changed. Should it?
+
+ $logger->info("vl: moving copy to new callnumber in copy overlay");
+
+ ($vol, $evt) =
+ OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
+ $e, $item->call_number,
+ $copy->call_number->record,
+ $copy->call_number->owning_lib
+ );
+
+ if($evt) {
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
+
+ $copy->call_number($vol);
+ }
+ } # cn-update
+
+ # for every field that has a non-'' value, overlay the copy value
+ foreach (qw/ barcode location circ_lib status
+ circulate deposit deposit_amount ref holdable
+ price circ_as_type alert_message opac_visible circ_modifier/) {
+
+ my $val = $item->$_();
+ $copy->$_($val) if $val and $val ne '';
+ }
+
+ # de-flesh for update
+ $copy->call_number($copy->call_number->id);
+ $copy->ischanged(1);
+
+ $evt = OpenILS::Application::Cat::AssetCommon->
+ update_fleshed_copies($e, {all => 1}, undef, [$copy]);
+
+ if($evt) {
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
+
+ } else {
+
+ # Creating a new copy
+ $logger->info("vl: creating new copy in import");
+
+ # --------------------------------------------------------------------------------
+ # Find or create the volume
+ # --------------------------------------------------------------------------------
+ my ($vol, $evt) =
+ OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
+ $e, $item->call_number, $rec->imported_as, $item->owning_lib);
+
+ if($evt) {
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
+
+ # --------------------------------------------------------------------------------
+ # Create the new copy
+ # --------------------------------------------------------------------------------
+ $copy = Fieldmapper::asset::copy->new;
+ $copy->loan_duration(2);
+ $copy->fine_level(2);
+ $copy->barcode($item->barcode);
+ $copy->location($item->location);
+ $copy->circ_lib($item->circ_lib || $item->owning_lib);
+ $copy->status( defined($item->status) ? $item->status : OILS_COPY_STATUS_IN_PROCESS );
+ $copy->circulate($item->circulate);
+ $copy->deposit($item->deposit);
+ $copy->deposit_amount($item->deposit_amount);
+ $copy->ref($item->ref);
+ $copy->holdable($item->holdable);
+ $copy->price($item->price);
+ $copy->circ_as_type($item->circ_as_type);
+ $copy->alert_message($item->alert_message);
+ $copy->opac_visible($item->opac_visible);
+ $copy->circ_modifier($item->circ_modifier);
+
+ # --------------------------------------------------------------------------------
+ # Check for dupe barcode
+ # --------------------------------------------------------------------------------
+ if($evt = OpenILS::Application::Cat::AssetCommon->create_copy($e, $vol, $copy)) {
+ $$report_args{evt} = $evt;
+ $$report_args{import_error} = 'import.item.duplicate.barcode'
+ if $evt->{textcode} eq 'ITEM_BARCODE_EXISTS';
+ respond_with_status($report_args);
+ next;
+ }
+
+ # --------------------------------------------------------------------------------
+ # create copy notes
+ # --------------------------------------------------------------------------------
+ $evt = OpenILS::Application::Cat::AssetCommon->create_copy_note(
+ $e, $copy, '', $item->pub_note, 1) if $item->pub_note;
+
+ if($evt) {
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
+
+ $evt = OpenILS::Application::Cat::AssetCommon->create_copy_note(
+ $e, $copy, '', $item->priv_note) if $item->priv_note;
+
+ if($evt) {
+ $$report_args{evt} = $evt;
+ respond_with_status($report_args);
+ next;
+ }
}
# set the import data on the import item
respond_with_status($report_args);
$logger->info("vl: successfully imported item " . $item->barcode);
}
-
}
$roe->rollback;
BEFORE INSERT OR UPDATE ON config.db_patch_dependencies
FOR EACH ROW EXECUTE PROCEDURE evergreen.array_overlap_check ('deprecates');
-INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0730', :eg_version); -- miker/senator/berick
+INSERT INTO config.upgrade_log (version, applied_to) VALUES ('0731', :eg_version); -- berick/senator
CREATE TABLE config.bib_source (
id SERIAL PRIMARY KEY,
pub_note TEXT,
priv_note_title TEXT,
priv_note TEXT,
+ internal_id TEXT,
CONSTRAINT vand_import_item_attr_def_idx UNIQUE (owner,name)
);
alert_message TEXT,
pub_note TEXT,
priv_note TEXT,
- opac_visible BOOL
+ opac_visible BOOL,
+ internal_id BIGINT -- queue_type == 'acq' ? acq.lineitem_detail.id : asset.copy.id
);
CREATE TABLE vandelay.import_bib_trash_fields (
( 534, 'ADMIN_USER_ACTIVITY_TYPE', oils_i18n_gettext( 534,
'Allows a user to create/retrieve/update/delete user activity types', 'ppl', 'description' )),
( 535, 'VIEW_TRIGGER_EVENT', oils_i18n_gettext( 535,
- 'Allows a user to view circ- and hold-related action/trigger events', 'ppl', 'description'))
+ 'Allows a user to view circ- and hold-related action/trigger events', 'ppl', 'description')),
+( 536, 'IMPORT_OVERLAY_COPY', oils_i18n_gettext( 536,
+ 'Allows a user to overlay copy data in MARC import', 'ppl', 'description'))
;
opac_visible TEXT;
pub_note TEXT;
priv_note TEXT;
+ internal_id TEXT;
attr_def RECORD;
tmp_attr_set RECORD;
ELSE '//*[@tag="' || attr_def.tag || '"]/*' || 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
+ END;
+
+
xpath :=
owning_lib || '|' ||
alert_message || '|' ||
pub_note || '|' ||
priv_note || '|' ||
+ internal_id || '|' ||
opac_visible;
FOR tmp_attr_set IN
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,
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, opac_vis TEXT )
+ circ_as TEXT, amessage TEXT, note TEXT, pnote TEXT, internal_id TEXT, opac_vis TEXT )
LOOP
attr_set.import_error := NULL;
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;
RETURN NEXT attr_set;
alert_message,
pub_note,
priv_note,
+ internal_id,
opac_visible,
import_error,
error_detail
item_data.alert_message,
item_data.pub_note,
item_data.priv_note,
+ item_data.internal_id,
item_data.opac_visible,
item_data.import_error,
item_data.error_detail
--- /dev/null
+-- 0731.schema.vandelay_item_overlay.sql
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('0731', :eg_version);
+
+ALTER TABLE vandelay.import_item_attr_definition
+ ADD COLUMN internal_id TEXT;
+
+ALTER TABLE vandelay.import_item
+ ADD COLUMN internal_id BIGINT;
+
+INSERT INTO permission.perm_list ( id, code, description ) VALUES
+( 536, 'IMPORT_OVERLAY_COPY', oils_i18n_gettext( 536,
+ 'Allows a user to overlay copy data in MARC import', 'ppl', 'description'));
+
+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,
+ 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.import_error,
+ item_data.error_detail
+ );
+ END LOOP;
+
+ RETURN NULL;
+END;
+$func$ LANGUAGE PLPGSQL;
+
+
+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;
+
+ attr_def RECORD;
+ tmp_attr_set RECORD;
+ attr_set vandelay.import_item%ROWTYPE;
+
+ xpath 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 '//*[@tag="' || attr_def.tag || '"]/*[@code="' || attr_def.owning_lib || '"]'
+ ELSE '//*[@tag="' || attr_def.tag || '"]/*' || 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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;
+
+ 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,
+ 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 )
+ 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;
+
+ 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 tmp_attr_set.circ_mod != '' THEN
+ 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 tmp_attr_set.cl != '' THEN
+
+ -- 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;
+
+ RETURN NEXT attr_set;
+
+ END LOOP;
+
+ END IF;
+
+ RETURN;
+
+END;
+$$ LANGUAGE PLPGSQL;
+
+
+
+COMMIT;
jsId='vlUploadRecordType' onchange='vlShowUploadForm();'>
<option value='bib' selected='selected'>[% l('Bibliographic Records') %]</option>
<option value='auth'>[% l('Authority Records') %]</option>
+ <option value='bib-acq'>[% l('Acquisitions Records') %]</option>
</select>
</td>
</tr>
function createQueue(queueName, type, onload, importDefId, matchSet) {
var name = (type=='bib') ? 'bib' : 'authority';
var method = 'open-ils.vandelay.'+ name +'_queue.create'
+
+ var qType = name;
+ if (vlUploadRecordType.getValue().match(/acq/))
+ var qType = 'acq';
+
+ console.log('record type ' + vlUploadRecordType.getValue());
+ console.log('record type ' + vlUploadRecordType.getValue());
+
fieldmapper.standardRequest(
['open-ils.vandelay', method],
{ async: true,
- params: [authtoken, queueName, null, name, matchSet, importDefId],
+ params: [authtoken, queueName, null, qType, matchSet, importDefId],
oncomplete : function(r) {
var queue = r.recv().content();
if(e = openils.Event.parse(queue))
var queueName = dijit.byId('vl-queue-name').getValue();
currentType = dijit.byId('vl-record-type').getValue();
+ // could be bib-acq, which makes no sense in most places
+ if (currentType.match(/bib/)) currentType = 'bib';
+
var handleProcessSpool = function() {
if(
vlUploadQueueImportNoMatch.checked ||
--- /dev/null
+Vandelay (MARC Import/Export) Copy Overlay
+------------------------------------------
+
+Vandelay Item Attributes (Cataloging -> MARC Import /
+Export -> Import Item Attribute Definitions) contains
+a new field called "Overlay Match ID". The presence of data
+in this field extracted from an import-item copy indicates
+to the Vandelay import process that a copy overlay is requested
+instead of new copy creation. The value for the field is the
+copy id for bib record queues and the ACQ lineitem_detail ID for
+Acquisitions Queues. For either type of queue, however, overlay
+occurs against a real copy (asset.copy). In the ACQ queue case,
+we use the lineitem_detail ID because this is the data ACQ
+providers and sub-systems will have access to.
+
+When a match point ID value is a set and a matching copy is found,
+the values extracted from the inbound copy data are used to replace
+values on the existing found copy, including the call-number label.
+Any fields on the inbound copy that are empty are ignored.
+
+One use case for this feature are shelf-ready items produced by a
+3rd-party (e.g. ACQ provider) and delivered to the library via MARC
+file for upload. The file might contain improved MARC bibliographic
+data as well as real barcodes (i.e. not temporary ACQ generated
+barcodes) for the copies already purchased through the vendor.
+
+Permission
+~~~~~~~~~~
+
+This adds a new permission called IMPORT_OVERLAY_COPY which is
+required to perform the copy overlay step.
+
+Regardless of permission, it is not possible to overlay values on
+a copy unless the imported bib record links (creates/overlays/merges)
+to/with the owning bib record for the copy to be overlaid. This is
+both for security and removal of a potent foot-gun.
+