my $U = "OpenILS::Application::AppUtils";
my $conf;
my %marctemplates;
+my $assetcom = 'OpenILS::Application::Cat::AssetCommon';
__PACKAGE__->register_method(
method => "retrieve_marc_template",
);
sub create_record_xml {
- my( $self, $client, $login, $xml, $source ) = @_;
+ my( $self, $client, $login, $xml, $source, $oargs, $strip_grps ) = @_;
my $override = 1 if $self->api_name =~ /override/;
+ $oargs = { all => 1 } unless defined $oargs;
my( $user_obj, $evt ) = $U->checksesperm($login, 'CREATE_MARC');
return $evt if $evt;
$meth = $self->method_lookup(
"open-ils.cat.biblio.record.xml.import.override") if $override;
- my ($s) = $meth->run($login, $xml, $source);
+ my ($s) = $meth->run($login, $xml, $source, $oargs, $strip_grps);
return $s;
}
);
sub biblio_record_replace_marc {
- my( $self, $conn, $auth, $recid, $newxml, $source ) = @_;
+ my( $self, $conn, $auth, $recid, $newxml, $source, $oargs, $strip_grps ) = @_;
my $e = new_editor(authtoken=>$auth, xact=>1);
return $e->die_event unless $e->checkauth;
- return $e->die_event unless $e->allowed('CREATE_MARC', $e->requestor->ws_ou);
+ return $e->die_event unless $e->allowed('UPDATE_MARC', $e->requestor->ws_ou);
my $fix_tcn = $self->api_name =~ /replace/o;
- my $override = $self->api_name =~ /override/o;
+ if($self->api_name =~ /override/o) {
+ $oargs = { all => 1 } unless defined $oargs;
+ } else {
+ $oargs = {};
+ }
my $res = OpenILS::Application::Cat::BibCommon->biblio_record_replace_marc(
- $e, $recid, $newxml, $source, $fix_tcn, $override);
+ $e, $recid, $newxml, $source, $fix_tcn, $oargs, $strip_grps);
$e->commit unless $U->event_code($res);
- #my $ses = OpenSRF::AppSession->create('open-ils.ingest');
- #$ses->request('open-ils.ingest.full.biblio.record', $recid);
-
return $res;
}
$template = $e->retrieve_biblio_record_entry( $titem->target_biblio_record_entry )->marc;
}
- my $responses = [];
- my $some_failed = 0;
+ my $num_failed = 0;
+ my $num_succeeded = 0;
- $self->respond_complete(
- $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses)->gather(1)
+ $conn->respond_complete(
+ $actor->request('open-ils.actor.anon_cache.set_value', $auth, batch_edit_progress => {})->gather(1)
) if ($actor);
for my $item ( @$items ) {
)->[0]->{'vandelay.template_overlay_bib_record'};
}
- $some_failed++ if ($success eq 'f');
+ if ($success eq 'f') {
+ $num_failed++;
+ } else {
+ $num_succeeded++;
+ }
if ($actor) {
- push @$responses, { record => $rec->id, success => $success };
- $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ $actor->request(
+ 'open-ils.actor.anon_cache.set_value', $auth,
+ batch_edit_progress => {
+ succeeded => $num_succeeded,
+ failed => $num_failed
+ },
+ );
} else {
$conn->respond({ record => $rec->id, success => $success });
}
unless ($e->delete_container_biblio_record_entry_bucket_item($item)) {
$e->rollback;
if ($actor) {
- push @$responses, { complete => 1, success => 'f' };
- $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ $actor->request(
+ 'open-ils.actor.anon_cache.set_value', $auth,
+ batch_edit_progress => {
+ complete => 1,
+ success => 'f',
+ succeeded => $num_succeeded,
+ failed => $num_failed,
+ }
+ );
return undef;
} else {
return { complete => 1, success => 'f' };
}
}
- if ($titem && !$some_failed) {
+ if ($titem && !$num_failed) {
return $e->die_event unless ($e->delete_container_biblio_record_entry_bucket_item($titem));
}
if ($e->commit) {
if ($actor) {
- push @$responses, { complete => 1, success => 't' };
- $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ $actor->request(
+ 'open-ils.actor.anon_cache.set_value', $auth,
+ batch_edit_progress => {
+ complete => 1,
+ success => 't',
+ succeeded => $num_succeeded,
+ failed => $num_failed,
+ }
+ );
} else {
return { complete => 1, success => 't' };
}
} else {
if ($actor) {
- push @$responses, { complete => 1, success => 'f' };
- $actor->request('open-ils.actor.anon_cache.set_value', $auth, res_list => $responses);
+ $actor->request(
+ 'open-ils.actor.anon_cache.set_value', $auth,
+ batch_edit_progress => {
+ complete => 1,
+ success => 'f',
+ succeeded => $num_succeeded,
+ failed => $num_failed,
+ }
+ );
} else {
return { complete => 1, success => 'f' };
}
sub biblio_record_xml_import {
- my( $self, $client, $authtoken, $xml, $source, $auto_tcn) = @_;
+ my( $self, $client, $authtoken, $xml, $source, $auto_tcn, $oargs, $strip_grps) = @_;
my $e = new_editor(xact=>1, authtoken=>$authtoken);
return $e->die_event unless $e->checkauth;
return $e->die_event unless $e->allowed('IMPORT_MARC', $e->requestor->ws_ou);
- my $override = $self->api_name =~ /override/;
+ if ($self->api_name =~ /override/) {
+ $oargs = { all => 1 } unless defined $oargs;
+ } else {
+ $oargs = {};
+ }
my $record = OpenILS::Application::Cat::BibCommon->biblio_record_xml_import(
- $e, $xml, $source, $auto_tcn, $override);
+ $e, $xml, $source, $auto_tcn, $oargs, $strip_grps);
return $record if $U->event_code($record);
$e->commit;
- #my $ses = OpenSRF::AppSession->create('open-ils.ingest');
- #$ses->request('open-ils.ingest.full.biblio.record', $record->id);
-
return $record;
}
method => "biblio_record_record_metadata",
api_name => "open-ils.cat.biblio.record.metadata.retrieve",
authoritative => 1,
- argc => 1, #(session_id, biblio_tree )
- notes => "Walks the tree and commits any changed nodes " .
- "adds any new nodes, and deletes any deleted nodes",
+ argc => 2, #(session_id, list of bre ids )
+ notes => "Returns a list of slim-downed bre objects based on the " .
+ "ids passed in",
);
sub biblio_record_record_metadata {
my $rec = $editor->retrieve_biblio_record_entry($_);
$rec->creator($editor->retrieve_actor_user($rec->creator));
$rec->editor($editor->retrieve_actor_user($rec->editor));
+ $rec->attrs($U->get_bre_attrs([$rec->id], $editor)->{$rec->id});
$rec->clear_marc; # slim the record down
push( @results, $rec );
}
my $tag = substr($field, 0, 3);
$logger->debug("Tag = $tag");
my @node = $doc->findnodes("//marc:datafield[\@tag='$tag']");
+ next unless (@node);
# Now parse the subfields and build up the subfield XPath
my @subfields = split(//, substr($field, 3));
if (!@subfields) {
@subfields = ('a');
}
- my $subxpath;
- foreach my $sf (@subfields) {
- $subxpath .= "\@code='$sf' or ";
- }
- $subxpath = substr($subxpath, 0, -4);
- $logger->debug("subxpath = $subxpath");
+ my $xpath = 'marc:subfield[' . join(' or ', map { "\@code='$_'" } @subfields) . ']';
+ $logger->debug("xpath = $xpath");
# Find the contents of the specified subfields
foreach my $x (@node) {
- my $cn = $x->findvalue("marc:subfield[$subxpath]");
+ # We can't use find($xpath)->to_literal_delimited here because older 2.x
+ # versions of the XML::LibXML module don't have to_literal_delimited().
+ my $cn = join(
+ ' ',
+ map { $_->textContent } $x->findnodes($xpath)
+ );
push @res, {$tag => $cn} if ($cn);
}
}
@org_ids = ($user_obj->home_ou);
}
+ # Create an editor that can be shared across all iterations of
+ # _build_volume_list(). Otherwise, .authoritative calls can result
+ # in creating too many cstore connections.
+ my $e = new_editor();
+
if( $self->api_name =~ /global/ ) {
- return _build_volume_list( { record => $docid, deleted => 'f', label => { '<>' => '##URI##' } } );
+ return _build_volume_list($e, { record => $docid, deleted => 'f', label => { '<>' => '##URI##' } } );
} else {
my @all_vols;
for my $orgid (@org_ids) {
- my $vols = _build_volume_list(
+ my $vols = _build_volume_list($e,
{ record => $docid, owning_lib => $orgid, deleted => 'f', label => { '<>' => '##URI##' } } );
push( @all_vols, @$vols );
}
sub _build_volume_list {
+ my $e = shift;
my $search_hash = shift;
+ $e ||= new_editor();
+
$search_hash->{deleted} = 'f';
- my $e = new_editor();
my $vols = $e->search_asset_call_number([
$search_hash,
my $copies = $e->search_asset_copy([
{ call_number => $volume->id , deleted => 'f' },
- { flesh => 1, flesh_fields => { acp => ['stat_cat_entries','parts'] } }
+ {
+ join => {
+ acpm => {
+ type => 'left',
+ join => {
+ bmp => { type => 'left' }
+ }
+ }
+ },
+ flesh => 1,
+ flesh_fields => { acp => ['stat_cat_entries','parts'] },
+ order_by => [
+ {'class' => 'bmp', 'field' => 'label_sortkey', 'transform' => 'oils_text_as_bytea'},
+ {'class' => 'bmp', 'field' => 'label', 'transform' => 'oils_text_as_bytea'},
+ {'class' => 'acp', 'field' => 'barcode'}
+ ]
+ }
]);
- $copies = [ sort { $a->barcode cmp $b->barcode } @$copies ];
-
for my $c (@$copies) {
if( $c->status == OILS_COPY_STATUS_CHECKED_OUT ) {
$c->circulations(
sub fleshed_copy_update {
- my( $self, $conn, $auth, $copies, $delete_stats ) = @_;
+ my( $self, $conn, $auth, $copies, $delete_stats, $oargs, $create_parts ) = @_;
return 1 unless ref $copies;
my( $reqr, $evt ) = $U->checkses($auth);
return $evt if $evt;
my $editor = new_editor(requestor => $reqr, xact => 1);
- my $override = $self->api_name =~ /override/;
+ if ($self->api_name =~ /override/) {
+ $oargs = { all => 1 } unless defined $oargs;
+ } else {
+ $oargs = {};
+ }
my $retarget_holds = [];
$evt = OpenILS::Application::Cat::AssetCommon->update_fleshed_copies(
- $editor, $override, undef, $copies, $delete_stats, $retarget_holds, undef);
+ $editor, $oargs, undef, $copies, $delete_stats, $retarget_holds, undef, $create_parts);
if( $evt ) {
$logger->info("fleshed copy update failed with event: ".OpenSRF::Utils::JSON->perl2JSON($evt));
$ses->request('open-ils.circ.hold.reset.batch', $auth, $hold_ids);
}
+__PACKAGE__->register_method(
+ method => "transfer_copies_to_volume",
+ api_name => "open-ils.cat.transfer_copies_to_volume",
+ argc => 3,
+ signature => {
+ desc => 'Transfers specified copies to the specified call number, and changes Circ Lib to match the new Owning Lib.',
+ params => [
+ {desc => 'Authtoken', type => 'string'},
+ {desc => 'Call Number ID', type => 'number'},
+ {desc => 'Array of Copy IDs', type => 'array'},
+ ]
+ },
+ return => {desc => '1 on success, Event on error'}
+);
+
+__PACKAGE__->register_method(
+ method => "transfer_copies_to_volume",
+ api_name => "open-ils.cat.transfer_copies_to_volume.override",);
+
+sub transfer_copies_to_volume {
+ my( $self, $conn, $auth, $volume, $copies, $oargs ) = @_;
+ my $delete_stats = 1;
+ my $force_delete_empty_bib = undef;
+ my $create_parts = undef;
+
+ # initial tests
+
+ return 1 unless ref $copies;
+ my( $reqr, $evt ) = $U->checkses($auth);
+ return $evt if $evt;
+ my $editor = new_editor(requestor => $reqr, xact => 1);
+ if ($self->api_name =~ /override/) {
+ $oargs = { all => 1 } unless defined $oargs;
+ } else {
+ $oargs = {};
+ }
+
+ # does the volume exist? good, we also need its owning_lib later
+ my( $cn, $cn_evt ) = $U->fetch_callnumber( $volume, 0, $editor );
+ return $cn_evt if $cn_evt;
+
+ # flesh and munge the copies
+ my $fleshed_copies = [];
+ my $copy;
+ foreach my $copy_id ( @{ $copies } ) {
+ $copy = $editor->search_asset_copy([
+ { id => $copy_id , deleted => 'f' },
+ {
+ flesh => 1,
+ flesh_fields => { acp => ['parts', 'stat_cat_entries'] }
+ }
+ ])->[0];
+ return OpenILS::Event->new('ASSET_COPY_NOT_FOUND') if !$copy;
+ $copy->call_number( $volume );
+ $copy->circ_lib( $cn->owning_lib() );
+ $copy->ischanged( 't' );
+ push @$fleshed_copies, $copy;
+ }
+
+ # actual work
+ my $retarget_holds = [];
+ $evt = OpenILS::Application::Cat::AssetCommon->update_fleshed_copies(
+ $editor, $oargs, undef, $fleshed_copies, $delete_stats, $retarget_holds, $force_delete_empty_bib, $create_parts);
+
+ if( $evt ) {
+ $logger->info("copy to volume transfer failed with event: ".OpenSRF::Utils::JSON->perl2JSON($evt));
+ $editor->rollback;
+ return $evt;
+ }
+
+ # take care of the parts
+ for my $copy (@$fleshed_copies) {
+ my $parts = $copy->parts;
+ next unless $parts;
+ my $part_objs = [];
+ foreach my $part (@$parts) {
+ my $part_label = $part->label;
+ my $part_obj = $editor->search_biblio_monograph_part(
+ {
+ label=>$part_label,
+ record=>$cn->record,
+ deleted=>'f'
+ }
+ )->[0];
+ if (!$part_obj) {
+ $part_obj = Fieldmapper::biblio::monograph_part->new();
+ $part_obj->label( $part_label );
+ $part_obj->record( $cn->record );
+ unless($editor->create_biblio_monograph_part($part_obj)) {
+ return $editor->die_event if $editor->die_event;
+ }
+ }
+ push @$part_objs, $part_obj;
+ }
+ $copy->parts( $part_objs );
+ $copy->ischanged(1);
+ $evt = OpenILS::Application::Cat::AssetCommon->update_copy_parts($editor, $copy, 1); #delete_parts=1
+ return $evt if $evt;
+ }
+
+ $editor->commit;
+ $logger->info("copy to volume transfer successfully updated ".scalar(@$copies)." copies");
+ reset_hold_list($auth, $retarget_holds);
+
+ return 1;
+}
__PACKAGE__->register_method(
method => 'in_db_merge',
}
__PACKAGE__->register_method(
+ method => 'calculate_marc_merge',
+ api_name => 'open-ils.cat.merge.marc.per_profile',
+ signature => q/
+ Calculate the result of merging one or more MARC records
+ per the specified merge profile
+ @param auth The login session key
+ @param merge_profile ID of the record merge profile
+ @param records Array of two or more MARCXML records to be
+ merged. If two are supplied, the first
+ is treated as the record to be overlaid,
+ and the the incoming record that will
+ overlay the first. If more than two are
+ supplied, the first is treated as the
+ record to be overlaid, and each following
+ record in turn will be merged into that
+ record.
+ @return MARCXML string of the results of the merge
+ /
+);
+__PACKAGE__->register_method(
+ method => 'calculate_bib_marc_merge',
+ api_name => 'open-ils.cat.merge.biblio.per_profile',
+ signature => q/
+ Calculate the result of merging one or more bib records
+ per the specified merge profile
+ @param auth The login session key
+ @param merge_profile ID of the record merge profile
+ @param records Array of two or more bib record IDs of
+ the bibs to be merged.
+ @return MARCXML string of the results of the merge
+ /
+);
+__PACKAGE__->register_method(
+ method => 'calculate_authority_marc_merge',
+ api_name => 'open-ils.cat.merge.authority.per_profile',
+ signature => q/
+ Calculate the result of merging one or more authority records
+ per the specified merge profile
+ @param auth The login session key
+ @param merge_profile ID of the record merge profile
+ @param records Array of two or more bib record IDs of
+ the bibs to be merged.
+ @return MARCXML string of the results of the merge
+ /
+);
+
+sub _handle_marc_merge {
+ my ($e, $merge_profile_id, $records) = @_;
+
+ my $result = shift @$records;
+ foreach my $incoming (@$records) {
+ my $response = $e->json_query({
+ from => [
+ 'vandelay.merge_record_xml_using_profile',
+ $incoming, $result,
+ $merge_profile_id
+ ]
+ });
+ return unless ref($response);
+ $result = $response->[0]->{'vandelay.merge_record_xml_using_profile'};
+ }
+ return $result;
+}
+
+sub calculate_marc_merge {
+ my( $self, $conn, $auth, $merge_profile_id, $records ) = @_;
+
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+ or return $e->die_event;
+ return $e->die_event unless ref($records) && @$records >= 2;
+
+ return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+sub calculate_bib_marc_merge {
+ my( $self, $conn, $auth, $merge_profile_id, $bib_ids ) = @_;
+
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+ or return $e->die_event;
+ return $e->die_event unless ref($bib_ids) && @$bib_ids >= 2;
+
+ my $records = [];
+ foreach my $id (@$bib_ids) {
+ my $bre = $e->retrieve_biblio_record_entry($id) or return $e->die_event;
+ push @$records, $bre->marc();
+ }
+
+ return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+sub calculate_authority_marc_merge {
+ my( $self, $conn, $auth, $merge_profile_id, $authority_ids ) = @_;
+
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my $merge_profile = $e->retrieve_vandelay_merge_profile($merge_profile_id)
+ or return $e->die_event;
+ return $e->die_event unless ref($authority_ids) && @$authority_ids >= 2;
+
+ my $records = [];
+ foreach my $id (@$authority_ids) {
+ my $are = $e->retrieve_authority_record_entry($id) or return $e->die_event;
+ push @$records, $are->marc();
+ }
+
+ return _handle_marc_merge($e, $merge_profile_id, $records)
+}
+
+__PACKAGE__->register_method(
method => "fleshed_volume_update",
api_name => "open-ils.cat.asset.volume.fleshed.batch.update",);
api_name => "open-ils.cat.asset.volume.fleshed.batch.update.override",);
sub fleshed_volume_update {
- my( $self, $conn, $auth, $volumes, $delete_stats, $options ) = @_;
+ my( $self, $conn, $auth, $volumes, $delete_stats, $options, $oargs ) = @_;
my( $reqr, $evt ) = $U->checkses($auth);
return $evt if $evt;
$options ||= {};
- my $override = ($self->api_name =~ /override/);
+ if ($self->api_name =~ /override/) {
+ $oargs = { all => 1 } unless defined $oargs;
+ } else {
+ $oargs = {};
+ }
my $editor = new_editor( requestor => $reqr, xact => 1 );
my $retarget_holds = [];
my $auto_merge_vols = $options->{auto_merge_vols};
+ my $create_parts = $options->{create_parts};
+ my $copy_ids = [];
for my $vol (@$volumes) {
$logger->info("vol-update: investigating volume ".$vol->id);
if( $vol->isdeleted ) {
$logger->info("vol-update: deleting volume");
- my $cs = $editor->search_asset_copy(
- { call_number => $vol->id, deleted => 'f' } );
- return OpenILS::Event->new(
- 'VOLUME_NOT_EMPTY', payload => $vol->id ) if @$cs;
+ return $editor->die_event unless
+ $editor->allowed('UPDATE_VOLUME', $vol->owning_lib);
+
+ if(my $evt = $assetcom->delete_volume($editor, $vol, $oargs, $$options{force_delete_copies})) {
+ $editor->rollback;
+ return $evt;
+ }
- $vol->deleted('t');
- return $editor->event unless
+ return $editor->die_event unless
$editor->update_asset_call_number($vol);
-
} elsif( $vol->isnew ) {
$logger->info("vol-update: creating volume");
- $evt = OpenILS::Application::Cat::AssetCommon->create_volume( $override, $editor, $vol );
+ ($vol,$evt) = $assetcom->create_volume( $auto_merge_vols ? { all => 1} : $oargs, $editor, $vol );
return $evt if $evt;
} elsif( $vol->ischanged ) {
$logger->info("vol-update: update volume");
- my $resp = update_volume($vol, $editor, ($override or $auto_merge_vols));
- return $resp->{evt} if $resp->{evt};
- $vol = $resp->{merge_vol};
+
+ # Three cases here:
+ # 1) We're editing a volume, and not its copies.
+ # 2) We're editing a volume, and a subset of its copies.
+ # 3) We're editing a volume, and all of its copies.
+ #
+ # For 1) and 3), we definitely want to edit the volume
+ # itself (and possibly auto-merge), but for 2), we want
+ # to create a new volume (and possibly auto-merge).
+
+ if (scalar(@$copies) == 0) { # case 1
+
+ my $resp = update_volume($vol, $editor, ($oargs->{all} or grep { $_ eq 'VOLUME_LABEL_EXISTS' } @{$oargs->{events}} or $auto_merge_vols));
+ return $resp->{evt} if $resp->{evt};
+ $vol = $resp->{merge_vol} if $resp->{merge_vol};
+
+ } else {
+
+ my $resp = $editor->json_query({
+ select => {
+ acp => [
+ {transform => 'count', aggregate => 1, column => 'id', alias => 'count'}
+ ]
+ },
+ from => 'acp',
+ where => {
+ call_number => $vol->id,
+ deleted => 'f',
+ id => {'not in' => [ map { $_->id } @$copies ]}
+ }
+ });
+ if ($resp->[0]->{count} && $resp->[0]->{count} > 0) { # case 2
+
+ ($vol,$evt) = $assetcom->create_volume( $auto_merge_vols ? { all => 1} : $oargs, $editor, $vol );
+ return $evt if $evt;
+
+ } else { # case 3
+
+ my $resp = update_volume($vol, $editor, ($oargs->{all} or grep { $_ eq 'VOLUME_LABEL_EXISTS' } @{$oargs->{events}} or $auto_merge_vols));
+ return $resp->{evt} if $resp->{evt};
+ $vol = $resp->{merge_vol} if $resp->{merge_vol};
+ }
+
+ }
}
# now update any attached copies
if( $copies and @$copies and !$vol->isdeleted ) {
$_->call_number($vol->id) for @$copies;
- $evt = OpenILS::Application::Cat::AssetCommon->update_fleshed_copies(
- $editor, $override, $vol, $copies, $delete_stats, $retarget_holds, undef);
+ $evt = $assetcom->update_fleshed_copies(
+ $editor, $oargs, $vol, $copies, $delete_stats, $retarget_holds, undef, $create_parts);
return $evt if $evt;
+ push( @$copy_ids, $_->id ) for @$copies;
}
}
$editor->finish;
reset_hold_list($auth, $retarget_holds);
- return scalar(@$volumes);
+ if ($options->{return_copy_ids}) {
+ return $copy_ids;
+ } else {
+ return scalar(@$volumes);
+ }
}
my $evt;
my $merge_vol;
+ return {evt => $editor->event} unless
+ $editor->allowed('UPDATE_VOLUME', $vol->owning_lib);
+
return {evt => $evt}
if ( $evt = OpenILS::Application::Cat::AssetCommon->org_cannot_have_vols($editor, $vol->owning_lib) );
sub batch_volume_transfer {
- my( $self, $conn, $auth, $args ) = @_;
+ my( $self, $conn, $auth, $args, $oargs ) = @_;
my $evt;
my $rec = $$args{docid};
my $vol_ids = $$args{volumes};
my $override = 1 if $self->api_name =~ /override/;
+ $oargs = { all => 1 } unless defined $oargs;
$logger->info("merge: transferring volumes to lib=$o_lib and record=$rec");
# for each volume, see if there are any copies that have a
# remote circ_lib (circ_lib != vol->owning_lib and != $o_lib ).
# if so, warn them
- unless( $override ) {
+ unless( $override && ($oargs->{all} || grep { $_ eq 'COPY_REMOTE_CIRC_LIB' } @{$oargs->{events}}) ) {
for my $v (@all) {
$logger->debug("merge: searching for copies with remote circ_lib for volume ".$v->id);
}
}
+ # record the difference between the destination bib and the present bib
+ my $same_bib = $vol->record == $rec;
+
# see if there is a volume at the destination lib that
# already has the requested label
my $existing_vol = $e->search_asset_call_number(
# regardless of what volume was used as the destination,
# update any copies that have moved over to the new lib
- my $copies = $e->search_asset_copy({call_number=>$vol->id, deleted => 'f'});
+ my $copies = $e->search_asset_copy([
+ { call_number => $vol->id , deleted => 'f' },
+ {
+ flesh => 1,
+ flesh_fields => { acp => ['parts'] }
+ }
+ ]);
# update circ lib on the copies - make this a method flag?
for my $copy (@$copies) {
$e->update_asset_copy($copy) or return $e->event;
}
+ # update parts if volume is moving bib records
+ if( !$same_bib ) {
+ for my $copy (@$copies) {
+ my $parts = $copy->parts;
+ next unless $parts;
+ my $part_objs = [];
+ foreach my $part (@$parts) {
+ my $part_label = $part->label;
+ my $part_obj = $e->search_biblio_monograph_part(
+ {
+ label=>$part_label,
+ record=>$rec,
+ deleted=>'f'
+ }
+ )->[0];
+
+ if (!$part_obj) {
+ $part_obj = Fieldmapper::biblio::monograph_part->new();
+ $part_obj->label( $part_label );
+ $part_obj->record( $rec );
+ unless($e->create_biblio_monograph_part($part_obj)) {
+ return $e->die_event if $e->die_event;
+ }
+ }
+ push @$part_objs, $part_obj;
+ }
+
+ $copy->parts( $part_objs );
+ $copy->ischanged(1);
+ $evt = OpenILS::Application::Cat::AssetCommon->update_copy_parts($e, $copy, 1); #delete_parts=1
+ return $evt if $evt;
+ }
+ }
+
# Now see if any empty records need to be deleted after all of this
for(@rec_ids) {
);
sub create_serial_record_xml {
- my( $self, $client, $login, $source, $owning_lib, $record_id, $xml ) = @_;
+ my( $self, $client, $login, $source, $owning_lib, $record_id, $xml, $oargs ) = @_;
my $override = 1 if $self->api_name =~ /override/; # not currently used
+ $oargs = { all => 1 } unless defined $oargs; # Not currently used, but here for consistency.
my $e = new_editor(xact=>1, authtoken=>$login);
return $e->die_event unless $e->checkauth;
$e->commit and return $retval;
}
+__PACKAGE__->register_method(
+ method => "acn_sms_msg",
+ api_name => "open-ils.cat.acn.send_sms_text",
+ signature => q^
+ Send an SMS text from an A/T template for specified call numbers.
+
+ First parameter is null or an auth token (whether a null is allowed
+ depends on the sms.disable_authentication_requirement.callnumbers OU
+ setting).
+
+ Second parameter is the id of the context org.
+
+ Third parameter is the code of the SMS carrier from the
+ config.sms_carrier table.
+
+ Fourth parameter is the SMS number.
+
+ Fifth parameter is the ACN id's to target, though currently only the
+ first ACN is used by the template (and the UI is only sending one).
+ ^
+);
+
+sub acn_sms_msg {
+ my($self, $conn, $auth, $org_id, $carrier, $number, $target_ids) = @_;
+
+ my $sms_enable = $U->ou_ancestor_setting_value(
+ $org_id || $U->get_org_tree->id,
+ 'sms.enable'
+ );
+ # We could maybe make a Validator for this on the templates
+ if (! $U->is_true($sms_enable)) {
+ return -1;
+ }
+
+ my $disable_auth = $U->ou_ancestor_setting_value(
+ $org_id || $U->get_org_tree->id,
+ 'sms.disable_authentication_requirement.callnumbers'
+ );
+
+ my $e = new_editor(
+ (defined $auth)
+ ? (authtoken => $auth, xact => 1)
+ : (xact => 1)
+ );
+ return $e->event unless $disable_auth || $e->checkauth;
+
+ my $targets = $e->batch_retrieve_asset_call_number($target_ids);
+
+ $e->rollback; # FIXME using transaction because of pgpool/slony setups, but not
+ # simply making this method authoritative because of weirdness
+ # with transaction handling in A/T code that causes rollback
+ # failure down the line if handling many targets
+
+ return undef unless @$targets;
+ return $U->fire_object_event(
+ undef, # event_def
+ 'acn.format.sms_text', # hook
+ $targets,
+ $org_id,
+ undef, # granularity
+ { # user_data
+ sms_carrier => $carrier,
+ sms_notify => $number
+ }
+ );
+}
+
+
+
+__PACKAGE__->register_method(
+ method => "fixed_field_values_by_rec_type",
+ api_name => "open-ils.cat.biblio.fixed_field_values.by_rec_type",
+ argc => 2,
+ signature => {
+ desc => 'Given a record type (as in cmfpm.rec_type), return fixed fields and their possible values as known to the DB',
+ params => [
+ {desc => 'Record Type', type => 'string'},
+ {desc => '(Optional) Fixed field', type => 'string'},
+ ]
+ },
+ return => {desc => 'an object in which the keys are fixed fields and the values are arrays representing the set of all unique values for that fixed field in that record type', type => 'object' }
+);
+
+
+sub fixed_field_values_by_rec_type {
+ my ($self, $conn, $rec_type, $fixed_field) = @_;
+
+ my $e = new_editor;
+ my $values = $e->json_query({
+ select => {
+ crad => ["fixed_field"],
+ ccvm => [qw/code value/],
+ cmfpm => [qw/length default_val/],
+ },
+ distinct => 1,
+ from => {
+ ccvm => {
+ crad => {
+ join => {
+ cmfpm => {
+ fkey => "fixed_field",
+ field => "fixed_field"
+ }
+ }
+ }
+ }
+ },
+ where => {
+ "+cmfpm" => {rec_type => $rec_type},
+ defined $fixed_field ?
+ ("+crad" => {fixed_field => $fixed_field}) : ()
+ },
+ order_by => [
+ {class => "crad", field => "fixed_field"},
+ {class => "ccvm", field => "code"}
+ ]
+ }) or return $e->die_event;
+
+ my $result = {};
+ for my $row (@$values) {
+ $result->{$row->{fixed_field}} ||= [];
+ push @{$result->{$row->{fixed_field}}}, [@$row{qw/code value length default_val/}];
+ }
+
+ return $result;
+}
+
+__PACKAGE__->register_method(
+ method => "retrieve_tag_table",
+ api_name => "open-ils.cat.tag_table.all.retrieve.local",
+ stream => 1,
+ argc => 3,
+ signature => {
+ desc => "Retrieve set of MARC tags, subfields, and indicator values for the user's OU",
+ params => [
+ {desc => 'Authtoken', type => 'string'},
+ {desc => 'MARC Format', type => 'string'},
+ {desc => 'MARC Record Type', type => 'string'},
+ ]
+ },
+ return => {desc => 'Structure representing the tag table available to that user', type => 'object' }
+);
+__PACKAGE__->register_method(
+ method => "retrieve_tag_table",
+ api_name => "open-ils.cat.tag_table.all.retrieve.stock",
+ stream => 1,
+ argc => 3,
+ signature => {
+ desc => 'Retrieve set of MARC tags, subfields, and indicator values for stock MARC standard',
+ params => [
+ {desc => 'Authtoken', type => 'string'},
+ {desc => 'MARC Format', type => 'string'},
+ {desc => 'MARC Record Type', type => 'string'},
+ ]
+ },
+ return => {desc => 'Structure representing the stock tag table', type => 'object' }
+);
+__PACKAGE__->register_method(
+ method => "retrieve_tag_table",
+ api_name => "open-ils.cat.tag_table.field_list.retrieve.local",
+ stream => 1,
+ argc => 3,
+ signature => {
+ desc => "Retrieve set of MARC tags for available to the user's OU",
+ params => [
+ {desc => 'Authtoken', type => 'string'},
+ {desc => 'MARC Format', type => 'string'},
+ {desc => 'MARC Record Type', type => 'string'},
+ ]
+ },
+ return => {desc => 'Structure representing the tags available to that user', type => 'object' }
+);
+__PACKAGE__->register_method(
+ method => "retrieve_tag_table",
+ api_name => "open-ils.cat.tag_table.field_list.retrieve.stock",
+ stream => 1,
+ argc => 3,
+ signature => {
+ desc => 'Retrieve set of MARC tags for stock MARC standard',
+ params => [
+ {desc => 'Authtoken', type => 'string'},
+ {desc => 'MARC Format', type => 'string'},
+ {desc => 'MARC Record Type', type => 'string'},
+ ]
+ },
+ return => {desc => 'Structure representing the stock MARC tags', type => 'object' }
+);
+
+sub retrieve_tag_table {
+ my( $self, $conn, $auth, $marc_format, $marc_record_type ) = @_;
+ my $e = new_editor( authtoken=>$auth, xact=>1 );
+ return $e->die_event unless $e->checkauth;
+
+ my $field_list_only = ($self->api_name =~ /\.field_list\./) ? 1 : 0;
+ my $context_ou;
+ if ($self->api_name =~ /\.local$/) {
+ $context_ou = $e->requestor->ws_ou;
+ }
+
+ my %sf_by_tag;
+ unless ($field_list_only) {
+ my $subfields = $e->json_query(
+ { from => [ 'config.ou_marc_subfields', 1, $marc_record_type, $context_ou ] }
+ );
+ foreach my $sf (@$subfields) {
+ my $sf_data = {
+ code => $sf->{code},
+ description => $sf->{description},
+ mandatory => $sf->{mandatory},
+ repeatable => $sf->{repeatable},
+ };
+ if ($sf->{value_ctype}) {
+ $sf_data->{value_list} = $e->json_query({
+ select => { ccvm => [
+ 'code',
+ { column => 'value', alias => 'description' }
+ ]
+ },
+ from => 'ccvm',
+ where => { ctype => $sf->{value_ctype} },
+ order_by => { ccvm => { code => {} } },
+ });
+ }
+ push @{ $sf_by_tag{$sf->{tag}} }, $sf_data;
+ }
+ }
+
+ my $fields = $e->json_query(
+ { from => [ 'config.ou_marc_fields', 1, $marc_record_type, $context_ou ] }
+ );
+
+ foreach my $field (@$fields) {
+ next if $field->{hidden} eq 't';
+ unless ($field_list_only) {
+ my $tag = $field->{tag};
+ if ($tag ge '010') {
+ for my $pos (1..2) {
+ my $ind_ccvm_key = "${marc_format}_${marc_record_type}_${tag}_ind_${pos}";
+ my $indvals = $e->json_query({
+ select => { ccvm => [
+ 'code',
+ { column => 'value', alias => 'description' }
+ ]
+ },
+ from => 'ccvm',
+ where => { ctype => $ind_ccvm_key }
+ });
+ next unless defined($indvals);
+ $field->{"ind$pos"} = $indvals;
+ }
+ $field->{subfields} = exists($sf_by_tag{$tag}) ? $sf_by_tag{$tag} : [];
+ }
+ }
+ $conn->respond($field);
+ }
+}
+
1;
# vi:et:ts=4:sw=4