From fdeaa6f92d82e5d8e276f5e358838a970a6d2a73 Mon Sep 17 00:00:00 2001 From: Tomas Cohen Arazi Date: Fri, 9 Jun 2023 15:42:23 -0300 Subject: [PATCH] Bug 33036: REST API: Merge biblio records implements merging of records + attached items, subscriptions etc via the API as an alternative to the web interface: cgi-bin/koha/cataloguing/merge.pl To Test: 1) you need an API user with the permissions "editcatalogue" 2) two records: one to be merged into (with biblio_id, eg 262) and another one from which to merge (with biblio_id_to_merge, eg 9) which will be deleted! both records may/should have items, subscription, subscriptionhistory, serial, suggestions orders and holds 3) check both records via the web 4) Apply patch 5) execute API calls eg like PUT /biblios/{biblio_id}/merge/{biblio_id_to_merge} eg: curl -s -u koha:koha -X PUT "http://127.0.0.1:8081/api/v1/biblios/262/merge/9" 6) the record with the id is deleted now, the record with has all items, etc attached, return code is 200, with the message {"message":"Successfuly merged 9 into 262"} 7) optionally a full MARCXML record may be sent as body of the API call curl -s -u koha:koha -X PUT "http://127.0.0.1:8081/api/v1/biblios/262/merge/2" -d @marcfile.xml 8) now also the content of the record with is replaced with the content of the MARCXML file 9) Sign off. 10) Thx Sponsored-by: Technische Hochschule Wildau Co-authored-by: domm@plix.at --- Koha/REST/V1/Biblios.pm | 96 +++++++++++++++++++++++++ api/v1/swagger/paths/biblios_merge.yaml | 51 +++++++++++++ api/v1/swagger/swagger.yaml | 2 + 3 files changed, 149 insertions(+) create mode 100644 api/v1/swagger/paths/biblios_merge.yaml diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm index 6457d49d7a..966f25c270 100644 --- a/Koha/REST/V1/Biblios.pm +++ b/Koha/REST/V1/Biblios.pm @@ -25,6 +25,9 @@ use Koha::Ratings; use Koha::RecordProcessor; use C4::Biblio qw( DelBiblio AddBiblio ModBiblio ); use C4::Search qw( FindDuplicate ); +use C4::Serials qw( CountSubscriptionFromBiblionumber ); +use C4::Reserves qw( MergeHolds ); +use C4::Acquisition qw( ModOrder GetOrdersByBiblionumber ); use C4::Barcodes::ValueBuilder; use C4::Context; @@ -859,4 +862,97 @@ sub list { }; } +=head3 merge + +Controller function that handles merging two biblios. If an optional +MARCXML is provided as the request body, this MARCXML replaces the +bibliodata of the merge target biblio. + +=cut + +sub merge { + my $c = shift->openapi->valid_input or return; + my $ref_biblionumber = $c->validation->param('biblio_id'); + my $bn_merge = $c->validation->param('biblio_id_to_merge'); + my $dbh = C4::Context->dbh; + + my $body = $c->req->body; + my $biblio = Koha::Biblios->find( $ref_biblionumber ); + if ( not defined $biblio ) { + return $c->render( + status => 404, + json => { error => sprintf("[%s] biblio to merge into not found", $ref_biblionumber) } + ); + } + + my $from_biblio = Koha::Biblios->find( $bn_merge ); + if ( not defined $from_biblio ) { + return $c->render( + status => 404, + # openapi => { error => sprintf("[%s] biblio to merge from not found", $bn_merge) } + json => { error => sprintf("[%s] biblio to merge from not found", $bn_merge) } + ); + } + + return try { + if ($body) { + my $record = MARC::Record->new_from_xml($body, 'UTF-8', 'MARC21'); + $record->leader($biblio->metadata->record->leader()); + ModBiblio($record, $ref_biblionumber, $biblio->frameworkcode); + } + + $from_biblio->items->move_to_biblio($biblio); + $from_biblio->article_requests->update({ biblionumber => $ref_biblionumber }, { no_triggers => 1 }); + + my $sth_subscription = $dbh->prepare(" + UPDATE subscription SET biblionumber = ? WHERE biblionumber = ? + "); + + my $sth_subscriptionhistory = $dbh->prepare(" + UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ? + "); + my $sth_serial = $dbh->prepare(" + UPDATE serial SET biblionumber = ? WHERE biblionumber = ? + "); + my $sth_suggestions = $dbh->prepare(" + UPDATE suggestions SET biblionumber = ? WHERE biblionumber = ? + "); + + # Moving subscriptions from the other record to the reference record + my $subcount = CountSubscriptionFromBiblionumber($bn_merge); + if ($subcount > 0) { + $sth_subscription->execute($ref_biblionumber, $bn_merge); + $sth_subscriptionhistory->execute($ref_biblionumber, $bn_merge); + } + + # Moving serials + $sth_serial->execute($ref_biblionumber, $bn_merge); + + # Moving suggestions + $sth_suggestions->execute($ref_biblionumber, $bn_merge); + + # Moving orders (orders linked to items of frombiblio have already been moved by move_to_biblio) + my @allorders = GetOrdersByBiblionumber($bn_merge); + foreach my $myorder (@allorders) { + $myorder->{'biblionumber'} = $ref_biblionumber; + ModOrder ($myorder); + # TODO : add error control (in ModOrder?) + } + + # Deleting the other records + # Move holds + MergeHolds($dbh, $ref_biblionumber, $bn_merge); + my $error = DelBiblio($bn_merge); + if ($error) { + $c->render( status => 400, json => {error => $error}); + } + + return $c->render( status => 200, openapi => { message => sprintf("Successfully merged %s into %s", + $bn_merge, $ref_biblionumber) + }); + } catch { + $c->render( status => 400, json => {error => $_}); + }; +} + 1; diff --git a/api/v1/swagger/paths/biblios_merge.yaml b/api/v1/swagger/paths/biblios_merge.yaml new file mode 100644 index 0000000000..4714be1532 --- /dev/null +++ b/api/v1/swagger/paths/biblios_merge.yaml @@ -0,0 +1,51 @@ +--- +"/biblios/{biblio_id}/merge/{biblio_id_to_merge}": + put: + x-mojo-to: Biblios#merge + operationId: mergeBiblios + tags: + - merge_biblios + summary: Merge Biblios + parameters: + - name: biblio_id + in: path + description: Bilblionumber to be merged into + required: true + type: string + - name: biblio_id_to_merge + in: path + required: true + description: Biblionumber from which to merge + type: string + - name: body + in: body + description: MARCXML for final record + required: false + schema: + type: object + responses: + '200': + description: Result message + '404': + description: Biblio not found + schema: + "$ref": "../swagger.yaml#/definitions/error" + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal server error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "editcatalogue" diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 9e60aa77a2..d32fc45551 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -183,6 +183,8 @@ paths: $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items" "/biblios/{biblio_id}/item_groups/{item_group_id}/items/{item_id}": $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items~1{item_id}" + "/biblios/{biblio_id}/merge/{biblio_id_to_merge}": + $ref: "./paths/biblios_merge.yaml#/~1biblios~1{biblio_id}~1merge~1{biblio_id_to_merge}" "/cash_registers/{cash_register_id}/cashups": $ref: "./paths/cash_registers.yaml#/~1cash_registers~1{cash_register_id}~1cashups" "/cashups/{cashup_id}": -- 2.20.1