From dfb6070a2b9474e0e7bcb9d6e69d3256f280b89c Mon Sep 17 00:00:00 2001
From: Ere Maijala <ere.maijala@helsinki.fi>
Date: Mon, 27 Aug 2018 15:07:12 +0300
Subject: [PATCH] Bug 20447: Include basic summary holdings in the OAI-PMH
 responses.

https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=20447

If include_items is set in oai conf, also basic location information from holdings records is included in the returned MARC records.
---
 C4/Biblio.pm                  |  6 +++
 C4/Holdings.pm                | 76 +++++++++++++++++++++++++++++++++++
 Koha/OAI/Server/ListBase.pm   | 10 ++++-
 Koha/OAI/Server/Repository.pm |  1 +
 4 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/C4/Biblio.pm b/C4/Biblio.pm
index 24aee2a10b..3b1f850dca 100644
--- a/C4/Biblio.pm
+++ b/C4/Biblio.pm
@@ -2821,6 +2821,12 @@ sub EmbedItemsInMarcBiblio {
 
     $itemnumbers = [] unless defined $itemnumbers;
 
+    if ( C4::Context->preference('SummaryHoldings') && !@$itemnumbers ) {
+        require C4::Holdings;
+        my $holdings_fields = C4::Holdings::GetMarcHoldingsFields( $biblionumber );
+        $marc->append_fields(@$holdings_fields) if ( @$holdings_fields );
+    }
+
     my $frameworkcode = GetFrameworkCode($biblionumber);
     _strip_item_fields($marc, $frameworkcode);
 
diff --git a/C4/Holdings.pm b/C4/Holdings.pm
index 9b0ad5b2db..ca9879e2bf 100644
--- a/C4/Holdings.pm
+++ b/C4/Holdings.pm
@@ -426,6 +426,35 @@ sub GetMarcHoldingsByBiblionumber {
     return \@records;
 }
 
+=head2 GetMarcHoldingsFields
+
+  my @marc_fields = GetMarcHoldingsFields($biblionumber);
+
+Returns an array of MARC::Record objects of the holdings for the biblio.
+
+=cut
+
+sub GetMarcHoldingsFields {
+	my ( $biblionumber ) = @_;
+    # This is so much faster than using Koha::Holdings->search that it makes sense even if it's ugly.
+    my $sth = C4::Context->dbh->prepare( 'SELECT * FROM holdings WHERE biblionumber = ?' );
+    $sth->execute( $biblionumber );
+    my $holdings = $sth->fetchall_arrayref({});
+    $sth->finish();
+    my @holdings_fields;
+    my ( $holdingstag, $holdingssubfield ) = GetMarcHoldingFromKohaField( 'holdings.holdingbranch' );
+    ITEMLOOP: foreach my $holding (@$holdings) {
+        my $mungedholding = {
+            map {
+                defined($holding->{$_}) && $holding->{$_} ne '' ? ("holdings.$_" => $holding->{$_}) : ()
+            } keys %{ $holding }
+        };
+        my $marc = TransformKohaHoldingToMarc($mungedholding);
+        push @holdings_fields, $marc->field( $holdingstag );
+    }
+    return \@holdings_fields;
+}
+
 =head2 GetHoldingFrameworkCode
 
   $frameworkcode = GetFrameworkCode( $holding_id )
@@ -788,6 +817,53 @@ sub TransformMarcHoldingToKohaOneField {
     return $retval;
 }
 
+=head2 TransformKohaHoldingToMarc
+
+    $record = TransformKohaToMarc( $hash [, $params ] )
+
+This function builds partial MARC::Record from holdings hash entries.
+This function is called when embedding holdings into a biblio record.
+
+=cut
+
+sub TransformKohaHoldingToMarc {
+    my ( $hash, $params ) = @_;
+
+    my $record = MARC::Record->new();
+    SetMarcUnicodeFlag( $record, C4::Context->preference("marcflavour") );
+
+    # In the next call we use the Default holdings framework, since it is considered
+    # authoritative for Koha to Marc mappings.
+    my $mss = C4::Biblio::GetMarcSubfieldStructure( 'HLD', { unsafe => 1 } ); # do not change framewok
+    my $tag_hr = {};
+    while ( my ($kohafield, $value) = each %$hash ) {
+        foreach my $fld ( @{ $mss->{$kohafield} } ) {
+            my $tagfield    = $fld->{tagfield};
+            my $tagsubfield = $fld->{tagsubfield};
+            next if !$tagfield;
+            my @values = $params->{no_split}
+                ? ( $value )
+                : split(/\s?\|\s?/, $value, -1);
+            foreach my $value ( @values ) {
+                next if $value eq '';
+                $tag_hr->{$tagfield} //= [];
+                push @{$tag_hr->{$tagfield}}, [($tagsubfield, $value)];
+            }
+        }
+    }
+    foreach my $tag (sort keys %$tag_hr) {
+        my @sfl = @{$tag_hr->{$tag}};
+        @sfl = sort { $a->[0] cmp $b->[0]; } @sfl;
+        @sfl = map { @{$_}; } @sfl;
+        # Special care for control fields: remove the subfield indication @
+        # and do not insert indicators.
+        my @ind = $tag < 10 ? () : ( " ", " " );
+        @sfl = grep { $_ ne '@' } @sfl if $tag < 10;
+        $record->insert_fields_ordered( MARC::Field->new($tag, @ind, @sfl) );
+    }
+    return $record;
+}
+
 1;
 
 
diff --git a/Koha/OAI/Server/ListBase.pm b/Koha/OAI/Server/ListBase.pm
index 92af1c3661..5e7c2bd3c5 100644
--- a/Koha/OAI/Server/ListBase.pm
+++ b/Koha/OAI/Server/ListBase.pm
@@ -69,8 +69,9 @@ sub GetRecords {
         if ($include_items) {
             $sql .= "
                 OR biblionumber IN (SELECT biblionumber from deleteditems WHERE timestamp >= ? AND timestamp <= ?)
+                OR biblionumber IN (SELECT biblionumber from holdings WHERE timestamp >= ? AND timestamp <= ?)
             ";
-            push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'});
+            push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'}, $token->{'from_arg'}, $token->{'until_arg'});
             if (!$deleted) {
                 $sql .= "
                     OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ?)
@@ -106,6 +107,8 @@ sub GetRecords {
                     SELECT timestamp FROM deletedbiblio_metadata WHERE biblionumber = ?
                     UNION
                     SELECT timestamp FROM deleteditems WHERE biblionumber = ?
+                    UNION
+                    SELECT timestamp FROM holdings WHERE biblionumber = ?
                 ) bis
             ";
         } else {
@@ -117,6 +120,8 @@ sub GetRecords {
                     SELECT timestamp FROM deleteditems WHERE biblionumber = ?
                     UNION
                     SELECT timestamp FROM items WHERE biblionumber = ?
+                    UNION
+                    SELECT timestamp FROM holdings WHERE biblionumber = ?
                 ) bi
             ";
         }
@@ -139,7 +144,8 @@ sub GetRecords {
                 );
                 last STAGELOOP;
             }
-            my @params = $deleted ? ( $biblionumber, $biblionumber ) : ( $biblionumber, $biblionumber, $biblionumber );
+            my @params = $deleted ? ( $biblionumber, $biblionumber, $biblionumber )
+                : ( $biblionumber, $biblionumber, $biblionumber, $biblionumber );
             $record_sth->execute( @params ) || die( 'Could not execute statement: ' . $sth->errstr );
 
             my ($timestamp) = $record_sth->fetchrow;
diff --git a/Koha/OAI/Server/Repository.pm b/Koha/OAI/Server/Repository.pm
index 30ace36f5e..e95200dd69 100644
--- a/Koha/OAI/Server/Repository.pm
+++ b/Koha/OAI/Server/Repository.pm
@@ -93,6 +93,7 @@ mode. A configuration file koha-oai.conf can look like that:
       xsl_file: /usr/local/koha/koha-tmpl/intranet-tmpl/xslt/UNIMARCslim2OAIDC.xsl
 
 Note the 'include_items' parameter which is the only mean to return item-level info.
+If summary holdings are enabled, 'include_items' includes their location information too.
 
 =cut
 
-- 
2.17.1