From f8c4b83e76846ac7e072c98d6e321bcf7e5194a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?ANDRE=20s=C3=A9bastien?= <sandre@afi-sa.fr>
Date: Thu, 30 May 2024 13:37:03 +0000
Subject: [PATCH] hotline : #188862 : add new zoom field

---
 VERSIONS_HOTLINE/188862                       |   1 +
 cosmogramme/sql/patch/patch_464.php           |   9 +
 library/Class/Lieu.php                        |  52 ++-
 library/Class/Map.php                         |  91 ++++-
 .../Controller/Plugin/Manager/Lieu.php        |  16 +-
 library/ZendAfi/Form/Admin/Location.php       |  16 +-
 library/ZendAfi/View/Helper/MapForLieu.php    |  22 +-
 library/ZendAfi/View/Helper/RenderLieu.php    |   4 +-
 .../admin/controllers/LieuControllerTest.php  | 368 +++++++++++++-----
 tests/db/UpgradeDBTest.php                    |  19 +
 tests/library/Class/LieuTest.php              |   9 +-
 .../RendezVous/UsergroupAgendaAdminTest.php   |  17 +-
 12 files changed, 457 insertions(+), 167 deletions(-)
 create mode 100644 VERSIONS_HOTLINE/188862
 create mode 100644 cosmogramme/sql/patch/patch_464.php

