From 9e41ee7170a7f5a0415eb8ff997bb5706838cb3b Mon Sep 17 00:00:00 2001
From: Laurent Laffont <llaffont@afi-sa.fr>
Date: Thu, 23 Jan 2020 17:14:56 +0100
Subject: [PATCH] dev #93357 Adds Flora ILS Webservices

---
 cosmogramme/FEATURES/93357                    |  10 +
 cosmogramme/VERSIONS_WIP/93357                |   1 +
 cosmogramme/php/_init.php                     |  15 -
 cosmogramme/php/config_integrations.php       |   6 +-
 cosmogramme/php/fonctions/objets_saisie.php   |  35 +-
 library/Class/AdminVar.php                    |   1 -
 library/Class/IntBib.php                      |  72 +++-
 library/Class/WebService/SIGB/Emprunt.php     |   2 +
 library/Class/WebService/SIGB/Flora.php       |  49 +++
 .../SIGB/Flora/AbstractResponseReader.php     |  45 ++
 .../GetBorrowerIdentityResponseReader.php     |  75 ++++
 .../SIGB/Flora/GetHoldListResponseReader.php  |  50 +++
 .../SIGB/Flora/GetLoanListResponseReader.php  |  67 +++
 .../SIGB/Flora/ItemsResponseReader.php        |  68 +++
 .../Class/WebService/SIGB/Flora/Service.php   | 178 ++++++++
 library/ZendAfi/Translate.php                 |   6 +-
 tests/fixtures/flora_borrower_identity.xml    |  41 ++
 tests/fixtures/flora_hold_list.xml            |  65 +++
 tests/fixtures/flora_items_circulation.xml    | 189 +++++++++
 tests/fixtures/flora_loan_list.xml            |  93 +++++
 tests/library/Class/CommSigbTest.php          |  37 +-
 .../Class/WebService/SIGB/FloraTest.php       | 392 ++++++++++++++++++
 22 files changed, 1456 insertions(+), 41 deletions(-)
 create mode 100644 cosmogramme/FEATURES/93357
 create mode 100644 cosmogramme/VERSIONS_WIP/93357
 create mode 100644 library/Class/WebService/SIGB/Flora.php
 create mode 100644 library/Class/WebService/SIGB/Flora/AbstractResponseReader.php
 create mode 100644 library/Class/WebService/SIGB/Flora/GetBorrowerIdentityResponseReader.php
 create mode 100644 library/Class/WebService/SIGB/Flora/GetHoldListResponseReader.php
 create mode 100644 library/Class/WebService/SIGB/Flora/GetLoanListResponseReader.php
 create mode 100644 library/Class/WebService/SIGB/Flora/ItemsResponseReader.php
 create mode 100644 library/Class/WebService/SIGB/Flora/Service.php
 create mode 100644 tests/fixtures/flora_borrower_identity.xml
 create mode 100644 tests/fixtures/flora_hold_list.xml
 create mode 100644 tests/fixtures/flora_items_circulation.xml
 create mode 100644 tests/fixtures/flora_loan_list.xml
 create mode 100644 tests/library/Class/WebService/SIGB/FloraTest.php

diff --git a/cosmogramme/FEATURES/93357 b/cosmogramme/FEATURES/93357
new file mode 100644
index 00000000000..6027e05c6d4
--- /dev/null
+++ b/cosmogramme/FEATURES/93357
@@ -0,0 +1,10 @@
+        '93357' =>
+            ['Label' => $this->_('SIGB Decalog Flora'),
+             'Desc' => $this->_('Support des web services du SIGB Decalog Flora (disponibilité temps réel, réservations, compte lecteur)'),
+             'Image' => '',
+             'Video' => '',
+             'Category' => $this->_('Communication SIGB'),
+             'Right' => function($feature_description, $user) {return true;},
+             'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=SIGB_Flora',
+             'Test' => '',
+             'Date' => '2020-01-23'],
\ No newline at end of file
diff --git a/cosmogramme/VERSIONS_WIP/93357 b/cosmogramme/VERSIONS_WIP/93357
new file mode 100644
index 00000000000..87736556235
--- /dev/null
+++ b/cosmogramme/VERSIONS_WIP/93357
@@ -0,0 +1 @@
+ - ticket #93357 : Ajout du connecteur SIGB Decalog Flora
\ No newline at end of file
diff --git a/cosmogramme/php/_init.php b/cosmogramme/php/_init.php
index b5e8b97d9ee..954976b27bc 100644
--- a/cosmogramme/php/_init.php
+++ b/cosmogramme/php/_init.php
@@ -2,23 +2,8 @@
 error_reporting(E_ERROR | E_PARSE);
 
 define("APPLI","cosmogramme");
-define("COSMOPATH", "/var/www/html/vhosts/opac2/www/htdocs");
 define("CRLF", chr(13) . chr(10));
 define("BR","<br />");
