return $counts->[0]->{id};
}
+__PACKAGE__->register_method(
+ method => 'transfer_lineitem',
+ api_name => 'open-ils.acq.lineitem.transfer_to_bib',
+ signature => {
+ desc => q/Transfers a lineitem and all associated
+ assets to a different target bib record /,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'The lineitem id', type => 'number'},
+ {desc => 'The target bib record id', type => 'number'},
+ ],
+ return => {desc => q/Updated lineitem on success, event on error/}
+ }
+);
+
+sub transfer_lineitem {
+ my($self, $conn, $auth, $li_id, $bib_id, $ops) = @_;
+ $ops ||= {}; # future use
+
+ my $e = new_editor(authtoken=>$auth, xact=>1);
+ return $e->die_event unless $e->checkauth;
+
+ my ($li, $evt, $perm_org) = fetch_and_check_li($e, $li_id, 'write');
+ return $evt if $evt;
+
+ my $orig_bib_id = $li->eg_bib_id; # capture for later.
+
+ if ($orig_bib_id eq $bib_id) {
+ # Transferring to the same bib. Nothing to do.
+ $e->rollback;
+ return $li;
+ }
+
+ # Sanity check the target bib.
+
+ my $bre = $e->retrieve_biblio_record_entry($bib_id)
+ or return $e->die_event;
+
+ if ($U->is_true($bre->deleted)) {
+ $e->rollback;
+ return OpenILS::Event->new(
+ 'BAD_PARAMS', {note => 'Target bib is deleted'});
+ }
+
+ # Point the lineitem at the selected bib record. Note this will
+ # work even if the lineitem is not currently pointing at any bib
+ # record. IOW, this could be used to manually link LI's to bibs.
+
+ $li->eg_bib_id($bib_id);
+ $li->edit_time('now');
+ $li->editor($e->requestor->id);
+ $e->update_acq_lineitem($li) or return $e->die_event;
+
+
+ # Transfer any asset.call_number's (with their linked asset.copy's)
+ # that were created for this lineitem to the new bib record.
+ my $acp_ids = $e->json_query({
+ select => {acqlid => ['eg_copy_id']},
+ from => 'acqlid',
+ where => {lineitem => $li_id}
+ });
+
+ # TODO: Transfer monograph parts.
+
+ my $copies = $e->search_asset_copy(
+ {id => [map {$_->{eg_copy_id}} @$acp_ids]});
+
+ if (@$copies) {
+
+ # Group copies into call number batches so each call number can
+ # be assessed and processed once.
+ my %cn_batches;
+ for my $copy (@$copies) {
+ my $cn_id = $copy->call_number;
+ $cn_batches{$cn_id} = [] unless $cn_batches{$cn_id};
+ push(@{$cn_batches{$cn_id}}, $copy);
+ }
+
+ while (my ($cn_id, $cn_copies) = each %cn_batches) {
+ my $evt = transfer_order_volume($e, $bib_id, $cn_id, $cn_copies);
+ return $evt if $evt;
+ }
+
+ # Transfer parts as needed to the target bib record.
+ my $part_maps = $e->search_asset_copy_part_map([
+ {target_copy => [map {$_->id} @$copies]},
+ {flesh => 1, flesh_fields => {acpm => ['part']}}
+ ]);
+
+ for my $map (@$part_maps) {
+ my $evt = transfer_order_copy_parts($e, $bib_id, $map);
+ return $evt if $evt;
+ }
+ }
+
+ $e->commit;
+
+ return $li;
+}
+
+# Transfer copy part map from the source bib to the target bib
+# If a part exists on the target bib with the same label, use it.
+# Otherwise, create a new part linked to the target bib to be used
+# by the migrated copy.
+#
+# Returns undef on success, event on error.
+sub transfer_order_copy_parts {
+ my ($e, $bib_id, $map) = @_;
+
+ # See if the same part exists on the target bib.
+ my $existing = $e->search_biblio_monograph_part({
+ record => $bib_id,
+ label => $map->part->label,
+ deleted => 'f'
+ })->[0];
+
+ if ($existing) {
+ # matching part exists on target record.
+ # Teach existing copy part map to use it.
+
+ $map->part($existing->id);
+
+ } else {
+ # No matching part exists on the target record.
+ # Create one and teach the existing copy part map to use it.
+
+ my $new_part = Fieldmapper::biblio::monograph_part->new;
+ $new_part->record($bib_id);
+ $new_part->label($map->part->label);
+ $new_part->label_sortkey($map->part->label_sortkey);
+ $e->create_biblio_monograph_part($new_part) or return $e->die_event;
+
+ $map->part($new_part->id);
+ }
+
+ $e->update_asset_copy_part_map($map) or return $e->die_event;
+
+ return undef;
+}
+
+# 1. If every copy linked to the CN is represented by ordered copies
+# -- the ones we're processing here -- then transfer the call number
+# wholesale to the new bib record.
+#
+# 2. Otherwise, find-or-create a like call number for the target
+# bib record and update the ordered copies to use the new/found
+# call number.
+#
+# Returns undef on success, event on error.
+sub transfer_order_volume {
+ my ($e, $bib_id, $cn_id, $cn_copies) = @_;
+
+ my $cn = $e->retrieve_asset_call_number($cn_id) or return $e->die_event;
+
+ my $copy_count = $e->json_query({
+ select => {acp => [{
+ aggregate => 1,
+ transform => 'count',
+ column => 'id'
+ }]},
+ from => 'acp',
+ where => {call_number => $cn_id}
+ })->[0];
+
+ my $target_cn;
+ my $evt;
+
+ if ($copy_count->{count} == scalar(@$cn_copies)) {
+ # Order copies represent all copies linked to the callnumber
+ # See if a matching CN exists at the target bib and, if so,
+ # transfer our copies to the existing CN. Otherwise,
+ # simply point our call number at the new bib.
+
+ # See if a matching CN already exists at the target bib
+ $target_cn = OpenILS::Application::Cat::AssetCommon->volume_exists(
+ $e, $bib_id, $cn->label, $cn->owning_lib, $cn->prefix, $cn->suffix
+ );
+
+ if ($target_cn) {
+ # We are transferring all attached copies to the matching
+ # callnumber on the target bib. This call number is no
+ # longer needed. Delete it.
+ $evt = OpenILS::Application::Cat::AssetCommon->delete_volume(
+ $e, $cn,
+ 1, # override
+ 0, # delete copies
+ 1 # skip copy checks
+ );
+
+ return $evt if $evt;
+
+ } else {
+ # No matching CN exists. Point our CN at the target bib.
+ $cn->record($bib_id);
+ $cn->edit_date('now');
+ $cn->editor($e->requestor->id);
+ $e->update_asset_call_number($cn) or return $e->die_event;
+ return undef; # all done
+ }
+ }
+
+ # Copies need to be migrated to the target call number.
+
+ ($target_cn, $evt) =
+ OpenILS::Application::Cat::AssetCommon->find_or_create_volume(
+ $e, $cn->label, $bib_id, $cn->owning_lib,
+ $cn->prefix, $cn->suffix, $cn->label_class
+ ) unless $target_cn;
+
+ return $evt if $evt;
+
+ # ... transfer copies.
+ # Transfer order copies to the new call number.
+ for my $copy (@$cn_copies) {
+ $copy->call_number($target_cn->id);
+ $copy->edit_date('now');
+ $copy->editor($e->requestor->id);
+ $e->update_asset_copy($copy) or return $e->die_event;
+ }
+
+ return undef;
+}
+
1;