diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index abddb22ebf107f60b85a22d45dffc2de954dab09..b046936fe736f950530aef74c32448f2e4a2f96e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,9 +17,10 @@ variables:
   GIT_DEPTH: 0
 
 stages:
+  - quality
   - build
 
-build:
+build-dev-master:
   image: docker:18.09
   services:
     - docker:18.09-dind
@@ -32,3 +33,62 @@ build:
     - dev
     - master
 
+build-mr:
+  image: docker:18.09
+  services:
+    - docker:18.09-dind
+  stage: build
+  script:
+    - docker build .
+  only:
+    - merge_requests
+
+sonarqube:
+  stage: quality
+  only:
+    - dev
+  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/sonarsource/sonar-scanner-cli:4
+  variables:
+    SONAR_USER_HOME: '${CI_PROJECT_DIR}/.sonar' # Defines the location of the analysis task cache
+    GIT_DEPTH: '0' # T
+  cache:
+    key: '${CI_JOB_NAME}'
+    paths:
+      - .sonar/cache
+  script:
+    - >
+      sonar-scanner
+      -Dsonar.projectName=ecolyo-agent-server
+      -Dsonar.projectVersion=1.0
+      -Dsonar.sourceEncoding=UTF-8
+      -Dsonar.projectBaseDir=.
+      -Dsonar.host.url=${SONAR_URL}
+      -Dsonar.projectKey=ecolyo-agent-server
+      -Dsonar.login=${SONAR_TOKEN}
+      -Dsonar.cpd.exclusions=internal/mocks/**,internal/**/*_test.go
+      -Dsonar.qualitygate.wait=true
+
+sonarqube:
+  stage: quality
+  only:
+    - merge_requests
+  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/sonarsource/sonar-scanner-cli:4
+  variables:
+    SONAR_USER_HOME: '${CI_PROJECT_DIR}/.sonar' # Defines the location of the analysis task cache
+    GIT_DEPTH: '0' # T
+  cache:
+    key: '${CI_JOB_NAME}'
+    paths:
+      - .sonar/cache
+  script:
+    - >
+      sonar-scanner
+      -Dsonar.projectName=ecolyo-agent-server-mr
+      -Dsonar.projectVersion=1.0
+      -Dsonar.sourceEncoding=UTF-8
+      -Dsonar.projectBaseDir=.
+      -Dsonar.host.url=${SONAR_URL}
+      -Dsonar.projectKey=ecolyo-agent-server-mr
+      -Dsonar.login=${SONAR_MR_TOKEN}
+      -Dsonar.cpd.exclusions=internal/mocks/**,internal/**/*_test.go
+      -Dsonar.qualitygate.wait=true
diff --git a/internal/models/consent.go b/internal/models/consent.go
index dfe09b744f69739b20a6f275c08a67fd5da6839b..59d456a7e7ebefdc2be2e0d6e699ad4774f2dc10 100644
--- a/internal/models/consent.go
+++ b/internal/models/consent.go
@@ -3,7 +3,6 @@ package models
 import (
 	"encoding/json"
 	"errors"
-	"fmt"
 	"log"
 	"net/http"
 	"time"
@@ -152,7 +151,7 @@ func (dh *DataHandler) DeleteConsentById(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	// Update and save consent
+	// Update and save consent in MySQL
 	consent.EndDate = time.Now()
 	err = dh.sqlClient.Save(&consent).Error
 	if err != nil {
@@ -163,6 +162,16 @@ func (dh *DataHandler) DeleteConsentById(w http.ResponseWriter, r *http.Request)
 
 	dh.sqlClient.Delete(&consent)
 
+	// Update and save consent in Meilisearch
+	deletedConsent := []map[string]interface{}{
+		{
+			"ID":        consent.ID,
+			"endDate":   consent.EndDate,
+			"DeletedAt": consent.DeletedAt,
+		},
+	}
+	dh.meiliClient.Index("consents").UpdateDocuments(deletedConsent)
+
 	log.Printf("| deleted consent | id : %d | %v", id, r.RemoteAddr)
 }
 
@@ -180,10 +189,17 @@ func (dh *DataHandler) SearchConsent(w http.ResponseWriter, r *http.Request) {
 
 		hits, err := json.Marshal(resp.Hits)
 		if err != nil {
-			fmt.Println("error:", err)
+			http.Error(w, "error when marshal hits", http.StatusInternalServerError)
+			log.Println(err.Error())
+			return
 		}
 		var consents []Consent
 		err = json.Unmarshal(hits, &consents)
+		if err != nil {
+			http.Error(w, "error when unmarshal hits", http.StatusInternalServerError)
+			log.Println(err.Error())
+			return
+		}
 
 		w.Header().Set("Content-Type", "application/json")
 		json.NewEncoder(w).Encode(consents)
diff --git a/internal/models/customPopup.go b/internal/models/customPopup.go
index db4aa73c1d4e17a13fbc906664e4f639152f1b27..7fa78b1833bb51c7b4c2939148cd4e19f216fe9c 100644
--- a/internal/models/customPopup.go
+++ b/internal/models/customPopup.go
@@ -5,15 +5,17 @@ import (
 	"errors"
 	"log"
 	"net/http"
+	"time"
 
 	"gorm.io/gorm"
 )
 
 type CustomPopup struct {
-	ID           uint   `gorm:"<-:create"`
-	PopupEnabled bool   `json:"popupEnabled"`
-	Title        string `json:"title"`
-	Description  string `json:"description"`
+	ID           uint      `gorm:"<-:create"`
+	PopupEnabled bool      `json:"popupEnabled"`
+	Title        string    `json:"title"`
+	Description  string    `json:"description"`
+	EndDate      time.Time `json:"endDate"`
 }
 
 // GetCustomPopup godoc
@@ -74,6 +76,7 @@ func (dh *DataHandler) SaveCustomPopup(w http.ResponseWriter, r *http.Request) {
 	updatedCustomPopup.PopupEnabled = customPopup.PopupEnabled
 	updatedCustomPopup.Title = customPopup.Title
 	updatedCustomPopup.Description = customPopup.Description
+	updatedCustomPopup.EndDate = customPopup.EndDate
 
 	dh.sqlClient.Save(&updatedCustomPopup)
 
diff --git a/internal/models/models.go b/internal/models/models.go
index 35d828b99568bb548f257abd1078e814b3ad2101..73adcd0c8a891de62a45ea409a73e4e1a3da2887 100644
--- a/internal/models/models.go
+++ b/internal/models/models.go
@@ -1,6 +1,7 @@
 package models
 
 import (
+	"errors"
 	"fmt"
 
 	"forge.grandlyon.com/web-et-numerique/llle_project/backoffice-server/internal/common"
@@ -30,13 +31,13 @@ func NewDataHandler() *DataHandler {
 	if dbUser == "" || dbPassword == "" || dbName == "" {
 		sqlClient, err = gorm.Open(sqlite.Open("backoffice.db"), &gorm.Config{})
 		if err != nil {
-			panic("failed to connect database")
+			panic("failed to connect sqlite database")
 		}
 	} else {
 		dsn := fmt.Sprintf("%v:%v@tcp(%v:3306)/%v?charset=utf8mb4&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbName)
 		sqlClient, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
 		if err != nil {
-			panic("failed to connect database")
+			panic("failed to connect MySQL database")
 		}
 	}
 
@@ -50,21 +51,30 @@ func NewDataHandler() *DataHandler {
 	sqlClient.AutoMigrate(&Price{})
 	sqlClient.AutoMigrate(&Consent{})
 
-	//TODO fix recreate on server init
-	// Create default partner status
-	sqlClient.Create(&PartnersInfo{
-		GRDFFailure:           false,
-		EnedisFailure:         false,
-		EGLFailure:            false,
-		NotificationActivated: false,
-	})
+	// Check if partners info already exists
+	var partnersInfo PartnersInfo
+	err = sqlClient.First(&partnersInfo).Error
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		// Create default partner status
+		sqlClient.Create(&PartnersInfo{
+			GRDFFailure:           false,
+			EnedisFailure:         false,
+			EGLFailure:            false,
+			NotificationActivated: false,
+		})
+	}
 
-	// Create default custom popup
-	sqlClient.Create(&CustomPopup{
-		PopupEnabled: false,
-		Title:        "",
-		Description:  "",
-	})
+	// Check if custom popup already exists
+	var customPopup CustomPopup
+	err = sqlClient.First(&customPopup).Error
+	if errors.Is(err, gorm.ErrRecordNotFound) {
+		// Create default custom popup
+		sqlClient.Create(&CustomPopup{
+			PopupEnabled: false,
+			Title:        "",
+			Description:  "",
+		})
+	}
 
 	// Meilisearch setup
 	meiliClient := meilisearch.NewClient(meilisearch.ClientConfig{