diff --git a/VERSIONS_HOTLINE/188862 b/VERSIONS_HOTLINE/188862
new file mode 100644
index 00000000000..376fb4ed5e1
--- /dev/null
+++ b/VERSIONS_HOTLINE/188862
@@ -0,0 +1 @@
+ - correctif #188862 : Administration : Lieux, ajout d'un nouveau champ zoom, pour permettre de change l'image d'Open street map.
\ No newline at end of file
diff --git a/cosmogramme/sql/patch/patch_464.php b/cosmogramme/sql/patch/patch_464.php
new file mode 100644
index 00000000000..4fbe11fbf2a
--- /dev/null
+++ b/cosmogramme/sql/patch/patch_464.php
@@ -0,0 +1,9 @@
+<?php
+$adapter = Zend_Db_Table::getDefaultAdapter();
+
+try
+{
+  $adapter->query('ALTER TABLE `lieux` ADD COLUMN `zoom` INT(3) NOT NULL DEFAULT 15;');
+}
+catch (Exception $e)
+{}
diff --git a/library/Class/Lieu.php b/library/Class/Lieu.php
index 8b962a8c414..4ec246cb1e1 100644
--- a/library/Class/Lieu.php
+++ b/library/Class/Lieu.php
@@ -19,10 +19,14 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-class Class_LieuLoader extends Storm_Model_Loader {
-  protected $_labels_by_ids_cache;
 
-  public function newWith($library) {
+class Class_LieuLoader extends Storm_Model_Loader
+{
+
+  protected ?array $_labels_by_ids_cache = null;
+
+  public function newWith($library)
+  {
     $location = Class_Lieu::newInstance();
     $location->updateAttributes(['libelle' => $library->getLibelle(),
                                  'adresse' => $library->getAdresse(),
@@ -34,22 +38,27 @@ class Class_LieuLoader extends Storm_Model_Loader {
   }
 
 
-  public function getAllLibelles() {
-    if (isset($this->_labels_by_ids_cache))
+  public function getAllLibelles(): array
+  {
+    if (null !== $this->_labels_by_ids_cache)
       return $this->_labels_by_ids_cache;
 
-    $labels = [];
-    foreach(Class_Lieu::findAllBy(['order' => 'libelle']) as $location)
-      $labels[$location->getId()] = $location->getLibelle();
+    $this->_labels_by_ids_cache = [];
+    foreach (Class_Lieu::query()
+             ->select(['id', 'libelle'])
+             ->order('libelle')
+             ->fetchAll() as $location)
+      $this->_labels_by_ids_cache [$location->getId()] = $location->getLibelle();
 
-    return $this->_labels_by_ids_cache = $labels;
+    return $this->_labels_by_ids_cache;
   }
 }
 
 
 
 
-class Class_Lieu extends Storm_Model_Abstract {
+class Class_Lieu extends Storm_Model_Abstract
+{
   use Trait_Translator;
 
   const GENERATE = 'GENERATE';
@@ -66,7 +75,8 @@ class Class_Lieu extends Storm_Model_Abstract {
                                           'mail' => '',
                                           'latitude' => '',
                                           'longitude' => '',
-                                          'url' => ''];
+                                          'url' => '',
+                                          'zoom' => Class_Map::DEFAULT_ZOOM];
 
   protected $_has_many = ['articles' => ['model' => 'Class_Article',
                                          'role' => 'lieu',
@@ -76,20 +86,21 @@ class Class_Lieu extends Storm_Model_Abstract {
                                      'role' => 'lieu',
                                      'order' => 'libelle']];
 
-
-  public function validate() {
+  public function validate()
+  {
     $this->check($this->hasLibelle(), $this->_('Le libellé doit être renseigné'));
   }
 
 
-  public function updateCoordinates($osm_service) {
+  public function updateCoordinates($osm_service): self
+  {
     if ($this->getLatitude() && $this->getLongitude())
       return $this;
 
-    if(!$coordinates = $osm_service->getCoordinates(['street' => $this->getAdresse(),
-                                                    'city' => $this->getVille(),
-                                                    'postalcode' => $this->getCodePostal(),
-                                                    'country' => $this->getPays()]))
+    if ( ! ($coordinates = $osm_service->getCoordinates(['street' => $this->getAdresse(),
+                                                         'city' => $this->getVille(),
+                                                         'postalcode' => $this->getCodePostal(),
+                                                         'country' => $this->getPays()])))
       return $this;
 
     return $this
@@ -98,7 +109,8 @@ class Class_Lieu extends Storm_Model_Abstract {
   }
 
 
-  public function getLibrary() {
+  public function getLibrary(): ?Class_Bib
+  {
     return Class_Bib::findFirstBy(['id_lieu' => $this->getId()]);
   }
-}
\ No newline at end of file
+}
diff --git a/library/Class/Map.php b/library/Class/Map.php
index 47395d0dbed..06a4ead77ae 100644
--- a/library/Class/Map.php
+++ b/library/Class/Map.php
@@ -20,53 +20,114 @@
  */
 
 
-class Class_Map extends Class_Entity {
-  const ZOOM = 15;
+class Class_Map
+{
 
-  public static function newWith($location) {
-    return (new static())->setLocation($location);
+  const DEFAULT_ZOOM = 15;
+  const MINI_ZOOM = 10;
+  const MAXI_ZOOM = 18;
+
+  protected ?Class_Lieu $_location;
+  protected string $_label = '';
+
+  public static function newWith(?Class_Lieu $location): self
+  {
+    return (new static)->setLocation($location);
   }
 
 
-  public function __construct() {
+  public function __construct()
+  {
     $this->setLabel('osm');
   }
 
 
-  public function getStaticMapUrl($params) {
-    if (!$full_adress = $this->getFullAdress())
+  public static function addElement(ZendAfi_Form $form, string $label): ZendAfi_Form
+  {
+    return $form->addElement('number', 'zoom', ['label' => $label,
+                                                'value' => static::DEFAULT_ZOOM,
+                                                'min' => static::MINI_ZOOM,
+                                                'max' => static::MAXI_ZOOM,
+                                                'required' => true,
+                                                'allowEmpty' => false]);
+  }
+
+
+  public function getLocation(): ?Class_Lieu
+  {
+    return $this->_location;
+  }
+
+
+  public function setLocation(?Class_Lieu $location): self
+  {
+    $this->_location = $location;
+
+    return $this;
+  }
+
+
+  public function getLabel(): string
+  {
+    return $this->_label;
+  }
+
+
+  public function setLabel(string $label): self
+  {
+    $this->_label = $label;
+
+    return $this;
+  }
+
+
+  public function getStaticMapUrl(array $params): string
+  {
+    if ( ! $full_adress = $this->getFullAdress())
       return '';
 
     $params = array_merge(['center' => $full_adress,
-                           'markers' => $full_adress],
+                           'markers' => $full_adress,
+                           'zoom' => $this->_zoom()],
                           $params);
 
-
     return Class_AdminVar::getValueOrDefault('STATIC_MAP') . '?' . http_build_query($params);
   }
 
 
-  public function getFullAdress() {
-    if(!$location = $this->getLocation())
+  public function getFullAdress(): string
+  {
+    if ( ! $location = $this->getLocation())
       return '';
 
-    if (!(($latitude = $location->getLatitude()) && ($longitude = $location->getLongitude())))
+    if ( ! (($latitude = $location->getLatitude()) && ($longitude = $location->getLongitude())))
       return '';
 
     return $latitude . ',' . $longitude;
   }
 
 
-  public function getFullMapUrl() {
-    if(!$location = $this->getLocation())
+  public function getFullMapUrl(): string
+  {
+    if ( ! $location = $this->getLocation())
       return '';
 
     $params = ['mlat' => $location->getLatitude(),
                'mlon' => $location->getLongitude(),
-               '#map' => implode('/', [static::ZOOM,
+               '#map' => implode('/', [$this->_zoom(),
                                        $location->getLatitude(),
                                        $location->getLongitude()])];
 
     return sprintf('https://openstreetmap.org/?%s', http_build_query($params));
   }
+
+
+  protected function _zoom(): int
+  {
+    $zoom = $this->getLocation()->getZoom();
+
+    return $zoom >= static::MINI_ZOOM && $zoom <= static::MAXI_ZOOM
+      ? $zoom
+      : static::DEFAULT_ZOOM;
+  }
 }
diff --git a/library/ZendAfi/Controller/Plugin/Manager/Lieu.php b/library/ZendAfi/Controller/Plugin/Manager/Lieu.php
index a6b2ca62c54..95a5127b85d 100644
--- a/library/ZendAfi/Controller/Plugin/Manager/Lieu.php
+++ b/library/ZendAfi/Controller/Plugin/Manager/Lieu.php
@@ -20,21 +20,26 @@
  */
 
 
-class ZendAfi_Controller_Plugin_Manager_Lieu extends ZendAfi_Controller_Plugin_Manager_Manager {
-  protected function _doBeforeSave($model) {
+class ZendAfi_Controller_Plugin_Manager_Lieu extends ZendAfi_Controller_Plugin_Manager_Manager
+{
+
+  protected function _doBeforeSave($model)
+  {
     $model->updateCoordinates($this->_controller->getOsmService());
     return $this;
   }
 
 
-  public function updateCoordinatesAction() {
+  public function updateCoordinatesAction()
+  {
     $this->_view->titre = $this->_view->_('Mise à jour automatique des coordonnées');
     $this->_view->locations = Class_Lieu::findAllBy(['order' => 'libelle']);
   }
 
 
-  public function updateCoordinatesForAction() {
-    if(!$location = Class_Lieu::find($this->_getParam('id')))
+  public function updateCoordinatesForAction()
+  {
+    if ( ! $location = Class_Lieu::find($this->_getParam('id')))
       return;
 
     $this->_doBeforeSave($location);
@@ -45,4 +50,3 @@ class ZendAfi_Controller_Plugin_Manager_Lieu extends ZendAfi_Controller_Plugin_M
     echo $this->_view->partial('lieu/_update.phtml', ['location' => $location]);
   }
 }
-?>
\ No newline at end of file
diff --git a/library/ZendAfi/Form/Admin/Location.php b/library/ZendAfi/Form/Admin/Location.php
index c8ba731ea54..7f4f421748f 100644
--- a/library/ZendAfi/Form/Admin/Location.php
+++ b/library/ZendAfi/Form/Admin/Location.php
@@ -20,10 +20,14 @@
  */
 
 
-class ZendAfi_Form_Admin_Location extends ZendAfi_Form {
-  public function init() {
+class ZendAfi_Form_Admin_Location extends ZendAfi_Form
+{
+
+  public function init()
+  {
     parent::init();
-    $this
+
+    Class_Map::addElement($this, $this->_('Zoom'))
       ->setMethod('post')
       ->addElement('text', 'libelle', ['label' => $this->_('Libellé'),
                                        'size' => 50,
@@ -67,6 +71,7 @@ class ZendAfi_Form_Admin_Location extends ZendAfi_Form {
       ->addElement('text', 'longitude', ['label' => $this->_('Longitude'),
                                          'size' => 25,
                                          'maxlength' => 25])
+
       ->addDisplayGroup(['libelle',
                          'url',
                          'telephone',
@@ -76,8 +81,9 @@ class ZendAfi_Form_Admin_Location extends ZendAfi_Form {
                          'ville',
                          'pays',
                          'latitude',
-                         'longitude'],
+                         'longitude',
+                         'zoom'],
                         'lieu',
                         ['legend' => $this->_('Lieu')]);
   }
-}
\ No newline at end of file
+}
diff --git a/library/ZendAfi/View/Helper/MapForLieu.php b/library/ZendAfi/View/Helper/MapForLieu.php
index 9246b20357e..da21f4282c4 100644
--- a/library/ZendAfi/View/Helper/MapForLieu.php
+++ b/library/ZendAfi/View/Helper/MapForLieu.php
@@ -19,26 +19,30 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-class ZendAfi_View_Helper_MapForLieu extends ZendAfi_View_Helper_BaseHelper {
-  public function mapForLieu($lieu, $options = []) {
-    if (!$lieu)
+
+class ZendAfi_View_Helper_MapForLieu extends ZendAfi_View_Helper_BaseHelper
+{
+
+  public function mapForLieu(?Class_Lieu $lieu, array $options = []): string
+  {
+    if ( ! $lieu)
       return '';
 
     $map = Class_Map::newWith($lieu);
 
-    $params = ['zoom' => Class_Map::ZOOM,
-               'size' => '200x200'];
+    $params = ['size' => '200x200'];
 
-    if (is_array($options))
+    if ($options)
       $params = array_merge($params, $options);
 
-    if(!$static_map_url = $map->getStaticMapUrl($params))
+    if ( ! $static_map_url = $map->getStaticMapUrl($params))
       return '';
 
     return $this->view->tagImg($static_map_url,
                                ['class' => sprintf('%s_static_map', $map->getLabel()),
                                 'style' => 'cursor: pointer',
                                 'alt' => $lieu->getLibelle(),
-                                'onclick' => sprintf("window.open(&quot;%s&quot;);return false;", $map->getFullMapUrl())]);
+                                'onclick' => sprintf("window.open(&quot;%s&quot;);return false;",
+                                                     $map->getFullMapUrl())]);
   }
-}
\ No newline at end of file
+}
diff --git a/library/ZendAfi/View/Helper/RenderLieu.php b/library/ZendAfi/View/Helper/RenderLieu.php
index 0f6a48e3475..062cbac2276 100644
--- a/library/ZendAfi/View/Helper/RenderLieu.php
+++ b/library/ZendAfi/View/Helper/RenderLieu.php
@@ -24,7 +24,7 @@ class ZendAfi_View_Helper_RenderLieu extends ZendAfi_View_Helper_BaseHelper {
     if (!$lieu)
       return '';
 
-    return $this->_renderMedia($this->view->mapForLieu($lieu, $map_options),
+    return $this->_renderMedia($this->view->mapForLieu($lieu, $map_options ?? []),
                                $this->_renderTitle($lieu),
                                $this->_renderAddress($lieu));
   }
@@ -55,4 +55,4 @@ class ZendAfi_View_Helper_RenderLieu extends ZendAfi_View_Helper_BaseHelper {
   }
 }
 
-?>
\ No newline at end of file
+?>
diff --git a/tests/application/modules/admin/controllers/LieuControllerTest.php b/tests/application/modules/admin/controllers/LieuControllerTest.php
index da496838797..0c5e09b3bba 100644
--- a/tests/application/modules/admin/controllers/LieuControllerTest.php
+++ b/tests/application/modules/admin/controllers/LieuControllerTest.php
@@ -19,14 +19,17 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
  */
 
-abstract class LieuControllerTestCase extends AbstractControllerTestCase {
-  protected $_storm_default_to_volatile = true;
-  public function setUp() {
+
+abstract class LieuControllerTestCase extends AbstractControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
 
     Class_AdminVar::set('ACTIVITY', 0);
 
-    $this->afi_annecy = $this->fixture('Class_Lieu',
+    $this->afi_annecy = $this->fixture(Class_Lieu::class,
                                        ['id' => 3,
                                         'libelle' => 'AFI Annecy',
                                         'adresse' => '11, boulevard du fier',
@@ -34,7 +37,7 @@ abstract class LieuControllerTestCase extends AbstractControllerTestCase {
                                         'ville' => 'Annecy',
                                         'pays' => 'France']);
 
-    $this->afi_lognes = $this->fixture('Class_Lieu',
+    $this->afi_lognes = $this->fixture(Class_Lieu::class,
                                        ['id' => 5,
                                         'libelle' => 'AFI Lognes',
                                         'adresse' => '35, rue de la Maison Rouge',
@@ -49,57 +52,69 @@ abstract class LieuControllerTestCase extends AbstractControllerTestCase {
 
 
 
-class LieuControllerListTest extends LieuControllerTestCase {
-  public function setUp() {
+class LieuControllerListTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
-    $this->dispatch('/admin/lieu', true);
+
+    $this->dispatch('/admin/lieu');
   }
 
 
   /** @test */
-  public function linktoUpdateCoordinatesShouldBePresent() {
+  public function linktoUpdateCoordinatesShouldBePresent()
+  {
     $this->assertXPath('//button[contains(@onclick, "/admin/lieu/update-coordinates")]');
   }
 
 
   /** @test */
-  public function tableShouldContainsAnnecy() {
+  public function tableShouldContainsAnnecy()
+  {
     $this->assertXPathContentContains('//tbody//tr[1]//td', 'AFI Annecy');
   }
 
 
   /** @test */
-  public function tableShouldContainsLognes() {
+  public function tableShouldContainsLognes()
+  {
     $this->assertXPathContentContains('//tbody//tr[2]//td', 'AFI Lognes');
   }
 
 
   /** @test */
-  public function annecyShouldHaveLinkToEdit() {
+  public function annecyShouldHaveLinkToEdit()
+  {
     $this->assertXPath('//tbody//tr[1]//a[contains(@href, "lieu/edit/id/3")]');
   }
 
 
   /** @test */
-  public function annecyShouldHaveLinkToDelete() {
+  public function annecyShouldHaveLinkToDelete()
+  {
     $this->assertXPath('//tbody//tr[1]//a[contains(@href, "lieu/delete/id/3")]');
   }
 
 
   /** @test */
-  public function titreShouldBeLieux() {
+  public function titreShouldBeLieux()
+  {
     $this->assertXPathContentContains('//h1', 'Lieux');
   }
 
 
   /** @test */
-  public function pageShouldContainsButtonToCreateLieu() {
+  public function pageShouldContainsButtonToCreateLieu()
+  {
     $this->assertXPathContentContains('//button[contains(@onclick, "lieu/add")]', 'Créer un lieu');
   }
 
 
   /** @test */
-  function menuGaucheAdminShouldContainsLinkToLieu() {
+  function menuGaucheAdminShouldContainsLinkToLieu()
+  {
     $this->assertXPathContentContains('//div[@class="menuGaucheAdmin"]//a[contains(@href,"admin/lieu")]',
                                       "Lieux");
   }
@@ -108,84 +123,109 @@ class LieuControllerListTest extends LieuControllerTestCase {
 
 
 
-class LieuControllerAddTest extends LieuControllerTestCase {
-  public function setUp() {
+class LieuControllerAddTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
-    $this->dispatch('/admin/lieu/add', true);
+
+    $this->dispatch('/admin/lieu/add');
   }
 
 
   /** @test */
-  public function titreShouldBeDeclarerUnNouveauLieu() {
+  public function titreShouldBeDeclarerUnNouveauLieu()
+  {
     $this->assertXPathContentContains('//h1', 'un nouveau lieu');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForLibelle() {
+  public function formShouldContainsInputForLibelle()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="libelle"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForMail() {
+  public function formShouldContainsInputForMail()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="mail"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForTelephone() {
+  public function formShouldContainsInputForTelephone()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="telephone"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForLatitude() {
+  public function formShouldContainsInputForLatitude()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="latitude"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForLongitude() {
+  public function formShouldContainsInputForLongitude()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="longitude"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForUrl() {
+  public function formShouldContainsInputForUrl()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="url"]');
   }
 
 
   /** @test */
-  public function formShouldContainsTextAreaForAdresse() {
+  public function formShouldContainsTextAreaForAdresse()
+  {
     $this->assertXPath('//form//textarea[@name="adresse"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForCodePostal() {
+  public function formShouldContainsInputForCodePostal()
+  {
     $this->assertXPath('//form//input[@name="code_postal"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForVille() {
+  public function formShouldContainsInputForVille()
+  {
     $this->assertXPath('//form//input[@name="ville"]');
   }
 
 
   /** @test */
-  public function formShouldContainsInputForPaysWithDefaultFrance() {
+  public function formShouldContainsInputForPaysWithDefaultFrance()
+  {
     $this->assertXPath('//form//input[@name="pays"][@value="FRANCE"]');
   }
+
+
+  /** @test */
+  public function formShouldContainsInputForZoomWithDefault_15()
+  {
+    $this->assertXPath('//form//input[@name="zoom"][@value="15"]');
+  }
 }
 
 
 
 
-class LieuControllerPostNewLieuTest extends LieuControllerTestCase {
-  public function setUp() {
+class LieuControllerPostNewLieuTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
 
     $osm_json_url = 'https://nominatim.openstreetmap.org/search?street=28+avenue+du+stade&city=Annecy&postalcode=74000&country=France&format=json&limit=1';
@@ -202,44 +242,51 @@ class LieuControllerPostNewLieuTest extends LieuControllerTestCase {
                          'adresse' => '28 avenue du stade',
                          'code_postal' => '74000',
                          'ville' => 'Annecy',
-                         'pays' => 'France']);
+                         'pays' => 'France',
+                         'zoom' => 15]);
 
     $this->new_lieu = Class_Lieu::findFirstBy(['order' => 'id desc']);
   }
 
 
   /** @test */
-  public function lieuShouldBeCreatedWithMJCDesRomains() {
+  public function lieuShouldBeCreatedWithMJCDesRomains()
+  {
     $this->assertEquals('MJC des Romains', $this->new_lieu->getLibelle());
   }
 
 
   /** @test */
-  public function adresseShouldBe28AvenueStade() {
+  public function adresseShouldBe28AvenueStade()
+  {
     $this->assertEquals('28 avenue du stade', $this->new_lieu->getAdresse());
   }
 
 
   /** @test */
-  public function codePostalShouldReturn74000() {
+  public function codePostalShouldReturn74000()
+  {
     $this->assertEquals('74000', $this->new_lieu->getCodePostal());
   }
 
 
   /** @test */
-  public function answerShouldRetirectToLieuEditId6() {
+  public function answerShouldRetirectToLieuEditId6()
+  {
     $this->assertRedirectTo(Class_Url::absolute('/admin/lieu/edit/id/6'));
   }
 
 
   /** @test */
-  public function latitudeShouldBe45Dot91() {
+  public function latitudeShouldBe45Dot91()
+  {
     $this->assertEquals('45.9102916', $this->new_lieu->getLatitude());
   }
 
 
   /** @test */
-  public function longtudeShouldBe6Dot12() {
+  public function longtudeShouldBe6Dot12()
+  {
     $this->assertEquals('6.1223875', $this->new_lieu->getLongitude());
   }
 }
@@ -247,107 +294,175 @@ class LieuControllerPostNewLieuTest extends LieuControllerTestCase {
 
 
 
-class LieuControllerEditAnnecyTest extends LieuControllerTestCase {
-  public function setUp() {
+class LieuControllerEditAnnecyTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
-    $this->dispatch('/admin/lieu/edit/id/3', true);
+
+    $this->dispatch('/admin/lieu/edit/id/3');
   }
 
 
   /** @test */
-  public function titreShouldBeModificationDuLieuAfiAnnecy() {
+  public function titreShouldBeModificationDuLieuAfiAnnecy()
+  {
     $this->assertXPathContentContains('//h1', 'Modifier le lieu "AFI Annecy"');
   }
 
 
   /** @test */
-  public function formInputLibelleShouldContainsAnnecy() {
+  public function formInputLibelleShouldContainsAnnecy()
+  {
     $this->assertXPath('//form[@method="post"]//input[@name="libelle"][@value="AFI Annecy"]');
   }
 
 
   /** @test */
-  public function adresseShouldContains11BoulevardDuFier() {
+  public function adresseShouldContains11BoulevardDuFier()
+  {
     $this->assertXPathContentContains('//textarea[@name="adresse"]', '11, boulevard du fier');
   }
 
 
   /** @test */
-  public function codePostalInputShouldContains74000() {
+  public function codePostalInputShouldContains74000()
+  {
     $this->assertXPath('//input[@name="code_postal"][@value="74000"]');
   }
 
 
   /** @test */
-  public function villeShouldContainsAnnecy() {
+  public function villeShouldContainsAnnecy()
+  {
     $this->assertXPath('//input[@name="ville"][@value="Annecy"]');
   }
 }
 
 
 
-class LieuControllerEditLognesTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerEditLognesTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
+
     $this->dispatch('/admin/lieu/edit/id/5');
   }
 
 
   /** @test */
-  public function latitudeShouldBe48() {
+  public function latitudeShouldBe48()
+  {
     $this->assertXPath('//input[@name="latitude"][@value="48,825853"]');
   }
 
 
   /** @test */
-  public function longitudeShouldBe2() {
+  public function longitudeShouldBe2()
+  {
     $this->assertXPath('//input[@name="longitude"][@value="2,630163"]');
   }
 
 
   /** @test */
-  public function pageShouldContainsStaticImage() {
-    $this->assertXPath('//img[@src="https://smap.afi-sa.net/staticmap.php?center=48%2C825853%2C2%2C630163&markers=48%2C825853%2C2%2C630163&zoom=15&size=200x200"]',
-                       $this->_response->getBody());
+  public function pageShouldContainsStaticImage()
+  {
+    $this->assertXPath('//img[@src="https://smap.afi-sa.net/staticmap.php?center=48%2C825853%2C2%2C630163&markers=48%2C825853%2C2%2C630163&zoom=15&size=200x200"]');
   }
 }
 
 
 
 
+class LieuControllerEditZoomTest extends LieuControllerTestCase
+{
+
+  /** @test */
+  public function lieuZoom_14_pageShouldContainsStaticImageWithZoom_14()
+  {
+    Class_Lieu::find(5)->setZoom(14)->assertSave();
+
+    $this->dispatch('/admin/lieu/edit/id/5');
+
+    $this->assertXPath('//img[@class="osm_static_map"][@src="https://smap.afi-sa.net/staticmap.php?center=48%2C825853%2C2%2C630163&markers=48%2C825853%2C2%2C630163&zoom=14&size=200x200"]');
+  }
+
+
+  /** @test */
+  public function lieuZoom_20_pageShouldContainsStaticImageWithDefaultZoom_15()
+  {
+    Class_Lieu::find(5)->setZoom(20)->assertSave();
+
+    $this->dispatch('/admin/lieu/edit/id/5');
+
+    $this->assertXPath('//img[@class="osm_static_map"][@src="https://smap.afi-sa.net/staticmap.php?center=48%2C825853%2C2%2C630163&markers=48%2C825853%2C2%2C630163&zoom=15&size=200x200"]');
+  }
+
+
+  /** @test */
+  public function lieuZoom_10_pageShouldContainsStaticImageWithDefaultZoom_15()
+  {
+    Class_Lieu::find(5)->setZoom(9)->assertSave();
+
+    $this->dispatch('/admin/lieu/edit/id/5');
+
+    $this->assertXPath('//img[@class="osm_static_map"][@src="https://smap.afi-sa.net/staticmap.php?center=48%2C825853%2C2%2C630163&markers=48%2C825853%2C2%2C630163&zoom=15&size=200x200"]');
+  }
+}
+
 
 
-class LieuControllerPostLieuAnnecyTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerPostLieuAnnecyTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
 
     $this->postDispatch('/admin/lieu/edit/id/3',
                         ['libelle' => 'Agence Francaise Informatique',
                          'latitude' => '45.798789',
-                         'longitude' => '6.456123']);
+                         'longitude' => '6.456123',
+                         'zoom' => 14]);
   }
 
 
   /** @test */
-  public function latitudeShouldNotBeUpdatedByOSM() {
+  public function latitudeShouldNotBeUpdatedByOSM()
+  {
     $this->assertEquals('45.798789', $this->afi_annecy->getLatitude());
   }
 
 
   /** @test */
-  public function longitudeShouldNotBeUpdatedByOSM() {
+  public function longitudeShouldNotBeUpdatedByOSM()
+  {
     $this->assertEquals('6.456123', $this->afi_annecy->getLongitude());
   }
 
 
   /** @test */
-  public function libelleAnnecyShouldBeAgenceFrancaise() {
+  public function libelleAnnecyShouldBeAgenceFrancaise()
+  {
     $this->assertEquals('Agence Francaise Informatique', $this->afi_annecy->getLibelle());
   }
 
 
   /** @test */
-  public function lieuAnnecyShouldHaveBeenSaved() {
+  public function zoomAnnecyShouldBe_14()
+  {
+    $this->assertEquals(14, $this->afi_annecy->getZoom());
+  }
+
+
+  /** @test */
+  public function lieuAnnecyShouldHaveBeenSaved()
+  {
     $this->assertEquals($this->afi_annecy, Class_Lieu::find(3));
   }
 }
@@ -355,8 +470,11 @@ class LieuControllerPostLieuAnnecyTest extends LieuControllerTestCase {
 
 
 
-class LieuControllerDeleteAnnecyTest extends LieuControllerTestCase {
-  public function setUp() {
+class LieuControllerDeleteAnnecyTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
 
     $this->dispatch('/admin/lieu/delete/id/3');
@@ -364,13 +482,15 @@ class LieuControllerDeleteAnnecyTest extends LieuControllerTestCase {
 
 
   /** @test */
-  public function lieuShouldHaveBeenDeleted() {
+  public function lieuShouldHaveBeenDeleted()
+  {
     $this->assertNull(Class_Lieu::find(3));
   }
 
 
   /** @test */
-  public function answerShouldRetirectToLieuEditId90() {
+  public function answerShouldRetirectToLieuEditId90()
+  {
     $this->assertRedirectTo('/admin/lieu/index');
   }
 }
@@ -378,51 +498,66 @@ class LieuControllerDeleteAnnecyTest extends LieuControllerTestCase {
 
 
 
-class LieuControllerPostTestErrors extends AbstractControllerTestCase {
-  protected $_storm_default_to_volatile = true;
+class LieuControllerPostTestErrors extends AbstractControllerTestCase
+{
 
-  public function setUp() {
+  public function setUp()
+  {
     parent::setUp();
+
     $this->postDispatch('/admin/lieu/add',
                         ['libelle' => '',
-                         'mail' => 'glurps']);
+                         'mail' => 'glurps',
+                         'zoom' => 15]);
   }
 
 
   /** @test */
-  function saveShouldNotBeCalledIfLibelleEmpty() {
+  function saveShouldNotBeCalledIfLibelleEmpty()
+  {
     $this->assertEmpty(Class_Lieu::findAll());
   }
 
 
   /** @test */
-  function responseShouldNotRedirect() {
+  function responseShouldNotRedirect()
+  {
     $this->assertNotRedirect();
   }
 
+
   /** @test */
-  function errorShouldDisplayUneValeurEstRequise() {
+  function errorShouldDisplayUneValeurEstRequise()
+  {
     $this->assertXPathContentContains('//ul[@class="errors"]//li', "Une valeur est requise");
   }
 
+
   /** @test */
-  function errorShouldDisplayMailInvalide() {
-    $this->assertXPathContentContains('//ul[@class="errors"]//li', "'glurps' n'est pas un email valide", $this->_response->getBody());
+  function errorShouldDisplayMailInvalide()
+  {
+    $this->assertXPathContentContains('//ul[@class="errors"]//li',
+                                      "'glurps' n'est pas un email valide");
   }
 }
 
 
 
-class LieuControllerErrorsFindTest extends LieuControllerTestCase {
+
+class LieuControllerErrorsFindTest extends LieuControllerTestCase
+{
+
   /** @test */
-  public function lieuNotFoundOnEditShouldRedirectToIndex() {
+  public function lieuNotFoundOnEditShouldRedirectToIndex()
+  {
     $this->dispatch('/admin/lieu/edit/id/999999999999');
     $this->assertRedirectTo('/admin/lieu/index');
   }
 
 
   /** @test */
-  public function lieuNotFoundOnDeleteShouldRedirectToIndex() {
+  public function lieuNotFoundOnDeleteShouldRedirectToIndex()
+  {
     $this->dispatch('/admin/lieu/delete/id/999999999999');
     $this->assertRedirectTo('/admin/lieu/index');
   }
@@ -430,8 +565,12 @@ class LieuControllerErrorsFindTest extends LieuControllerTestCase {
 
 
 
-class LieuControllerPostNewLieuWithUnknowAdressTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerPostNewLieuWithUnknowAdressTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
 
     $osm_json_url = 'https://nominatim.openstreetmap.org/search?street=28+avenue+du+stade&city=Annecy&postalcode=74000&country=France&format=json&limit=1';
@@ -446,81 +585,108 @@ class LieuControllerPostNewLieuWithUnknowAdressTest extends LieuControllerTestCa
                          'adresse' => '28 avenue du stade',
                          'code_postal' => '74000',
                          'ville' => 'Annecy',
-                         'pays' => 'France']);
+                         'pays' => 'France',
+                         'zoom' => 15]);
 
     $this->new_lieu = Class_Lieu::findFirstBy(['order' => 'id desc']);
   }
 
 
   /** @test */
-  public function notificationShouldContainsCoordinatesNotFound() {
+  public function notificationShouldContainsCoordinatesNotFound()
+  {
     $this->assertFlashMessengerContentContains('Pas de coordonnées (latitude, longitude) trouvées pour l\'adresse fournie. Merci de remplir manuellement');
   }
 }
 
 
 
-class LieuControllerExportCsvActionTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerExportCsvActionTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
-    $this->dispatch('/admin/lieu/export-csv', true);
+
+    $this->dispatch('/admin/lieu/export-csv');
   }
 
 
   /** @test */
-  public function annecyShouldBeInCsv() {
-    $this->assertContains('"AFI Annecy";"11, boulevard du fier";Annecy;74000', $this->_response->getBody());
+  public function annecyShouldBeInCsv()
+  {
+    $this->assertContains('"AFI Annecy";"11, boulevard du fier";Annecy;74000',
+                          $this->_response->getBody());
   }
 }
 
 
 
-class LieuControllerUpdateCoordinatesActionTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerUpdateCoordinatesActionTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
-    $this->dispatch('/admin/lieu/update-coordinates', true);
+
+    $this->dispatch('/admin/lieu/update-coordinates');
   }
 
 
   /** @test */
-  public function annecyShouldBeInCsv() {
+  public function annecyShouldBeInCsv()
+  {
     $this->assertXPathContentContains('//script', '/admin/lieu/update-coordinates-for/id/3');
   }
 }
 
 
 
-class LieuControllerUpdateCoordinatesForLocationActionTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerUpdateCoordinatesForLocationActionTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
+
     Class_WebService_OpenStreetMap::setDefaultHttpClient($this->mock()
                                                          ->whenCalled('open_url')
                                                          ->answers('[]'));
 
-    $this->dispatch('/admin/lieu/update-coordinates-for/id/3', true);
+    $this->dispatch('/admin/lieu/update-coordinates-for/id/3');
   }
 
 
   /** @test */
-  public function annecyShouldBeInCsv() {
+  public function annecyShouldBeInCsv()
+  {
     $this->assertXPathContentContains('//li', 'AFI Annecy<br/>11, boulevard du fier<br/>Annecy<br/>74000<br/>France<br/>');
   }
 }
 
 
 
-class LieuControllerStaticMapOsmTest extends LieuControllerTestCase {
-  public function setUp() {
+
+class LieuControllerStaticMapOsmTest extends LieuControllerTestCase
+{
+
+  public function setUp()
+  {
     parent::setUp();
+
     $this->afi_annecy->setLatitude('45.798789')
                      ->setLongitude('6.456123');
-    $this->dispatch('/admin/lieu/edit/id/3', true);
+
+    $this->dispatch('/admin/lieu/edit/id/3');
   }
 
 
   /** @test */
-  public function pageShouldContainsStaticOSMImage() {
-    $this->assertXPath('//img[@src="https://smap.afi-sa.net/staticmap.php?center=45.798789%2C6.456123&markers=45.798789%2C6.456123&zoom=15&size=200x200"]',
-                       $this->_response->getBody());
+  public function pageShouldContainsStaticOSMImage()
+  {
+    $this->assertXPath('//img[@src="https://smap.afi-sa.net/staticmap.php?center=45.798789%2C6.456123&markers=45.798789%2C6.456123&zoom=15&size=200x200"]');
   }
-}
\ No newline at end of file
+}
diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php
index 23c78447f1e..054eec723a4 100644
--- a/tests/db/UpgradeDBTest.php
+++ b/tests/db/UpgradeDBTest.php
@@ -5894,3 +5894,22 @@ class UpgradeDB_463_Test extends UpgradeDBTestCase
     $this->assertEquals($id_origine, Class_Bib::find($id)->getIdOrigine());
   }
 }
+
+
+
+
+class UpgradeDB_464_Test extends UpgradeDBTestCase
+{
+
+  public function prepare()
+  {
+    $this->silentQuery('ALTER TABLE `lieux` DROP COLUMN IF EXISTS `zoom`;');
+  }
+
+
+  /** @test */
+  public function tableLieuxShouldContainsNewColumnZoomInt_3()
+  {
+    $this->assertFieldType('lieux', 'zoom', 'int(3)');
+  }
+}
diff --git a/tests/library/Class/LieuTest.php b/tests/library/Class/LieuTest.php
index c9e7a7aa1a3..324f0017958 100644
--- a/tests/library/Class/LieuTest.php
+++ b/tests/library/Class/LieuTest.php
@@ -35,6 +35,11 @@ class LieuTest extends ModelTestCase {
                       ->setLibelle('ici')
                       ->isValid());
   }
-}
 
-?>
\ No newline at end of file
+
+  /** @test */
+  public function newLiexShouldHaveDefaultZoom_15()
+  {
+    $this->assertEquals(15, Class_Lieu::getLoader()->newInstanceWithId(4)->getZoom());
+  }
+}
diff --git a/tests/scenarios/RendezVous/UsergroupAgendaAdminTest.php b/tests/scenarios/RendezVous/UsergroupAgendaAdminTest.php
index 7e2e58f043e..2d987d142da 100644
--- a/tests/scenarios/RendezVous/UsergroupAgendaAdminTest.php
+++ b/tests/scenarios/RendezVous/UsergroupAgendaAdminTest.php
@@ -478,7 +478,8 @@ class UsergroupAgendaAdminAllSearchTest extends UsergroupAgendaAdminModoPortailL
   /** @test */
   public function userContentShouldFilterByArcadiaUserGroup() {
     $this->dispatchWithQuery(['search_user' => 'Arcadia']);
-    $this->assertSqlEquals(["SELECT `bib_admin_users`.`id` FROM `bib_admin_users` WHERE (`bib_admin_users`.`id_user` IN (999) AND (`bib_admin_users`.`login` LIKE '%Arcadia%' OR `bib_admin_users`.`nom` LIKE '%Arcadia%' OR `bib_admin_users`.`prenom` LIKE '%Arcadia%' OR `bib_admin_users`.`pseudo` LIKE '%Arcadia%' OR `bib_admin_users`.`mail` LIKE '%Arcadia%' OR `bib_admin_users`.`idabon` LIKE '%Arcadia%'))",
+    $this->assertSqlEquals(["SELECT `lieux`.`id`, `lieux`.`libelle` FROM `lieux` ORDER BY `lieux`.`libelle` ASC",
+                            "SELECT `bib_admin_users`.`id` FROM `bib_admin_users` WHERE (`bib_admin_users`.`id_user` IN (999) AND (`bib_admin_users`.`login` LIKE '%Arcadia%' OR `bib_admin_users`.`nom` LIKE '%Arcadia%' OR `bib_admin_users`.`prenom` LIKE '%Arcadia%' OR `bib_admin_users`.`pseudo` LIKE '%Arcadia%' OR `bib_admin_users`.`mail` LIKE '%Arcadia%' OR `bib_admin_users`.`idabon` LIKE '%Arcadia%'))",
                             "SELECT `user_groups`.* FROM `user_groups` WHERE (`user_groups`.`id` IN (43))",
                             "SELECT `rendez_vous`.* FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-03-27' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-06-27' AND `rendez_vous`.`group_id` IN (43)) ORDER BY `rendez_vous`.`date` ASC, `rendez_vous`.`begin_time` ASC LIMIT 20",
                             "SELECT COUNT(*) AS `numberof` FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-03-27' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-06-27' AND `rendez_vous`.`group_id` IN (43) AND `rendez_vous`.`group_id` IN (43))"]);
@@ -523,7 +524,8 @@ class UsergroupAgendaAdminAllSearchTest extends UsergroupAgendaAdminModoPortailL
   /** @test */
   public function withLibelleAgendaSuperShouldQueryOnLibelleLikeSuper() {
     $this->dispatchWithQuery(['search_libelle'=>'super']);
-    $this->assertSqlEquals(["SELECT `user_groups`.* FROM `user_groups` WHERE (`user_groups`.`libelle` LIKE '%super%')",
+    $this->assertSqlEquals(["SELECT `lieux`.`id`, `lieux`.`libelle` FROM `lieux` ORDER BY `lieux`.`libelle` ASC",
+                            "SELECT `user_groups`.* FROM `user_groups` WHERE (`user_groups`.`libelle` LIKE '%super%')",
                             "SELECT `rendez_vous`.* FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-03-27' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-06-27' AND `rendez_vous`.`group_id` IN (43)) ORDER BY `rendez_vous`.`date` ASC, `rendez_vous`.`begin_time` ASC LIMIT 20",
                             "SELECT COUNT(*) AS `numberof` FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-03-27' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-06-27' AND `rendez_vous`.`group_id` IN (43) AND `rendez_vous`.`group_id` IN (43))"]);
   }
@@ -1178,9 +1180,8 @@ class UsergroupAgendaAdminSearchCustomFieldsMultiTest
 
   /** @test */
   public function requestShouldExists() {
-    $this->assertSqlEquals(["SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` IN ('included') AND `custom_field_values`.`custom_field_id` = 10)",
-                            "SELECT `rendez_vous`.* FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-01-20' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-04-20' AND `rendez_vous`.`id` IN (5)) LIMIT 20",
-                            "SELECT COUNT(*) AS `numberof` FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-01-20' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-04-20' AND `rendez_vous`.`id` IN (5))"]);
+    $this->assertSql("SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` IN ('included') AND `custom_field_values`.`custom_field_id` = 10)");
+    $this->assertSql("SELECT `rendez_vous`.* FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-01-20' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-04-20' AND `rendez_vous`.`id` IN (5)) LIMIT 20");
   }
 }
 
@@ -1249,7 +1250,8 @@ class UsergroupAgendaAdminSearchCustomFieldsSelectWithoutResultTest
 
   /** @test */
   public function requestShouldExists() {
-    $this->assertSqlEquals(["SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` IN ('fromage', 'dessert', 'l'association') AND `custom_field_values`.`custom_field_id` = 3)"]);
+    $this->assertSqlEquals(["SELECT `lieux`.`id`, `lieux`.`libelle` FROM `lieux` ORDER BY `lieux`.`libelle` ASC",
+                            "SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` IN ('fromage', 'dessert', 'l'association') AND `custom_field_values`.`custom_field_id` = 3)"]);
   }
 }
 
@@ -1300,7 +1302,8 @@ class UsergroupAgendaAdminSearchCustomFieldOfAgendaTest
 
   /** @test */
   public function requestShouldExists() {
-    $this->assertSqlEquals(["SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` LIKE '%nord%' AND `custom_field_values`.`custom_field_id` = 5)",
+    $this->assertSqlEquals(["SELECT `lieux`.`id`, `lieux`.`libelle` FROM `lieux` ORDER BY `lieux`.`libelle` ASC",
+                            "SELECT `custom_field_values`.* FROM `custom_field_values` WHERE (`custom_field_values`.`value` LIKE '%nord%' AND `custom_field_values`.`custom_field_id` = 5)",
                             "SELECT `user_groups`.* FROM `user_groups` WHERE (`user_groups`.`id` IN (43))",
                             "SELECT `rendez_vous`.* FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-01-20' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-04-20' AND `rendez_vous`.`group_id` IN (43)) LIMIT 20",
                             "SELECT COUNT(*) AS `numberof` FROM `rendez_vous` WHERE (LEFT(`rendez_vous`.`date`, 10) >= '2019-01-20' AND LEFT(`rendez_vous`.`date`, 10) <= '2019-04-20' AND `rendez_vous`.`group_id` IN (43) AND `rendez_vous`.`group_id` IN (43))"]);
-- 
GitLab