-define('COM_PERGAME', 1);
-define('COM_OPSYS', 2);
-define('COM_Z3950', 3);
-define('COM_VSMART', 4);
-define('COM_KOHA', 5);
-define('COM_CARTHAME', 6);
-define('COM_NANOOK', 7);
-define('COM_ORPHEE', 8);
-define('COM_MICROBIB', 9);
-define('COM_BIBLIXNET', 10);
-define('COM_DYNIX', 11);
-define('COM_CDSCRIPT', 12);
-define('COM_PMB', 13);
-define('COM_WATERBEAR', 14);
 
 $basePath = dirname(realpath(__FILE__));
 set_include_path(get_include_path()
diff --git a/cosmogramme/php/config_integrations.php b/cosmogramme/php/config_integrations.php
index ba9ec0f99c5..1c08f128255 100644
--- a/cosmogramme/php/config_integrations.php
+++ b/cosmogramme/php/config_integrations.php
@@ -152,14 +152,16 @@ else
 		print('<tr>');
 		print('<th class="form" align="left" colspan="2"><a name="b'.$id_bib.'p0"><b>Paramètres bibliothèque</b></a></th></tr>');
 		printLigne("Nom abrégé",getChamp("nom_court",$bib["nom"],45),"form_first");
-		printLigne("Sigb",getComboCodif("sigb","sigb",$bib["sigb"]));
+
+    printLigne("Sigb",getComboSimple("sigb", $bib["sigb"], Class_IntBib::getSigbLabels()));
+
 		printLigne("Code qualité des notices",getComboCodif("qualite","code_qualite",$bib["qualite"]));
 		printLigne("Ecart maxi entre 2 intégrations",getChamp("ecart_ajouts",$bib["ecart_ajouts"],3));
 		printLigne("Exclure des exports du catalogue",getComboSimple("pas_exporter",$bib["pas_exporter"],array("0"=>"non","1"=>"oui")));
 		printLigne("Adresse E-mail",getChamp("mail",$bib["mail"],45));
 		$blocs_params=getBlocsParams($id_bib,$bib["comm_sigb"],$bib["comm_params"]);
 		$event="activerBlocCommBib('".$id_bib."',this.value)";
-		printLigne("Mode de communication avec le sigb",getComboCodif("comm_sigb","comm_sigb",$bib["comm_sigb"],'onchange="'.$event.'"').$blocs_params);
+		printLigne("Mode de communication avec le sigb",getComboSimple("comm_sigb",$bib["comm_sigb"], Class_IntBib::getCommLabels(),false, 'onchange="'.$event.'"').$blocs_params);
 		print('<tr><th colspan="2" class="form" align="center">'. $bouton_modif .'</th></tr>');
 		print('</table></form></div>');
 
diff --git a/cosmogramme/php/fonctions/objets_saisie.php b/cosmogramme/php/fonctions/objets_saisie.php
index ec0985d0990..ee1b2fd550e 100644
--- a/cosmogramme/php/fonctions/objets_saisie.php
+++ b/cosmogramme/php/fonctions/objets_saisie.php
@@ -80,10 +80,10 @@ function getTextarea($id, $valeur, $largeur, $hauteur) {
  */
 function getBlocsParams($id_bib, $type, $valeurs) {
   $valeurs = unserialize($valeurs);
-  $types = getCodifsVariable("comm_sigb");
+  $types = array_keys(Class_IntBib::getCommLabels());
+
+  foreach($types as $clef) {
 
-  foreach($types as $item) {
-    $clef = $item['code'];
     $bloc .= sprintf('<div id="comm_%s_%s" style="display:%s">',
                      $id_bib, $clef, ($clef == $type) ? 'block' : 'none');
 
@@ -91,7 +91,7 @@ function getBlocsParams($id_bib, $type, $valeurs) {
     $champs_params = [];
     $titres[0] = 'Paramètres';
 
-    if(in_array($clef, [COM_NANOOK]))
+    if(in_array($clef, [Class_IntBib::COM_NANOOK]))
       $champs_params[0] = ['url_serveur',
                            ['provide_suggest' => function($id, $valeur) {
                                return getOuiNon($id, $valeur);
@@ -100,23 +100,23 @@ function getBlocsParams($id_bib, $type, $valeurs) {
                                return getOuiNon($id, $valeur);
                              }]];
 
-    if(in_array($clef, [COM_CDSCRIPT]))
+    if(in_array($clef, [Class_IntBib::COM_CDSCRIPT]))
       $champs_params[0] = ['server_url',
                            'remote_library_id'];
 
 
-    if (in_array($clef, [COM_PMB, COM_VSMART, COM_MICROBIB, COM_BIBLIXNET, COM_WATERBEAR]))
+    if (in_array($clef, [Class_IntBib::COM_PMB, Class_IntBib::COM_VSMART, Class_IntBib::COM_MICROBIB, Class_IntBib::COM_BIBLIXNET, Class_IntBib::COM_WATERBEAR]))
       $champs_params[0] = ['url_serveur'];
 
-    if (COM_PMB == $clef)
+    if (Class_IntBib::COM_PMB == $clef)
       $champs_params[0][] = ['exp_bulletin_is_exp_notice' => function($id, $valeur) {
           return getOuiNon($id, $valeur);
         }];
 
-    if (in_array($clef, [COM_CARTHAME]))
+    if (in_array($clef, [Class_IntBib::COM_CARTHAME]))
       $champs_params[0] = ['url_serveur', 'key','sigb_field_name'];
 
-    if (in_array($clef, [COM_ORPHEE]))
+    if (in_array($clef, [Class_IntBib::COM_ORPHEE]))
       $champs_params[0] = ['url_serveur',
                            'key',
                            'allow_hold_available_items',
@@ -127,7 +127,7 @@ function getBlocsParams($id_bib, $type, $valeurs) {
                                                       '1' => 'A l\'exemplaire']);
                              }]];
 
-    if ($clef == COM_KOHA)
+    if ($clef == Class_IntBib::COM_KOHA)
       $champs_params[0] = ['url_serveur',
                            ['restful' => function($id, $valeur) {
                                return getOuiNon($id, $valeur);
@@ -150,16 +150,19 @@ function getBlocsParams($id_bib, $type, $valeurs) {
 															 return getTextArea($id, $valeur, 30, 5);
 														 }]];
 
-    if ($clef == COM_OPSYS)
+    if ($clef == Class_IntBib::COM_OPSYS)
       $champs_params[0] = ['url_serveur', 'catalogue_web', 'reserver_retrait_bib_abonne'];
 
-    if ($clef == COM_DYNIX)
+    if ($clef == Class_IntBib::COM_DYNIX)
       $champs_params[0] = ['url_serveur', 'client_id'];
 
-    if ($clef == COM_Z3950)
+    if ($clef == Class_IntBib::COM_Z3950)
       $champs_params[0] = ['url_serveur', 'login', 'password', 'nom_base'];
 
-    if ($clef == COM_PERGAME) {
+    if ($clef == Class_IntBib::COM_FLORA)
+      $champs_params[0] = ['url_serveur', 'login', 'password'];
+
+    if ($clef == Class_IntBib::COM_PERGAME) {
       $titres = ['Règles de réservations', 'Règles de prolongations'];
       $champs_params[0] = ['Autoriser_docs_disponibles', 'Max_par_carte' , 'Max_par_document'];
       $champs_params[1] = ['Autoriser_prolongations', 'Interdire_si_reservation',
@@ -193,13 +196,13 @@ function getBlocsParams($id_bib, $type, $valeurs) {
       }
       $bloc .= '</table>';
 
-      if ($clef == COM_NANOOK) {
+      if ($clef == Class_IntBib::COM_NANOOK) {
         $bloc .= "Format(0.8.7): ip:port/chemin_tomcat/ilsdi/nom_base <br/>"
           . "Ex: 62.193.55.152:8080/afi_NanookWs/ilsdi/NANOOK";
       }
     }
 
-    if (in_array($clef, [COM_NANOOK, COM_KOHA, COM_PMB]))
+    if (in_array($clef, [Class_IntBib::COM_NANOOK, Class_IntBib::COM_KOHA, Class_IntBib::COM_PMB]))
       $bloc .= " <br/><a target='_blank' class='test' href='#'>Tester la communication</a>";
     $bloc .= '</div>';
   }
diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php
index 743af105fe9..a1b7126fe55 100644
--- a/library/Class/AdminVar.php
+++ b/library/Class/AdminVar.php
@@ -25,7 +25,6 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
 
   protected static $_all_vars_values, $_all_vars;
 
-
   protected $_inited = false;
 
   public function init() {
diff --git a/library/Class/IntBib.php b/library/Class/IntBib.php
index 771ef9c772e..c06c1582fcf 100644
--- a/library/Class/IntBib.php
+++ b/library/Class/IntBib.php
@@ -20,6 +20,54 @@
  */
 
 class IntBibLoader extends Storm_Model_Loader {
+  use Trait_Translator;
+  public function getSigbLabels() {
+    return [Class_IntBib::SIGB_NONE => $this->_('Pas informatisé'),
+            Class_IntBib::SIGB_PERGAME => 'Pergame',
+            Class_IntBib::SIGB_PAPRIKA => 'Paprika',
+            Class_IntBib::SIGB_ORPHEE => 'Orphee',
+            Class_IntBib::SIGB_OPSYS => 'Opsys/Aloès',
+            Class_IntBib::SIGB_MICROBIB => 'Microbib',
+            Class_IntBib::SIGB_ATALANTE => 'Atalante',
+            Class_IntBib::SIGB_MULTILIS => 'Multilis',
+            Class_IntBib::SIGB_BIBAL => 'Bibal',
+            Class_IntBib::SIGB_MILORD => 'Milord',
+            Class_IntBib::SIGB_ELISSA => 'Elissa',
+            Class_IntBib::SIGB_VSMART => 'VSmart',
+            Class_IntBib::SIGB_KOHA => 'Koha',
+            Class_IntBib::SIGB_NANOOK => 'Nanook',
+            Class_IntBib::SIGB_CARTHAME => 'Carthame/Karvi',
+            Class_IntBib::SIGB_DYNIX => 'Dynix',
+            Class_IntBib::SIGB_BIBLIXNET => 'BiblixNet',
+            Class_IntBib::SIGB_CDSCRIPT => 'CdScript',
+            Class_IntBib::SIGB_PMB => 'PMB',
+            Class_IntBib::SIGB_WATERBEAR => 'Waterbear',
+            Class_IntBib::SIGB_FLORA => 'Flora',
+    ];
+  }
+
+
+  public function getCommLabels() {
+    return [Class_IntBib::COM_NONE => $this->_('Aucun'),
+            Class_IntBib::COM_PERGAME => 'Pergame',
+            Class_IntBib::COM_OPSYS => 'Opsys',
+            Class_IntBib::COM_Z3950 => $this->_('serveur Z39.50'),
+            Class_IntBib::COM_VSMART => 'VSmart',
+            Class_IntBib::COM_KOHA => 'Koha',
+            Class_IntBib::COM_CARTHAME => 'Carthame',
+            Class_IntBib::COM_NANOOK => 'AFI-Nanook',
+            Class_IntBib::COM_ORPHEE => 'Orphée',
+            Class_IntBib::COM_MICROBIB => 'Microbib',
+            Class_IntBib::COM_BIBLIXNET => 'BiblixNet',
+            Class_IntBib::COM_DYNIX => 'Dynix-Symphony',
+            Class_IntBib::COM_CDSCRIPT => 'Cd-Script',
+            Class_IntBib::COM_PMB => 'PMB',
+            Class_IntBib::COM_WATERBEAR => 'Waterbear',
+            Class_IntBib::COM_FLORA => 'Flora',
+    ];
+
+  }
+
   public function findAllForCombo() {
     $combo = [];
     foreach(Class_IntBib::findAll() as $library)
@@ -98,6 +146,7 @@ class Class_IntBib extends Storm_Model_Abstract {
   const COM_NONE = 0;
   const COM_PERGAME = 1;
   const COM_OPSYS = 2;
+  const COM_Z3950 = 3;
   const COM_VSMART = 4;
   const COM_KOHA = 5;
   const COM_CARTHAME = 6;
@@ -109,12 +158,29 @@ class Class_IntBib extends Storm_Model_Abstract {
   const COM_CDSCRIPT = 12;
   const COM_PMB = 13;
   const COM_WATERBEAR = 14;
+  const COM_FLORA = 15;
 
   const SIGB_NONE = 0;
   const SIGB_PERGAME = 1;
+  const SIGB_PAPRIKA = 2;
+  const SIGB_ORPHEE = 3;
+  const SIGB_OPSYS = 4;
+  const SIGB_MICROBIB = 5;
+  const SIGB_ATALANTE = 6;
+  const SIGB_MULTILIS = 7;
+  const SIGB_BIBAL = 8;
+  const SIGB_MILORD = 9;
+  const SIGB_ELISSA = 10;
+  const SIGB_VSMART = 11;
   const SIGB_KOHA = 12;
   const SIGB_NANOOK = 13;
-  const SIGB_ORPHEE = 3;
+  const SIGB_CARTHAME = 14;
+  const SIGB_DYNIX = 15;
+  const SIGB_CDSCRIPT = 16;
+  const SIGB_PMB = 17;
+  const SIGB_WATERBEAR = 18;
+  const SIGB_BIBLIXNET = 19;
+  const SIGB_FLORA = 20;
 
   const TYPE = 'type';
   const URL_SERVER = 'url_server';
@@ -131,7 +197,8 @@ class Class_IntBib extends Storm_Model_Abstract {
                                    self::COM_DYNIX => 'Class_WebService_SIGB_Dynix',
                                    self::COM_CDSCRIPT => 'Class_WebService_SIGB_CdScript',
                                    self::COM_PMB => 'Class_WebService_SIGB_PMB',
-                                   self::COM_WATERBEAR => 'Class_WebService_SIGB_Waterbear'];
+                                   self::COM_WATERBEAR => 'Class_WebService_SIGB_Waterbear',
+                                   self::COM_FLORA => 'Class_WebService_SIGB_Flora'];
 
   protected $_table_name = 'int_bib';
   protected $_table_primary = 'id_bib';
@@ -163,7 +230,6 @@ class Class_IntBib extends Storm_Model_Abstract {
     return array_keys(self::$COM_CLASSES);
   }
 
-
   public static function detectService($id_type) {
     if (isset(self::$COM_CLASSES[$id_type]))
       return self::$COM_CLASSES[$id_type];
diff --git a/library/Class/WebService/SIGB/Emprunt.php b/library/Class/WebService/SIGB/Emprunt.php
index fe8ac12be57..5caf0fe3929 100644
--- a/library/Class/WebService/SIGB/Emprunt.php
+++ b/library/Class/WebService/SIGB/Emprunt.php
@@ -147,11 +147,13 @@ class Class_WebService_SIGB_Emprunt extends Class_WebService_SIGB_ExemplaireOper
   }
 
 
+  /** seulement Aloes CASQY ? à supprimer ? */
   public function getType() {
     return $this->type;
   }
 
 
+  /** seulement Aloes CASQY ? à supprimer ? */
   public function setType($type) {
     $this->type = $type;
     return $this;
diff --git a/library/Class/WebService/SIGB/Flora.php b/library/Class/WebService/SIGB/Flora.php
new file mode 100644
index 00000000000..ff855669ee9
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora {
+  protected static $_service;
+
+  public static function getService($params) {
+    if (!isset(static::$_service)) {
+      $instance = new static();
+
+      $url_serveur = str_replace('//XAPI', '/XAPI', $params['url_serveur'] . '/XAPI');
+
+      static::$_service =
+        Class_WebService_SIGB_Flora_Service::getService($url_serveur)
+        ->setAPICredentials($params['login'], $params['password']);
+
+    }
+    return static::$_service;
+  }
+
+  public static function setService($service) {
+    static::$_service = $service;
+  }
+
+
+  public static function reset() {
+    static::$_service = null;
+  }
+
+}
diff --git a/library/Class/WebService/SIGB/Flora/AbstractResponseReader.php b/library/Class/WebService/SIGB/Flora/AbstractResponseReader.php
new file mode 100644
index 00000000000..6082d6e3021
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/AbstractResponseReader.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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
+ */
+
+
+abstract class Class_WebService_SIGB_Flora_AbstractResponseReader {
+  protected
+    $_xml_parser;
+
+
+  public function parse($xml) {
+    $this->_xml_parser = Class_WebService_XMLParser::newInstance()
+      ->setElementHandler($this)
+      ->parse($xml);
+  }
+
+
+
+  public function dateToFR($content) {
+    if (empty($content))
+      return;
+
+    $year = substr($content, 0, 4);
+    $month = substr($content, 4, 2);
+    $day = substr($content, 6, 2);
+    return $day . '/' . $month . '/' . $year;
+  }
+}
diff --git a/library/Class/WebService/SIGB/Flora/GetBorrowerIdentityResponseReader.php b/library/Class/WebService/SIGB/Flora/GetBorrowerIdentityResponseReader.php
new file mode 100644
index 00000000000..08e6ab7d8bb
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/GetBorrowerIdentityResponseReader.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora_GetBorrowerIdentityResponseReader extends Class_WebService_SIGB_Flora_AbstractResponseReader {
+  protected
+    $_emprunteur;
+
+
+  public function emprunteurFromXML($xml) {
+    $this->_emprunteur = Class_WebService_SIGB_Emprunteur::newInstance();
+    $this->parse($xml);
+    return $this->_emprunteur;
+  }
+
+
+  public function endNOM($content) {
+    $this->_emprunteur->setName($content);
+  }
+
+
+  public function endPRENOM($content) {
+    $this->_emprunteur->setPrenom($content);
+  }
+
+
+  protected function _dateToISO($date) {
+    return substr($date,0,4).'-'.substr($date,4, 2).'-'.substr($date,6,2);
+  }
+
+
+  public function endDATE_NAISSANCE($content) {
+    $this->_emprunteur->setDateNaissance($this->_dateToISO($content));
+  }
+
+
+  public function endDATE_RENOUVELLEMENT($content) {
+    $this->_emprunteur->setEndDate($this->_dateToISO($content));
+  }
+
+
+  public function endCODE_POSTAL($content) {
+    $this->_emprunteur->setCodePostal($content);
+  }
+
+
+  public function endVILLE($content) {
+    $this->_emprunteur->setVille($content);
+  }
+
+
+  public function endEMAIL($content) {
+    $this->_emprunteur->setEmail($content);
+  }
+
+
+}
diff --git a/library/Class/WebService/SIGB/Flora/GetHoldListResponseReader.php b/library/Class/WebService/SIGB/Flora/GetHoldListResponseReader.php
new file mode 100644
index 00000000000..0da9aea718d
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/GetHoldListResponseReader.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora_GetHoldListResponseReader extends Class_WebService_SIGB_Flora_AbstractResponseReader {
+  protected
+    $_holds = [],
+    $_current_hold;
+
+  public function reservationsFromXML($xml) {
+    $this->parse($xml);
+    return $this->_holds;
+  }
+
+
+  public function startIcomm_hold() {
+    $this->_holds []= $this->_current_hold = Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire();
+  }
+
+
+  public function startCatalog_system_id($attributes) {
+    $datas = explode(' - ', $attributes['DISPLAY']);
+    $this->_current_hold
+      ->setTitre($datas[0])
+      ->setAuteur($datas[1]);
+  }
+
+
+  public function endCatalog_system_id($content) {
+    $this->_current_hold->setNoNotice($content);
+  }
+}
\ No newline at end of file
diff --git a/library/Class/WebService/SIGB/Flora/GetLoanListResponseReader.php b/library/Class/WebService/SIGB/Flora/GetLoanListResponseReader.php
new file mode 100644
index 00000000000..b615327eb3b
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/GetLoanListResponseReader.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora_GetLoanListResponseReader extends Class_WebService_SIGB_Flora_AbstractResponseReader {
+  protected
+    $_loans = [],
+    $_current_loan;
+
+  public function empruntsFromXML($xml) {
+    $this->parse($xml);
+    return $this->_loans;
+  }
+
+
+  public function startICOMM_LOAN() {
+    $this->_loans []= $this->_current_loan = Class_WebService_SIGB_Emprunt::newInstanceWithEmptyExemplaire();
+    $this->_current_loan->setRenewable(false);
+  }
+
+
+  public function endDue_Date($content) {
+    $this->_current_loan->setDateRetour($this->dateToFR($content));
+  }
+
+
+  public function endITEM_BARCODE($content) {
+    $this->_current_loan->setCodeBarre($content);
+  }
+
+
+  public function startLOAN_STATUS($attributes) {
+    $this->_current_loan->setType($attributes['DISPLAY']);
+  }
+
+
+  public function startITEM_SYSTEM_ID($attributes) {
+    $datas = explode(' - ', $attributes['DISPLAY']);
+    $author_parts = explode(' , ',$datas[2]);
+    $this->_current_loan
+      ->setTitre($datas[1])
+      ->setAuteur(trim(end($author_parts)));
+  }
+
+
+  public function startItem_Site($attributes) {
+    $this->_current_loan->setBibliotheque($attributes['DISPLAY']);
+  }
+}
\ No newline at end of file
diff --git a/library/Class/WebService/SIGB/Flora/ItemsResponseReader.php b/library/Class/WebService/SIGB/Flora/ItemsResponseReader.php
new file mode 100644
index 00000000000..509161a3f83
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/ItemsResponseReader.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora_ItemsResponseReader extends Class_WebService_SIGB_Flora_AbstractResponseReader {
+  const
+    STATUS_LOANED = 2;
+
+  protected
+    $_notice,
+    $_current_item;
+
+
+  public function recordFromXML($xml) {
+    $this->_notice = new Class_WebService_SIGB_Notice(0);
+    $this->parse($xml);
+    return $this->_notice;
+  }
+
+
+  public function startICOMM_ITEM() {
+    $this->_current_item = Class_WebService_SIGB_Exemplaire::newInstance()->beReservable();
+    $this->_notice->addExemplaire($this->_current_item);
+  }
+
+
+  public function endUNIQUE_KEY($content) {
+    $this->_current_item->setId($content);
+  }
+
+
+  public function endCODE($content) {
+    $this->_current_item->setCodeBarre($content);
+  }
+
+
+  public function startStatus($attributes) {
+    $this->_current_item->setDisponibilite($attributes['DISPLAY']);
+  }
+
+
+  public function endStatus($content) {
+    $this->_current_item->setReservable(static::STATUS_LOANED == (int)trim($content));
+  }
+
+
+  public function endDue_Date($content) {
+    $this->_current_item->setDateRetour($this->dateToFR($content));
+  }
+}
diff --git a/library/Class/WebService/SIGB/Flora/Service.php b/library/Class/WebService/SIGB/Flora/Service.php
new file mode 100644
index 00000000000..87a50817c10
--- /dev/null
+++ b/library/Class/WebService/SIGB/Flora/Service.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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_Flora_Service extends Class_WebService_SIGB_AbstractRESTService {
+  protected
+    $_api_login,
+    $_api_password,
+    $_token,
+    $_borrower_id;
+
+
+  public function getEmprunteur($user) {
+    $xml = $this->_httpGetBorrowerMethod('communication.getBorrowerIdentity',
+                                         $user->getIdabon());
+    return (new Class_WebService_SIGB_Flora_GetBorrowerIdentityResponseReader())
+      ->emprunteurFromXML($xml)
+      ->beValid()
+      ->setService($this);
+  }
+
+
+  public function __destruct(){
+    $this->disconnect();
+  }
+
+
+  public function httpGet($options) {
+    if (!$this->_token) {
+      $xml = parent::httpGet(['method' => 'login',
+                              'code' => $this->_api_login,
+                              'password' => $this->_api_password]);
+      $this->_token = $this->_getTagData($xml, 'apiSession');
+    }
+    return parent::httpGet(array_merge(['apiSession' => $this->_token],
+                                       $options));
+  }
+
+
+  protected function _httpGetBorrowerMethod($method, $barcode, $options = []) {
+    if (!$this->_borrower_id) {
+      $xml = $this->httpGet(['method' => 'communication.identifyBorrower',
+                             'borrowerCode' => $barcode]);
+
+      $this->_borrower_id = $this->_getTagData($xml, 'borrowerId');
+    }
+
+
+    return $this->httpGet(array_merge(['method' => $method,
+                                       'borrowerId' => $this->_borrower_id],
+                                      $options));
+  }
+
+
+
+  public function disconnect() {
+    if ($this->_token)
+      $this->httpGet(['method' => 'logout']);
+  }
+
+
+  public function setAPICredentials($login, $password) {
+    $this->_api_login = $login;
+    $this->_api_password = $password;
+    return $this;
+  }
+
+
+  public function getEmpruntsOf($emprunteur) {
+    $xml = $this->_httpGetBorrowerMethod('communication.getLoanList',
+                                         $emprunteur->getCodeBarres());
+    return (new Class_WebService_SIGB_Flora_GetLoanListResponseReader())->empruntsFromXML($xml);
+  }
+
+
+  public function getReservationsOf($emprunteur) {
+    $xml = $this->_httpGetBorrowerMethod('communication.getAskHoldList',
+                                         $emprunteur->getCodeBarres());
+    return (new Class_WebService_SIGB_Flora_GetHoldListResponseReader())->reservationsFromXML($xml);
+  }
+
+
+  public function getUserAnnexe($user) {
+  }
+
+
+  public function reserverExemplaire($user, $exemplaire, $code_annexe) {
+    $xml = $this->_httpGetBorrowerMethod('communication.hold',
+                                         $user->getIdabon(),
+                                         ['catalogId' => $exemplaire->getIdOrigine(),
+                                          'siteId' => 1,
+                                          'categoryId' => 1,
+                                          'itemNewId' => 2]);
+
+    if ($error = $this->_getTagData($xml, 'message'))
+      return ['statut' => false, 'erreur' => $error];
+
+    return ['statut' => true, 'erreur' => ''];
+  }
+
+
+  public function supprimerReservation($user, $reservation_id) {
+  }
+
+  public function prolongerPret($user, $pret_id) {
+  }
+
+  public function getNotice($id) {
+    $xml = $this->httpGet(['method' => 'communication.getItems',
+                           'catalogId' => $id]);
+    return (new Class_WebService_SIGB_Flora_ItemsResponseReader())->recordFromXML($xml);
+  }
+
+  public function providesSuggestions() {
+    return true;
+  }
+
+  public function suggestionsOf($user) {
+    return [];
+  }
+
+  public function newBuySuggestForm() {
+    return new ZendAfi_Form_SuggestionAchat();
+  }
+
+
+  public function suggest($suggestion) {
+    $doc_type = Class_TypeDoc::find($suggestion->getDocType());
+    $doctype_label = $doc_type
+      ? ' [' . $doc_type->getLibelle() . ']'
+      : '';
+
+    $xmldata = '<?xml version="1.0" encoding="UTF-8" ?> <IPURCH_REQUEST>';
+    $xmldata .= $doc_type ? '<TYPE_DOC>'.$doc_type.'</TYPE_DOC>' : '';
+    $xmldata .= $doc_type ? '<TYPE>'.$doc_type.'</TYPE>' : '';
+    $xmldata .= $suggestion->getTitle() ? '<TITLE>'.$suggestion->getTitle().'</TITLE>' : '';
+    $xmldata .= '<XML_FIELD>';
+    $xmldata .= $suggestion->getNote() ? '<BORROWER_COMMENTS>'.$suggestion->getNote().'</BORROWER_COMMENTS>' : '';
+    $xmldata .= '</XML_FIELD>';
+    $xmldata .= '</IPURCH_REQUEST>';
+
+    $xml = $this->httpGet(['apiSession' => $this->_token,
+                           'method' => 'communication.addPurchaseRequest',
+                           'borrowerId' => $suggestion->getUser()->getIdSigb(),
+                           'type' => $doc_type->getLibelle(),
+                           'xmlData' => $xmldata]);
+
+    return $this->_error($this->_('Suggestions non prises en charge par ce connecteur'));
+  }
+
+
+  public function loansHistory($borrower) {
+    return new Class_WebService_SIGB_LoansHistory($borrower);
+  }
+
+
+  public function providesLoansHistory() {
+    return true;
+  }
+}
diff --git a/library/ZendAfi/Translate.php b/library/ZendAfi/Translate.php
index 01c8924dacd..52257165fe8 100644
--- a/library/ZendAfi/Translate.php
+++ b/library/ZendAfi/Translate.php
@@ -26,9 +26,9 @@ class ZendAfi_Translate extends Zend_Translate {
 
     $args = func_get_args();
     if ($num <= 1)
-      return parent::_(Class_TextReplacements::replace($args[0]));
+      return $this->_adapter->translate(Class_TextReplacements::replace($args[0]));
 
-    $args[0] = parent::_(Class_TextReplacements::replace($args[0]));
+    $args[0] = $this->_adapter->translate(Class_TextReplacements::replace($args[0]));
     return call_user_func_array('sprintf', $args);
   }
 
@@ -51,7 +51,7 @@ class ZendAfi_Translate extends Zend_Translate {
     $sentence = $args[$sentence_nb];
     if (!$sentence)
       return '';
-    $translation = $this->translate(Class_TextReplacements::replace($sentence));
+    $translation = $this->_adapter->translate(Class_TextReplacements::replace($sentence));
 
     $_args = array_slice($args, 4);
     array_unshift($_args, $sentence);
diff --git a/tests/fixtures/flora_borrower_identity.xml b/tests/fixtures/flora_borrower_identity.xml
new file mode 100644
index 00000000000..256cf4e855b
--- /dev/null
+++ b/tests/fixtures/flora_borrower_identity.xml
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<response>
+  <UTILISATEUR display="" label="Utilisateur">
+    <BIBLIOTHEQUE display="Annecy" label="Site">1</BIBLIOTHEQUE>
+    <CATEGORIE display="Prêt au personnel" label="Catégorie de lecteur">3</CATEGORIE>
+    <CREDIT display="" label="Crédit"/>
+    <DATE_INSCRIPTION display="23/01/2020" label="Date dernière (ré)inscription">20200123</DATE_INSCRIPTION>
+    <DATE_RENOUVELLEMENT display="22/01/2021" label="Date prévue réinscription">20210122</DATE_RENOUVELLEMENT>
+    <DATE_SUSPENSION display="06/12/2017" label="Date fin de suspension">20171206</DATE_SUSPENSION>
+    <DEBIT display="" label="Débit"/>
+    <LANGUE display="Français" label="Langue">fr</LANGUE>
+    <NOM display="GLUHON" label="Nom">GLUHON</NOM>
+    <NUMERO_CARTE display="carte123" label="N° carte ou code-barres">carte123</NUMERO_CARTE>
+    <OVERDUE_COUNT display="" label="Nb. total relances"/>
+    <OVERDUE_LAST_DATE display="" label="Dernière relance le"/>
+    <ROLES display="" label="Rôles"/>
+    <SOLDE display="" label="Solde"/>
+    <SUSPENSION_START_DATE display="20/11/2017" label="Date de début de suspension">20171120</SUSPENSION_START_DATE>
+    <TYPE display="Lecteur" label="Type">2</TYPE>
+    <BP display="" label="Boîte postale"/>
+    <CODE_POSTAL display="74000" label="Code postal">74000</CODE_POSTAL>
+    <DATE_NAISSANCE display="24/05/1977" label="Date de naissance">19770524</DATE_NAISSANCE>
+    <SERVICE display="" label="Service"/>
+    <DISCIPLINE display="" label="Discipline"/>
+    <EMAIL display="paul.gluhon@mail.fr" label="Email">paul.gluhon@mail.fr</EMAIL>
+    <FONCTION display="" label="Fonction"/>
+    <MESSAGE display="" label="Message"/>
+    <NATIONALITE display="" label="Nationalité"/>
+    <NOM_MARITAL display="" label="Nom marital"/>
+    <PAYS display="France" label="Pays">FR</PAYS>
+    <PRENOM display="PAUL" label="Prénom">PAUL</PRENOM>
+    <QUALIFICATIF display="Monsieur" label="Qualificatif">3</QUALIFICATIF>
+    <RESPONSIBLE_ID display="" label="Responsable"/>
+    <RUE display="4 rue du Pré" label="Rue">4 rue du Pré</RUE>
+    <STYLESHEET display="flora2" label="Feuille de style">flora2</STYLESHEET>
+    <TEL_PERSONNEL display=""
+                   label="Téléphone personnel"/>
+    <TEL_PROF display="" label="Téléphone professionnel"/>
+    <VILLE display="ANNECY" label="Ville">ANNECY</VILLE>
+  </UTILISATEUR>
+</response>
diff --git a/tests/fixtures/flora_hold_list.xml b/tests/fixtures/flora_hold_list.xml
new file mode 100644
index 00000000000..df8aefe35b3
--- /dev/null
+++ b/tests/fixtures/flora_hold_list.xml
@@ -0,0 +1,65 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<response>
+  <icomm_holds>
+    <icomm_hold display="" label="réservation">
+      <hold_status display="en cours" label="etat">10</hold_status>
+      <hold_priority display="0" label="priorité">0</hold_priority>
+      <hold_ask_type display="demande de réservation sur le catalogue" label="type">10</hold_ask_type>
+      <hold_ask_start_date display="23/06/2010" label="date déb.">20100623</hold_ask_start_date>
+      <hold_ask_start_hour display="1548" label="heure déb.">1548</hold_ask_start_hour>
+      <hold_ask_start_iso_long display="23/06/2010 15:48" label="date début de demande de réservation">20100623154836</hold_ask_start_iso_long>
+      <hold_ask_end_date display="25/06/2010" label="date fin">20100625</hold_ask_end_date>
+      <hold_ask_end_hour display="1548" label="heure fin">1548</hold_ask_end_hour>
+      <hold_ask_end_iso_long display="25/06/2010 15:48" label="date fin de demande de réservation">20100625154836</hold_ask_end_iso_long>
+      <hold_aside_type display="standard" label="mise à disposition">0</hold_aside_type>
+      <hold_aside_start_date display="" label="date déb."/>
+      <hold_aside_start_hour display="" label="heure déb."/>
+      <hold_aside_start_iso_long display="" label="date déb."/>
+      <hold_aside_end_date display="" label="date fin"/>
+      <hold_aside_end_hour display="" label="heure fin"/>
+      <hold_aside_end_iso_long display="" label="date fin"/>
+      <hold_cancel_date display="" label="date annulation"/>
+      <borrower_system_id display="gluhon - paul - sau - carte123 - public adulte - 29/04/2010 - 14/06/2011 - 0.00" label="lecteur">UTILISATEUR9869</borrower_system_id>
+      <borrower_barcode display="carte123" label="cdb lect.">carte123</borrower_barcode>
+      <borrower_category display="public adulte" label="cat.lect.">1</borrower_category>
+      <catalog_system_id display="La circulation invisible des r - davezies , laurent - 10 j - 978-2-02-092558-7" label="objet">default:UNIMARC:90</catalog_system_id>
+      <item_system_id display="" label="exemplaire"/>
+      <item_barcode display="" label="réf."/>
+      <item_comm_type display="prêt" label="type comm.">1</item_comm_type>
+      <item_site display="site 1" label="site">1</item_site>
+      <item_comm_category display="papier" label="cat. comm.">1</item_comm_category>
+      <item_is_new display="oui" label="nouveauté">1</item_is_new>
+      <item_call_number display="" label="cote"/>
+    </icomm_hold>
+    <icomm_hold display="" label="réservation">
+      <hold_status display="en cours" label="etat">10</hold_status>
+      <hold_priority display="0" label="priorité">0</hold_priority>
+      <hold_ask_type display="demande de réservation sur le catalogue" label="type">10</hold_ask_type>
+      <hold_ask_start_date display="23/06/2010" label="date déb.">20100623</hold_ask_start_date>
+      <hold_ask_start_hour display="1548" label="heure déb.">1548</hold_ask_start_hour>
+      <hold_ask_start_iso_long display="23/06/2010 15:48" label="date début de demande de réservation">20100623154836</hold_ask_start_iso_long>
+      <hold_ask_end_date display="25/06/2010" label="date fin">20100625</hold_ask_end_date>
+      <hold_ask_end_hour display="1548" label="heure fin">1548</hold_ask_end_hour>
+      <hold_ask_end_iso_long display="25/06/2010 15:48" label="date fin de demande de réservation">20100625154836</hold_ask_end_iso_long>
+      <hold_aside_type display="standard" label="mise à disposition">0</hold_aside_type>
+      <hold_aside_start_date display="" label="date déb."/>
+      <hold_aside_start_hour display="" label="heure déb."/>
+      <hold_aside_start_iso_long display="" label="date déb."/>
+      <hold_aside_end_date display="" label="date fin"/>
+      <hold_aside_end_hour display="" label="heure fin"/>
+      <hold_aside_end_iso_long display="" label="date fin"/>
+      <hold_cancel_date display="" label="date annulation"/>
+      <borrower_system_id display="gluhon - paul - sau - carte123 - public adulte - 29/04/2010 - 14/06/2011 - 0.00" label="lecteur">UTILISATEUR9869</borrower_system_id>
+      <borrower_barcode display="carte123" label="cdb lect.">carte123</borrower_barcode>
+      <borrower_category display="public adulte" label="cat.lect.">1</borrower_category>
+      <catalog_system_id display="Des petites choses - arthur , laurent - 10 j - 978-2-02-092559-7" label="objet">default:UNIMARC:777</catalog_system_id>
+      <item_system_id display="" label="exemplaire"/>
+      <item_barcode display="" label="réf."/>
+      <item_comm_type display="prêt" label="type comm.">1</item_comm_type>
+      <item_site display="site 1" label="site">1</item_site>
+      <item_comm_category display="papier" label="cat. comm.">1</item_comm_category>
+      <item_is_new display="oui" label="nouveauté">1</item_is_new>
+      <item_call_number display="" label="cote"/>
+    </icomm_hold>
+  </icomm_holds>
+</response>
diff --git a/tests/fixtures/flora_items_circulation.xml b/tests/fixtures/flora_items_circulation.xml
new file mode 100644
index 00000000000..35ea0763263
--- /dev/null
+++ b/tests/fixtures/flora_items_circulation.xml
@@ -0,0 +1,189 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<response>
+   <ICOMM_ITEMS>
+     <ICOMM_ITEM display="" label="Objet de prêt">
+       <UNIQUE_KEY display="11325" label="UNIQUE_KEY">11325
+       </UNIQUE_KEY>
+       <SITE display="Médiathèque - INSEP" label="Site">1
+       </SITE>
+       <CODE display="9991207408" label="Code-barres">9991207408
+       </CODE>
+       <CATEGORY display="Monographies" label="Catégorie de prêt">1
+       </CATEGORY>
+       <IS_NEW display="Non" label="Nouveauté">0
+       </IS_NEW>
+       <IS_NEW_END_DATE display="" label="Date fin nouveauté"/>
+       <IS_SUITE display="Non" label="Représente un ensemble">0
+       </IS_SUITE>
+       <COLLECTION display="Salle de lecture Rez-de-chaussée" label="Localisation">1
+       </COLLECTION>
+       <SECTION display="" label="Section"/>
+       <CALL_NUMBER display="EPC1 MAC" label="Cote">EPC1 MAC
+       </CALL_NUMBER>
+       <SITE_ISSUED_FROM display="" label="Bib. d'origine"/>
+       <TRANSFER_DATE display="" label="Dernier transfert le"/>
+       <RECOVER_DATE display="" label="Dernier rapatriement le"/>
+       <RESTRICTION_CATEGORIES display="" label="Restriction catégories" separ="_RS_"/>
+       <RESTRICTION_LOAN_PERIOD display="" label="Durée de prêt de restriction (en min)"/>
+       <RESTRICTION_END_DATE display="" label="Date fin restriction"/>
+       <MESSAGES display="" label="Messages"/>
+       <LOAN_DATE display="" label="Date prêt"/>
+       <LOAN_HOUR display="" label="Heure prêt"/>
+       <DUE_DATE display="" label="Date retour prévue"/>
+       <DUE_HOUR display="" label="Heure retour prévue"/>
+       <CATALOG_ID display="Théorie et pratique de l'évaluation dans la pédagogie des activités physiques et sportives - Maccario, Bernard - 2e éd. - 1986 - 2-7114-0983-X - 1" label="Catalogue">default:UNIMARC:90
+       </CATALOG_ID>
+       <FATHER_CATALOG_ID display="" label="Périodique"/>
+       <SUITE_ADDED_DATE display="" label="Associé le"/>
+       <SUITE_CATALOG_ID display="" label="Ensemble catalogue"/>
+       <SUITE_ID display="" label="Ensemble"/>
+       <SUITE_BARCODE display="" label="Ensemble réf."/>
+       <STATUS display="Exclu" label="Etat">110
+       </STATUS>
+       <TYPE display="Prêt" label="Type comm.">1
+       </TYPE>
+       <RETURN_SITE display="" label="Bib. de retour"/>
+       <LAST_BORROWER_ID display="" label="Dernier emprunteur"/>
+       <LAST_LOAN_DATE display="" label="Date de dernier emprunt"/>
+       <LAST_RETURN_DATE display="" label="Date du dernier retour"/>
+       <ITEM_TYPE display="Exemplaire" label="Type d'item">library
+       </ITEM_TYPE>
+       <ITEM_ASIDE_MODE display="Standard" label="Type de gestion des mises à disposition">0
+       </ITEM_ASIDE_MODE>
+       <VOLUME_NUMBER display="" label="N° volume"/>
+       <LANGUAGE display="" label="Langue"/>
+       <MEDIUM display="" label="Support"/>
+       <ACQUISITION_MODE display="" label="Mode d'acquisition"/>
+       <DATE_ACQUISITION display="" label="Date acquisition"/>
+       <DONOR display="" label="Donateur"/>
+       <PURCHASE_ORDER display="" label="N° bon de commande"/>
+       <PRICE display="" label="Prix"/>
+       <PHYSICAL_CONDITION display="" label="Etat de conservation"/>
+       <CONDITION_CHECK_DATE display="" label="Date dernier contrôle"/>
+       <WORK_REQUIRED display="" label="Traitement à prévoir"/>
+       <PLANNED_WORK_DATE display="" label="Date prévisionnelle du traitement"/>
+       <LAST_WORK_DONE display="" label="Dernier traitement effectué"/>
+       <DATE_LAST_WORK_DONE display="" label="Date dernier traitement"/>
+       <PHYSICAL_WORK_NOTES display="" label="Note sur l'état physique" separ="_RS_"/>
+       <HOLDER display="" label="Dépositaire"/>
+       <DEPARTMENT display="" label="Service dépositaire"/>
+       <PERMISSION_REQUIRED_FROM display="" label="Demander autorisation à"/>
+       <CONDITION_USE display="" label="Conditions d'utilisation"/>
+       <TYPE_INSCRIPTION display="" label="Type d'inscription"/>
+       <INSCRIPTION_NOTES display="" label="Note inscriptions" separ="_RS_"/>
+       <GENERAL_NOTES display="" label="Notes diverses" separ="_RS_"/>
+       <DOMAINE display="" label="Domaine"/>
+       <TYPE_DOC display="Monographie" label="Type document">1
+       </TYPE_DOC>
+       <ACCESSION_NUMBER display="23340" label="N° inventaire">23340
+       </ACCESSION_NUMBER>
+       <OLD_KEY display="" label="N° reprise"/>
+       <PLANNED_DISCARD_DATE display="" label="Date prévue d'épuration"/>
+       <ACTUAL_DISCARD_DATE display="" label="Date effective d'épuration"/>
+       <PERIOD_CURRENCY display="" label="Durée de conservation (en jours)"/>
+       <SITE_OLD display="" label="Ancien site"/>
+       <STATUS_OLD display="" label="Ancien état de conservation"/>
+       <COLLECTION_OLD display="" label="Ancienne localisation"/>
+       <STOCKTAKING_MISSING display="" label="Objet manquant suite à récolement"/>
+       <STOCKTAKING_UPDATEDATE display="" label="Date de mise à jour via récolement"/>
+       <ON_FLY_CATALOGING display="Non" label="Objet catalogué à la volée">0
+       </ON_FLY_CATALOGING>
+       <SECURED_LOAN display="" label="Prêt secouru"/>
+     </ICOMM_ITEM>
+     <ICOMM_ITEM display="" label="Objet de prêt">
+       <UNIQUE_KEY display="9188" label="Exemplaire">9188
+       </UNIQUE_KEY>
+       <SITE display="Médiathèque - INSEP" label="Site">1
+       </SITE>
+       <CODE display="1000867" label="Code-barres">1000867
+       </CODE>
+       <CATEGORY display="Monographies" label="Catégorie de prêt">1
+       </CATEGORY>
+       <IS_NEW display="Non" label="Nouveauté">0
+       </IS_NEW>
+       <IS_NEW_END_DATE display="" label="Date fin nouveauté"/>
+       <IS_SUITE display="Non" label="Représente un ensemble">0
+       </IS_SUITE>
+       <COLLECTION display="Magasin 2 - Ouvrages" label="Localisation">10
+       </COLLECTION>
+       <SECTION display="" label="Section"/>
+       <CALL_NUMBER display="EPD3 FER" label="Cote">EPD3 FER
+       </CALL_NUMBER>
+       <SITE_ISSUED_FROM display="" label="Bib. d'origine"/>
+       <TRANSFER_DATE display="" label="Dernier transfert le"/>
+       <RECOVER_DATE display="" label="Dernier rapatriement le"/>
+       <RESTRICTION_CATEGORIES display="" label="Restriction catégories" separ="_RS_"/>
+       <RESTRICTION_LOAN_PERIOD display="" label="Durée de prêt de restriction (en min)"/>
+       <RESTRICTION_END_DATE display="" label="Date fin restriction"/>
+       <MESSAGES display="" label="Messages"/>
+       <LOAN_DATE display="19/02/2020" label="Date prêt">20200219
+       </LOAN_DATE>
+       <LOAN_HOUR display="1455" label="Heure prêt">1455
+       </LOAN_HOUR>
+       <DUE_DATE display="18/03/2020" label="Date retour prévue">20200318
+       </DUE_DATE>
+       <DUE_HOUR display="2359" label="Heure retour prévue">2359
+       </DUE_HOUR>
+       <CATALOG_ID display="Sophrologie et compétition sportive - Fernandez, Luis - 1982 - 2-7114-0841-8 - 1" label="Catalogue">default:UNIMARC:321
+       </CATALOG_ID>
+       <FATHER_CATALOG_ID display="" label="Périodique"/>
+       <SUITE_ADDED_DATE display="" label="Associé le"/>
+       <SUITE_CATALOG_ID display="" label="Ensemble catalogue"/>
+       <SUITE_ID display="" label="Ensemble"/>
+       <SUITE_BARCODE display="" label="Ensemble réf."/>
+       <STATUS display="Prêté" label="Etat">2
+       </STATUS>
+       <TYPE display="Prêt" label="Type comm.">1
+       </TYPE>
+       <RETURN_SITE display="" label="Bib. de retour"/>
+       <LAST_BORROWER_ID display="" label="Dernier emprunteur"/>
+       <LAST_LOAN_DATE display="" label="Date de dernier emprunt"/>
+       <LAST_RETURN_DATE display="" label="Date du dernier retour"/>
+       <ITEM_TYPE display="Exemplaire" label="Type d'item">library
+       </ITEM_TYPE>
+       <ITEM_ASIDE_MODE display="Standard" label="Type de gestion des mises à disposition">0
+       </ITEM_ASIDE_MODE>
+       <VOLUME_NUMBER display="" label="N° volume"/>
+       <LANGUAGE display="" label="Langue"/>
+       <MEDIUM display="" label="Support"/>
+       <ACQUISITION_MODE display="" label="Mode d'acquisition"/>
+       <DATE_ACQUISITION display="" label="Date acquisition"/>
+       <DONOR display="" label="Donateur"/>
+       <PURCHASE_ORDER display="" label="N° bon de commande"/>
+       <PRICE display="" label="Prix"/>
+       <PHYSICAL_CONDITION display="Bon" label="Etat de conservation">2
+       </PHYSICAL_CONDITION>
+       <CONDITION_CHECK_DATE display="" label="Date dernier contrôle"/>
+       <WORK_REQUIRED display="" label="Traitement à prévoir"/>
+       <PLANNED_WORK_DATE display="" label="Date prévisionnelle du traitement"/>
+       <LAST_WORK_DONE display="" label="Dernier traitement effectué"/>
+       <DATE_LAST_WORK_DONE display="" label="Date dernier traitement"/>
+       <PHYSICAL_WORK_NOTES display="" label="Note sur l'état physique" separ="_RS_"/>
+       <HOLDER display="" label="Dépositaire"/>
+       <DEPARTMENT display="" label="Service dépositaire"/>
+       <PERMISSION_REQUIRED_FROM display="" label="Demander autorisation à"/>
+       <CONDITION_USE display="" label="Conditions d'utilisation"/>
+       <TYPE_INSCRIPTION display="" label="Type d'inscription"/>
+       <INSCRIPTION_NOTES display="" label="Note inscriptions" separ="_RS_"/>
+       <GENERAL_NOTES display="" label="Notes diverses" separ="_RS_"/>
+       <DOMAINE display="" label="Domaine"/>
+       <TYPE_DOC display="Monographie" label="Type document">1
+       </TYPE_DOC>
+       <ACCESSION_NUMBER display="11387" label="N° inventaire">11387
+       </ACCESSION_NUMBER>
+       <OLD_KEY display="" label="N° reprise"/>
+       <PLANNED_DISCARD_DATE display="" label="Date prévue d'épuration"/>
+       <ACTUAL_DISCARD_DATE display="" label="Date effective d'épuration"/>
+       <PERIOD_CURRENCY display="" label="Durée de conservation (en jours)"/>
+       <SITE_OLD display="" label="Ancien site"/>
+       <STATUS_OLD display="" label="Ancien état de conservation"/>
+       <COLLECTION_OLD display="" label="Ancienne localisation"/>
+       <STOCKTAKING_MISSING display="" label="Objet manquant suite à récolement"/>
+       <STOCKTAKING_UPDATEDATE display="" label="Date de mise à jour via récolement"/>
+       <ON_FLY_CATALOGING display="Non" label="Objet catalogué à la volée">0
+       </ON_FLY_CATALOGING>
+       <SECURED_LOAN display="Non" label="Prêt secouru">0
+       </SECURED_LOAN>
+     </ICOMM_ITEM>
+   </ICOMM_ITEMS>
+</response>
diff --git a/tests/fixtures/flora_loan_list.xml b/tests/fixtures/flora_loan_list.xml
new file mode 100644
index 00000000000..645028f449c
--- /dev/null
+++ b/tests/fixtures/flora_loan_list.xml
@@ -0,0 +1,93 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<response>
+  <ICOMM_LOANS>
+    <ICOMM_LOAN display="" label="Prêt">
+      <LOAN_SITE display="Site 1" label="Bibliothèque de prêt">1</LOAN_SITE>
+      <COMM_TYPE display="Prêt" label="Type comm. appliqué">1</COMM_TYPE>
+      <BORROWER_SYSTEM_ID display="GLUHON - Paul - stu - carte123 - Public adulte - 29/04/2010 -'
+                                   14/06/2011 - 0.00" label="Lecteur">UTILISATEUR9869</BORROWER_SYSTEM_ID>
+      <BORROWER_BARCODE display="carte123" label="Cdb lect.">carte123</BORROWER_BARCODE>
+      <BORROWER_CATEGORY display="Public adulte" label="Cat. lect.">1</BORROWER_CATEGORY>
+      <SUITE_CATALOG_ID display="" label="Suite catalogue"/>
+      <SUITE_ID display="" label="Suite"/>
+      <SUITE_BARCODE display="" label="Suite réf."/>
+      <CATALOG_SYSTEM_ID display="1 - Systèmes d\'information et management des organisations - Reix ,
+                                  Robert - 1 se - 2-7117-7568-2" label="Catalogue">default:UNIMARC:72</CATALOG_SYSTEM_ID>
+      <ITEM_SYSTEM_ID display="1 - Systèmes d\'information et management des organisations - Reix ,
+			       Robert - 1 se - 2-7117-7568-2|0807011|Papier|Site 1|Prêté|Prêt"
+		      label="Objet">system:ICOMM_ITEM:517</ITEM_SYSTEM_ID>
+      <ITEM_BARCODE display="0807011" label="Réf.">0807011</ITEM_BARCODE>
+      <ITEM_COMM_TYPE display="Prêt" label="Type comm. objet">1</ITEM_COMM_TYPE>
+      <ITEM_SITE display="Annecy" label="Site">1</ITEM_SITE>
+      <ITEM_COMM_CATEGORY display="Papier" label="Cat. comm.">1</ITEM_COMM_CATEGORY>
+      <ITEM_IS_NEW display="Oui" label="Nouveauté">1</ITEM_IS_NEW>
+      <ITEM_CALL_NUMBER display="" label="Cote"/>
+      <ITEM_IS_SUITE display="Non" label="Prêt d\'une suite">0</ITEM_IS_SUITE>
+      <LOAN_STATUS display="A retourner" label="Etat">3</LOAN_STATUS>
+      <LOAN_DATE display="23/11/2009" label="Date comm.">20091123</LOAN_DATE>
+      <LOAN_HOUR display="1749" label="Heure prêt">1749</LOAN_HOUR>
+      <DUE_DATE display="01/12/2009" label="Date retour">20091201</DUE_DATE>
+      <DUE_HOUR display="1749" label="Heure retour">1749</DUE_HOUR>
+      <RENEWAL_COUNT display="0" label="Pro.">0</RENEWAL_COUNT>
+      <RENEWAL_DATE display="" label="Dernière prolongation le"/>
+      <OVERDUE_COUNT display="1" label="Rel.">1</OVERDUE_COUNT>
+      <OVERDUE_NEXT_DATE display="25/04/2010" label="A relancer'
+                                       		     le">20100425</OVERDUE_NEXT_DATE>
+      <OVERDUE_NEXT_HOUR display="1619" label="A relancer le (heure)">1619</OVERDUE_NEXT_HOUR>
+      <OVERDUE_DATE_0 display="" label="Date de rappel"/>
+      <OVERDUE_DATE_1 display="23/04/2010" label="Date 1ère relance">20100423</OVERDUE_DATE_1>
+      <OVERDUE_DATE_2 display="" label="Date 2ème relance"/>
+      <OVERDUE_DATE_3 display="" label="Date 3ème relance"/>
+      <OVERDUE_DATE_4 display="" label="Date 4ème relance"/>
+      <OVERDUE_DATE_5 display="" label="Date 5ème relance"/>
+      <SECURED_LOAN display="Non" label="Prêt secouru">0</SECURED_LOAN>
+      <RESPONSABLE_USER display="Tom - Jerry  - comm - 3845 - Public adulte - 09/04/2007 -
+                                 07/06/2011 - 0.00" label="Utilisateur responsable">38</RESPONSABLE_USER>
+      <MOTIVE display="" label="Motif"/>
+    </ICOMM_LOAN>
+    <ICOMM_LOAN display="" label="Prêt">
+      <LOAN_SITE display="Site 1" label="Bibliothèque de prêt">1</LOAN_SITE>
+      <COMM_TYPE display="Prêt" label="Type comm. appliqué">1</COMM_TYPE>
+      <BORROWER_SYSTEM_ID display="GLUHON - Paul - stu - carte123 - Public adulte - 29/04/2010 -'
+                                   14/06/2011 - 0.00" label="Lecteur">UTILISATEUR9869</BORROWER_SYSTEM_ID>
+      <BORROWER_BARCODE display="carte123" label="Cdb lect.">carte123</BORROWER_BARCODE>
+      <BORROWER_CATEGORY display="Public adulte" label="Cat. lect.">1</BORROWER_CATEGORY>
+      <SUITE_CATALOG_ID display="" label="Suite catalogue"/>
+      <SUITE_ID display="" label="Suite"/>
+      <SUITE_BARCODE display="" label="Suite réf."/>
+      <CATALOG_SYSTEM_ID display="1 - Des petites choses se perdent - Reix ,
+                                  Laurent Arthur - 1 se - 2-7117-7569-2" label="Catalogue">default:UNIMARC:666</CATALOG_SYSTEM_ID>
+      <ITEM_SYSTEM_ID display="1 - Des petites choses se perdent - Reix ,
+			       Laurent Arthur - 1 se - 2-7117-7569-2|0807011|Papier|Site 1|Prêté|Prêt"
+		      label="Objet">system:ICOMM_ITEM:666</ITEM_SYSTEM_ID>
+      <ITEM_BARCODE display="123456" label="Réf.">123456</ITEM_BARCODE>
+      <ITEM_COMM_TYPE display="Prêt" label="Type comm. objet">1</ITEM_COMM_TYPE>
+      <ITEM_SITE display="Annecy" label="Site">1</ITEM_SITE>
+      <ITEM_COMM_CATEGORY display="Papier" label="Cat. comm.">1</ITEM_COMM_CATEGORY>
+      <ITEM_IS_NEW display="Oui" label="Nouveauté">1</ITEM_IS_NEW>
+      <ITEM_CALL_NUMBER display="" label="Cote"/>
+      <ITEM_IS_SUITE display="Non" label="Prêt d\'une suite">0</ITEM_IS_SUITE>
+      <LOAN_STATUS display="A retourner" label="Etat">3</LOAN_STATUS>
+      <LOAN_DATE display="23/11/2009" label="Date comm.">20091123</LOAN_DATE>
+      <LOAN_HOUR display="1749" label="Heure prêt">1749</LOAN_HOUR>
+      <DUE_DATE display="03/12/2009" label="Date retour">20091203</DUE_DATE>
+      <DUE_HOUR display="1749" label="Heure retour">1749</DUE_HOUR>
+      <RENEWAL_COUNT display="0" label="Pro.">0</RENEWAL_COUNT>
+      <RENEWAL_DATE display="" label="Dernière prolongation le"/>
+      <OVERDUE_COUNT display="1" label="Rel.">1</OVERDUE_COUNT>
+      <OVERDUE_NEXT_DATE display="25/04/2010" label="A relancer'
+                                       		     le">20100425</OVERDUE_NEXT_DATE>
+      <OVERDUE_NEXT_HOUR display="1619" label="A relancer le (heure)">1619</OVERDUE_NEXT_HOUR>
+      <OVERDUE_DATE_0 display="" label="Date de rappel"/>
+      <OVERDUE_DATE_1 display="23/04/2010" label="Date 1ère relance">20100423</OVERDUE_DATE_1>
+      <OVERDUE_DATE_2 display="" label="Date 2ème relance"/>
+      <OVERDUE_DATE_3 display="" label="Date 3ème relance"/>
+      <OVERDUE_DATE_4 display="" label="Date 4ème relance"/>
+      <OVERDUE_DATE_5 display="" label="Date 5ème relance"/>
+      <SECURED_LOAN display="Non" label="Prêt secouru">0</SECURED_LOAN>
+      <RESPONSABLE_USER display="Tom - Jerry - comm - 3845 - Public adulte - 09/04/2007 -'
+                                 07/06/2011 - 0.00" label="Utilisateur responsable">38</RESPONSABLE_USER>
+      <MOTIVE display="" label="Motif"/>
+    </ICOMM_LOAN>
+  </ICOMM_LOANS>
+</response>
diff --git a/tests/library/Class/CommSigbTest.php b/tests/library/Class/CommSigbTest.php
index c838ebf3380..c9fae72de5b 100644
--- a/tests/library/Class/CommSigbTest.php
+++ b/tests/library/Class/CommSigbTest.php
@@ -809,4 +809,39 @@ class CommSigbItemAvailabilityWithoutSigbTest extends ModelTestCase {
   public function canConsultShouldReturnFalse() {
     $this->assertFalse($this->comm_sigb->canConsult(Class_IntBib::find(5), ''));
   }
-}
\ No newline at end of file
+}
+
+
+
+class CommSigbFloraTest extends CommSigbTestCase {
+  public function setUp() {
+    parent::setUp();
+
+    $this->bib_maze = $this->fixture('Class_IntBib',
+                                     ['id' => 5,
+                                      'comm_params' => ["url_serveur" => 'http://flora.org',
+                                                        'login' => 'api',
+                                                        'password' => 'secret'],
+                                      'comm_sigb' => Class_IntBib::COM_FLORA
+                                      ]);
+
+    Class_WebService_SIGB_Flora::setService($this->createMockForService('Flora'));
+  }
+
+
+  public function tearDown() {
+    Class_WebService_SIGB_Flora::reset();
+    parent::tearDown();
+  }
+
+
+  /** @test */
+  public function getModeCommShouldReturnAnArrayWithCommParams() {
+    $this->assertEquals(["url_serveur" => 'http://flora.org',
+                              'login' => 'api',
+                              'password' => 'secret',
+                              "type" => Class_IntBib::COM_FLORA,
+                              'id_bib' => 5],
+                        $this->bib_maze->getModeComm(5));
+  }
+}
diff --git a/tests/library/Class/WebService/SIGB/FloraTest.php b/tests/library/Class/WebService/SIGB/FloraTest.php
new file mode 100644
index 00000000000..f15e6396bde
--- /dev/null
+++ b/tests/library/Class/WebService/SIGB/FloraTest.php
@@ -0,0 +1,392 @@
+<?php
+/**
+ * Copyright (c) 2012-2020, 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
+ */
+
+
+abstract class FloraTestCase extends ModelTestCase {
+  protected
+    $_emprunteur,
+    $_mock_http,
+    $_service,
+    $_paul;
+
+  public function setUp() {
+    parent::setUp();
+    $this->_service = Class_WebService_SIGB_Flora::getService(['url_serveur' => 'https://flora.sigb.org/',
+                                                               'login' => 'API',
+                                                               'password' => 'secret']);
+
+
+    $this->_mock_http = $this->mock()
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?method=login&code=API&password=secret')
+                             ->answers("<?xml version='1.0' encoding='UTF-8' ?>\n"
+                                       .'<response>'
+                                       .'  <apiSession>ze_token</apiSession>'
+                                       .'  <timeout>1800</timeout>'
+                                       .'</response>')
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.identifyBorrower&borrowerCode=carte123')
+                             ->answers("<?xml version='1.0' encoding='UTF-8' ?>\n"
+                                       .'<response>'
+                                       .'  <borrowerId>UTILISATEUR9869</borrowerId>>'
+                                       .'</response>')
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.getBorrowerIdentity&borrowerId=UTILISATEUR9869')
+                             ->answers(file_get_contents(ROOT_PATH . 'tests/fixtures/flora_borrower_identity.xml'))
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=logout')
+                             ->answers("<?xml version='1.0' encoding='UTF-8' ?>\n"
+                                       .'<response>'
+                                       .'  <success/>'
+                                       .'</response>')
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.getLoanList&borrowerId=UTILISATEUR9869')
+                             ->answers(file_get_contents(ROOT_PATH . 'tests/fixtures/flora_loan_list.xml'))
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.getAskHoldList&borrowerId=UTILISATEUR9869')
+                             ->answers(file_get_contents(ROOT_PATH . 'tests/fixtures/flora_hold_list.xml'))
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.getItems&catalogId=' . urlencode('default:UNIMARC:90'))
+                             ->answers(file_get_contents(ROOT_PATH . 'tests/fixtures/flora_items_circulation.xml'))
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.addPurchaseRequest&borrowerId=UTILISATEUR9869&type=IPURCH_REQ_MONOGRAPH&xmlData=<?xmlversion="1.0" encoding="UTF-8" ?> <IPURCH_REQUEST> <TYPE_DOC>1</TYPE_DOC><TYPE>1</TYPE> <STATUT_REQ>1</STATUT_REQ> <TITLE>Mémoires d\'outre-tombe</TITLE><XML_FIELD> <BORROWER_COMMENTS>Test saisie suggestion par xapi</BORROWER_COMMENTS></XML_FIELD> </IPURCH_REQUEST>')
+                             ->answers( "<?xml version='1.0' encoding='UTF-8' ?>\n"
+                                       .'<response>'
+                                       .'<success/>'
+                                       .'</response>')
+
+
+                             ->whenCalled('open_url')
+                             ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.hold&borrowerId=UTILISATEUR9869&catalogId=' . urlencode('default:UNIMARC:90') . '&siteId=1&categoryId=1&itemNewId=2')
+                             ->answers( "<?xml version='1.0' encoding='UTF-8' ?>\n"
+                                       .'<response>'
+                                       .'<success/>'
+                                       .'</response>')
+
+                             ->beStrict();
+
+    $this->_service->setWebClient($this->_mock_http);
+
+
+    $this->_paul = $this->fixture('Class_Users',
+                                  ['id' => 14,
+                                   'login' => 'paulg',
+                                   'password' => 'secret',
+                                   'idabon' => 'carte123',
+                                   'id_int_bib' => 1])
+                        ->beAbonneSIGB();
+    ZendAfi_Auth::getInstance()->logUser($this->_paul);
+
+
+    $this->fixture('Class_IntBib',
+                   ['id' => 1,
+                    'libelle' => 'Annecy',
+                    'comm_sigb' => Class_IntBib::COM_FLORA]);
+
+    $this->fixture('Class_Notice',
+                   ['id' => 1,
+                    'titre_principal' => 'Systèmes d\'information',
+                    'type_doc' => Class_CodifTypeDoc::LIVRE,
+                    'exemplaires' => [
+                                      $this->fixture('Class_Exemplaire',
+                                                     ['id' => 1,
+                                                      'id_notice' => 1,
+                                                      'code_barres' => '0807011',
+                                                      'id_int_bib' => 1,
+                                                      'id_origine' => 'default:UNIMARC:72']
+                                      )]
+                   ]);
+
+    $this->fixture('Class_Notice',
+                   ['id' => 2,
+                    'titre_principal' => 'La circulation invisible',
+                    'type_doc' => Class_CodifTypeDoc::LIVRE,
+                    'exemplaires' => [
+                                      $this->fixture('Class_Exemplaire',
+                                                     ['id' => 2,
+                                                      'id_notice' => 2,
+                                                      'code_barres' => '9991207408',
+                                                      'id_int_bib' => 1,
+                                                      'id_origine' => 'default:UNIMARC:90']
+                                      )]
+                   ]);
+  }
+
+
+  public function tearDown() {
+    Class_WebService_SIGB_Flora::reset();
+    parent::tearDown();
+  }
+}
+
+
+
+
+class FloraGetEmprunteurTest extends FloraTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->_emprunteur = $this->_service->getEmprunteur($this->_paul);
+  }
+
+
+  public function borrowerDatas() {
+    return [[ 'name', 'GLUHON' ],
+            ['prenom', 'PAUL'],
+            ['dateNaissance', '1977-05-24'],
+            ['endDate', '2021-01-22'],
+            ['codePostal', '74000'],
+            ['ville', 'ANNECY'],
+            ['email', 'paul.gluhon@mail.fr']
+    ];
+
+  }
+
+
+  /** @test
+      @dataProvider borrowerDatas */
+  public function borrowerShouldHaveData($attribute_name, $value) {
+    $this->assertEquals($value, call_user_func( [ $this->_emprunteur, 'get'.$attribute_name ]));
+  }
+
+
+  /** @test */
+  public function borrowerShouldBeValid() {
+    $this->assertTrue($this->_emprunteur->isValid());
+  }
+
+
+  /** @test */
+  public function emprunteurShouldHaveTwoLoans() {
+    $this->assertEquals(2, $this->_emprunteur->getNbEmprunts());
+  }
+
+
+  public function firstLoanAttributes() {
+    return
+      [
+       ['getTitre', 'Systèmes d\'information'],
+       ['getDateRetourISO8601', '2009-12-01'],
+       ['isRenewable', false],
+      ];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider firstLoanAttributes
+   */
+  public function firstLoanItemAttributeShouldEqualsValue($attribute, $value) {
+    $this->assertEquals($value,
+                        call_user_func([$this->_emprunteur->getEmprunts()[0], $attribute]));
+  }
+
+
+  public function secondLoanAttributes() {
+    return
+      [
+       ['getTitre', 'Des petites choses se perdent'],
+       ['getAuteur', 'Laurent Arthur'],
+       ['getDateRetourISO8601', '2009-12-03'],
+       ['getType', 'A retourner'],
+       ['getBibliotheque', 'Annecy'],
+       ['isRenewable', false],
+      ];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider secondLoanAttributes
+   */
+  public function secondLoanItemAttributeShouldEqualsValue($attribute, $value) {
+    $this->assertEquals($value,
+                        call_user_func([$this->_emprunteur->getEmprunts()[1], $attribute]));
+  }
+
+
+  /** @test */
+  public function emprunteurShouldHaveTwoHold() {
+    $this->assertEquals(2, $this->_emprunteur->getNbReservations());
+  }
+
+
+  /** @test */
+  public function holdTitleShouldBeLaCirculationInvisible() {
+    $this->assertEquals('La circulation invisible',
+                        $this->_emprunteur
+                        ->getReservations()[0]
+                        ->getNoticeOpac()
+                        ->getTitrePrincipal());
+  }
+
+
+  public function secondHoldAttributes() {
+    return
+      [
+       ['getTitre', 'Des petites choses'],
+       ['getAuteur', 'arthur , laurent'],
+      ];
+  }
+
+
+  /**
+   * @test
+   * @dataProvider secondHoldAttributes
+   */
+  public function secondHoldItemAttributeShouldEqualsValue($attribute, $value) {
+    $this->assertEquals($value,
+                        call_user_func([$this->_emprunteur->getReservations()[1],
+                                        $attribute]));
+  }
+
+}
+
+
+
+
+class FloraDisconnectTest extends FloraTestCase {
+  /** @test */
+  public function sessionShouldHaveBeenClosedWhenOpened() {
+    $this->_emprunteur = $this->_service->getEmprunteur($this->_paul);
+    $this->_service->disconnect();
+    $this->assertTrue($this->_mock_http->methodHasBeenCalledWithParams('open_url',
+                                                                       ['https://flora.sigb.org/XAPI?apiSession=ze_token&method=logout']));
+  }
+
+
+  /** @test */
+  public function sessionShouldShouldNotBeClosedWhenNotUsed() {
+    $this->_service->disconnect();
+    $this->assertFalse($this->_mock_http->methodHasBeenCalled('open_url'));
+  }
+}
+
+
+
+
+class FloraGetNoticeCirculationTest extends FloraTestCase {
+  public function setUp() {
+    parent::setUp();
+    $this->_notice = $this->_service->getNotice('default:UNIMARC:90');
+  }
+
+
+  /** @test */
+  public function countItemsShouldBeTwo() {
+    $this->assertCount(2,$this->_notice->getExemplaires());
+    $exemplaire = array_first($this->_notice->getExemplaires());
+    return $exemplaire;
+  }
+
+
+  /**
+   *   @test
+   *   @depends countItemsShouldBeTwo
+   */
+  public function firstExemplaireCodeBarreShouldBe9991207408($exemplaire) {
+    $this->assertEquals('9991207408',$exemplaire->getCodeBarre());
+  }
+
+
+  /**
+   * @test
+   * @depends countItemsShouldBeTwo
+   */
+  public function firstExemplaireAvailabilityShouldBeExclu($exemplaire) {
+    $this->assertEquals('Exclu', $exemplaire->getDisponibilite());
+  }
+
+
+  /**
+   * @test
+   * @depends countItemsShouldBeTwo
+   */
+  public function firstExemplaireIdShouldBe11325($exemplaire) {
+    $this->assertEquals(11325, $exemplaire->getId());
+  }
+
+
+  /** @test */
+  public function firstItemShouldNotBeReservable() {
+    $this->assertFalse($this->_notice->getExemplaires()[0]->isReservable());
+  }
+
+
+  /**
+   * @test
+   * @depends countItemsShouldBeTwo
+   */
+  public function firstExemplaireDateRetourShouldBeEmpty($exemplaire) {
+    $this->assertEmpty($exemplaire->getDateRetour());
+  }
+
+
+  /** @test */
+  public function secondItemDueDateShouldBe2020_02_19() {
+    $this->assertEquals('18/03/2020', $this->_notice->getExemplaires()[1]->getDateRetour());
+  }
+
+
+  /** @test */
+  public function secondItemShouldBeReservable() {
+    $this->assertTrue($this->_notice->getExemplaires()[1]->isReservable());
+  }
+}
+
+
+
+class FloraHoldTitleCirculationInvisible extends FloraTestCase {
+  /** @test */
+  public function withSuccessfullRequestShouldAnswerStatutTrue() {
+    $response = $this->_service->reserverExemplaire(Class_Users::find(14),
+                                                    Class_Exemplaire::find(2),
+                                                    '');
+    $this->assertEquals(['statut' => true, 'erreur' => ''],
+                        $response);
+  }
+
+
+  /** @test */
+  public function withUnsuccessfullRequestShouldAnswerStatutFalseAndErrorHoldNotPossible() {
+    $this
+      ->_mock_http
+      ->whenCalled('open_url')
+      ->with('https://flora.sigb.org/XAPI?apiSession=ze_token&method=communication.hold&borrowerId=UTILISATEUR9869&catalogId=' . urlencode('default:UNIMARC:90') . '&siteId=1&categoryId=1&itemNewId=2')
+      ->answers("<?xml version='1.0' encoding='UTF-8' ?>\n"
+                . "  <error>\n"
+                . "    <code>APPLICATION_ERROR</code>\n"
+                . "    <message>Could not hold</message>\n"
+                . "  </error>");
+
+    $response = $this->_service->reserverExemplaire(Class_Users::find(14),
+                                                    Class_Exemplaire::find(2),
+                                                    '');
+    $this->assertEquals(['statut' => false, 'erreur' => 'Could not hold'],
+                        $response);
+  }
+}
\ No newline at end of file
-- 
GitLab