From 3aa879dc7194fe533fd7038035000c15d2de3bd4 Mon Sep 17 00:00:00 2001
From: Laurent Laffont <llaffont@afi-sa.fr>
Date: Fri, 27 May 2022 16:46:54 +0200
Subject: [PATCH] dev #127338 Koha : item deduplication using itemnumber
 (995$9)

---
 VERSIONS_WIP/137338                           |  1 +
 .../php/classes/KohaRecordIntegrationTest.php |  2 +-
 library/Class/Exemplaire.php                  |  8 +++
 .../SIGB/Koha/GetRecordsResponseReader.php    |  4 +-
 library/Class/WebService/SIGB/Koha/Notice.php | 32 ++++++++++++
 library/Class/WebService/SIGB/Notice.php      | 15 +++---
 tests/fixtures/KohaFixtures.php               |  3 +-
 .../Class/WebService/SIGB/KohaTest.php        | 50 ++++++++++++++++++-
 8 files changed, 100 insertions(+), 15 deletions(-)
 create mode 100644 VERSIONS_WIP/137338
 create mode 100644 library/Class/WebService/SIGB/Koha/Notice.php

diff --git a/VERSIONS_WIP/137338 b/VERSIONS_WIP/137338
new file mode 100644
index 00000000000..31d3df403b9
--- /dev/null
+++ b/VERSIONS_WIP/137338
@@ -0,0 +1 @@
+ - correctif #137338 : SIGB Koha : dédoublonnage des exemplaires sur le sous-champ 995$9 (itemnumber)
\ No newline at end of file
diff --git a/cosmogramme/tests/php/classes/KohaRecordIntegrationTest.php b/cosmogramme/tests/php/classes/KohaRecordIntegrationTest.php
index dc3cdf2cbc5..1b4809c6a2a 100644
--- a/cosmogramme/tests/php/classes/KohaRecordIntegrationTest.php
+++ b/cosmogramme/tests/php/classes/KohaRecordIntegrationTest.php
@@ -917,7 +917,6 @@ class KohaRecordIntegrationUnimarcWithSeveralISBNTest extends KohaRecordIntegrat
 abstract class NoticeIntegrationWithSubfieldDeduplicationTest extends KohaRecordIntegrationTestCase {
   public function setUp() {
     parent::setUp();
-    $this->_notice_integration = new notice_integration();
     $this->loadNotice('unimarc_oblivion');
 
     $zone995 = serialize([['code' => '9', 'valeur' => '1']]);
@@ -974,6 +973,7 @@ class NoticeIntegrationWithSubfieldDeduplicationDollarNineTest extends NoticeInt
 
 
 
+
 class NoticeIntegrationWithSubfieldDeduplicationNoneTest extends NoticeIntegrationWithSubfieldDeduplicationTest {
   public function getProfilDonnees() {
      return Class_IntProfilDonnees::forKoha()
diff --git a/library/Class/Exemplaire.php b/library/Class/Exemplaire.php
index db353c36ed1..47e125ed48d 100644
--- a/library/Class/Exemplaire.php
+++ b/library/Class/Exemplaire.php
@@ -556,6 +556,14 @@ class Class_Exemplaire extends Storm_Model_Abstract {
   }
 
 
+  public function getUniqItemNumber() : int {
+    if (!$data_profile = $this->getDataProfile())
+      return 0;
+
+    return (int)$this->getSubfield($data_profile->getItemUniqSubfield());
+  }
+
+
   public function toRaw() {
     $raw = $this->getRawAttributes() ;
     // LL: pour Opsys nous avons besoin de l'identifiant de l'annexe, d'où sauvegarde pour réutilisation dans ReservationLink
diff --git a/library/Class/WebService/SIGB/Koha/GetRecordsResponseReader.php b/library/Class/WebService/SIGB/Koha/GetRecordsResponseReader.php
index 33d1452fb92..57ce34ea82d 100644
--- a/library/Class/WebService/SIGB/Koha/GetRecordsResponseReader.php
+++ b/library/Class/WebService/SIGB/Koha/GetRecordsResponseReader.php
@@ -89,7 +89,7 @@ class Class_WebService_SIGB_Koha_GetRecordsResponseReader {
 
 
   public function getNoticeFromXML($xml) {
-    $this->_record = new Class_WebService_SIGB_Notice(0);
+    $this->_record = new Class_WebService_SIGB_Koha_Notice(0);
     if (false === ($doc = $this->_getReader()->read($xml)))
       return $this->_record;
 
@@ -333,4 +333,4 @@ class Class_WebService_SIGB_Koha_GetRecordsResponseReaderXmlReader {
     return $document;
   }
 
-}
\ No newline at end of file
+}
diff --git a/library/Class/WebService/SIGB/Koha/Notice.php b/library/Class/WebService/SIGB/Koha/Notice.php
new file mode 100644
index 00000000000..413251550b1
--- /dev/null
+++ b/library/Class/WebService/SIGB/Koha/Notice.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright (c) 2012-2022, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * BOKEH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * BOKEH is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with BOKEH; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+ */
+
+
+class Class_WebService_SIGB_Koha_Notice extends Class_WebService_SIGB_Notice {
+  public function getFirstILSItemLike(Class_Exemplaire $exemplaire) : Class_WebService_SIGB_Exemplaire {
+    $item_number = $exemplaire->getUniqItemNumber();
+    $ils_item = $this->_getFirstILSItemBy(fn($ils_item) => $item_number === (int)$ils_item->getId());
+
+    return $ils_item->isValid()
+      ? $ils_item
+      : parent::getFirstILSItemLike($exemplaire);
+  }
+}
diff --git a/library/Class/WebService/SIGB/Notice.php b/library/Class/WebService/SIGB/Notice.php
index 7933868b195..aacdfcfc0b1 100644
--- a/library/Class/WebService/SIGB/Notice.php
+++ b/library/Class/WebService/SIGB/Notice.php
@@ -102,19 +102,18 @@ class Class_WebService_SIGB_Notice {
 
   public function getFirstILSItemLike(Class_Exemplaire $item) : Class_WebService_SIGB_Exemplaire {
     return ('' !== ($barcode = (string) $item->getCodeBarres()))
-      ? $this->_getFirstILSItemByBarcode($barcode)
+      ? $this->_getFirstILSItemBy(fn($item) => ($barcode === (string)$item->getCodeBarre()))
       : Class_WebService_SIGB_Exemplaire::newInstance();
   }
 
 
-  protected function _getFirstILSItemByBarcode(string $barcode) : Class_WebService_SIGB_Exemplaire {
+  protected function _getFirstILSItemBy(Closure $detect_function) : Class_WebService_SIGB_Exemplaire {
     if ( ! $this->exemplaires)
       return Class_WebService_SIGB_Exemplaire::newInstance();
 
-    return (new Storm_Collection($this->exemplaires))
-      ->detect(function($item) use ($barcode)
-      {
-        return $barcode === (string) $item->getCodeBarre();
-      }) ?? Class_WebService_SIGB_Exemplaire::newInstance();
+    return
+      (new Storm_Collection($this->exemplaires))->detect($detect_function)
+      ??
+      Class_WebService_SIGB_Exemplaire::newInstance();
   }
-}
\ No newline at end of file
+}
diff --git a/tests/fixtures/KohaFixtures.php b/tests/fixtures/KohaFixtures.php
index c3df23cb4a3..f19519f847a 100644
--- a/tests/fixtures/KohaFixtures.php
+++ b/tests/fixtures/KohaFixtures.php
@@ -907,7 +907,6 @@ class KohaFixtures {
                 <ccode>ROMJEUN</ccode>
                 <itemcallnumber>JR ROW h</itemcallnumber>
                 <date_due></date_due>
-                <barcode>2661660441</barcode>
                 <itemlost>0</itemlost>
                 <datelastseen>2011-02-18</datelastseen>
                 <homebranch>BDM</homebranch>
@@ -5585,4 +5584,4 @@ class KohaFixtures {
   <pickup_location>Vitré - Médiathèque Madame de Sévigné</pickup_location>
 </HoldItem>';
   }
-}
\ No newline at end of file
+}
diff --git a/tests/library/Class/WebService/SIGB/KohaTest.php b/tests/library/Class/WebService/SIGB/KohaTest.php
index e01c59e01b6..748e86be959 100644
--- a/tests/library/Class/WebService/SIGB/KohaTest.php
+++ b/tests/library/Class/WebService/SIGB/KohaTest.php
@@ -246,67 +246,79 @@ class KohaServiceGetNoticeHarryPotterTest extends KohaServiceGetNoticeHarryPotte
     $this->assertEquals(12, count($this->potter->getExemplaires()));
   }
 
+
   /** @test */
   public function firstExemplaireCodeBarreShouldBe2661690090() {
     $this->assertEquals('2661690090', $this->potter->exemplaireAt(0)->getCodeBarre());
   }
 
+
   /** @test */
   public function firstExemplaireDateRetourShouldBe30_05_2011() {
     $this->assertEquals('30/05/2011', $this->potter->exemplaireAt(0)->getDateRetour());
   }
 
+
   /** @test */
   public function firstExemplaireShouldBeReservable() {
     $this->assertTrue($this->potter->exemplaireAt(0)->isReservable());
   }
 
+
   /** @test */
   public function firstExemplaireDisponibiliteShouldBeEnPret()  {
     $this->assertEquals(Class_WebService_SIGB_Exemplaire::newInstance()->message('DISPO_EN_PRET'), $this->potter->exemplaireAt(0)->getDisponibilite());
   }
 
+
   /** @test */
   public function firstExemplaireShouldBeValid()  {
     $this->assertTrue($this->potter->exemplaireAt(0)->isValid());
   }
 
+
   /** @test */
   public function secondExemplaireCodeBarreShouldBe2661680090() {
     $this->assertEquals('2661680090', $this->potter->exemplaireAt(1)->getCodeBarre());
   }
 
+
   /** @test */
   public function secondExemplaireDateRetourShouldBe15_08_2011() {
     $this->assertEquals('15/08/2011', $this->potter->exemplaireAt(1)->getDateRetour());
   }
 
+
   /** @test */
   public function secondExemplaireShouldBeReservable()  {
     $this->assertTrue($this->potter->exemplaireAt(1)->isReservable());
-
   }
 
+
   /** @test */
   public function secondExemplaireDisponibiliteShouldBeEnPret() {
     $this->assertEquals(Class_WebService_SIGB_Exemplaire::newInstance()->message('DISPO_EN_PRET'), $this->potter->exemplaireAt(1)->getDisponibilite());
   }
 
+
   /** @test */
   public function thirdExemplaireShouldBeReservable() {
     $this->assertTrue($this->potter->exemplaireAt(2)->isReservable());
   }
 
+
   /** @test */
   public function thirdExemplaireDisponibiliteShouldBeEmpruntable() {
     $this->assertEquals(Class_WebService_SIGB_Exemplaire::newInstance()->message('DISPO_LIBRE'), $this->potter->exemplaireAt(2)->getDisponibilite());
   }
 
+
   /** @test */
   public function fourthExemplaireShouldNotBeReservable() {
     $this->assertFalse($this->potter->exemplaireAt(3)->isReservable());
   }
 
+
   /** @test */
   public function fourthExemplaireDisponibiliteShouldBePilonne()  {
     $this->assertEquals(Class_WebService_SIGB_Exemplaire::newInstance()->message('DISPO_PILONNE'), $this->potter->exemplaireAt(3)->getDisponibilite());
@@ -324,26 +336,31 @@ class KohaServiceGetNoticeHarryPotterTest extends KohaServiceGetNoticeHarryPotte
     $this->assertFalse($this->potter->exemplaireAt(4)->isReservable());
   }
 
+
   /** @test */
   public function fifthExemplaireDisponibiliteShouldBePerdu() {
     $this->assertEquals(Class_WebService_SIGB_Exemplaire::newInstance()->message('DISPO_PERDU'), $this->potter->exemplaireAt(4)->getDisponibilite());
   }
 
+
   /** @test */
   public function sixthExemplaireShouldNotBeReservable() {
     $this->assertFalse($this->potter->exemplaireAt(5)->isReservable());
   }
 
+
   /** @test */
   public function sixthExemplaireDisponibiliteShouldBeEnReparation()  {
     $this->assertEquals("En réparation", $this->potter->exemplaireAt(5)->getDisponibilite());
   }
 
+
   /** @test */
   public function seventhExemplaireDisponibiliteShouldBeEnTraitement()  {
     $this->assertEquals("En traitement", $this->potter->exemplaireAt(6)->getDisponibilite());
   }
 
+
   /** @test */
   public function seventhExemplaireShouldNotBeReservable() {
     $this->assertFalse($this->potter->exemplaireAt(6)->isReservable());
@@ -355,6 +372,7 @@ class KohaServiceGetNoticeHarryPotterTest extends KohaServiceGetNoticeHarryPotte
     $this->assertEquals('En réserve', $this->potter->exemplaireAt(7)->getDisponibilite());
   }
 
+
   /** @test */
   public function eigthExemplaireShouldNotBeReservable() {
     $this->assertFalse($this->potter->exemplaireAt(7)->isReservable());
@@ -397,6 +415,34 @@ class KohaServiceGetNoticeHarryPotterTest extends KohaServiceGetNoticeHarryPotte
   public function twelfthExemplaireShouldNotBeReservable() {
     $this->assertFalse($this->potter->exemplaireAt(11)->isReservable());
   }
+
+
+  /** @test */
+  public function twelfthExemplaireShouldNotHaveBarCode() {
+    $this->assertEquals('', $this->potter->exemplaireAt(11)->getCodeBarre());
+  }
+
+
+  /** @test */
+  public function getFirstILSItemLikeItemWithId0239427ShouldAnswerTwelthILSItem() {
+    $item = $this->fixture(Class_Exemplaire::class,
+                           ['id' => 45,
+                            'code_barres' => 'fictive bar-code for commands',
+                            'data_profile' => Class_IntProfilDonnees::forKoha(),
+                            'zone995' => serialize([['code' => '9', 'valeur' => '0239427']])]);
+    $this->assertEquals($this->potter->exemplaireAt(11)->getId(),
+                        $this->potter->getFirstILSItemLike($item)->getId());
+  }
+
+
+  /** @test */
+  public function getFirstILSItemLikeItemWithBarCode2661660439ShouldAnswerNinthILSItem() {
+    $item = $this->fixture(Class_Exemplaire::class,
+                           ['id' => 45,
+                            'code_barres' => '2661660439']);
+    $this->assertEquals($this->potter->exemplaireAt(9)->getId(),
+                        $this->potter->getFirstILSItemLike($item)->getId());
+  }
 }
 
 
@@ -2194,4 +2240,4 @@ class KohaAuthenticationNoGroupsTestCase extends KohaAuthenticationGroupsTestCas
   public function groupIndividuShouldNotBe() {
     $this->assertNull(Class_UserGroup::findFirstBy(['libelle' => 'INDIVIDU']));
   }
-}
\ No newline at end of file
+}
-- 
GitLab