diff --git a/lang/en.json b/lang/en.json
index e38adf8cbf6e07a3b9097eb3f854c4da2ffaa6e4..3a63143bee4cf44b4954b0d8b292d816deb40534 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -13,7 +13,7 @@
         "annotationAdded": "Annotation added",
         "annotationMode": "Annotation mode",
         "appleStylus": "Apple stylus",
-        "areYouSure": "Are you sure?",
+        "areYouSure": "Do you want to perform this action ?",
         "atRange": "at",
         "attachmentAdded": "Attachment added",
         "attachmentViewed": "Attachment viewed",
@@ -651,6 +651,7 @@
         "groupsToManage": "Choose the authorized assignment groups",
 		"unlinkGroup": "Unlink group",
         "emptyGroups": "No groups available to associate",
+        "errorConvertingDocument": "Error converting document",
         "emptyGroupUsers": "No users associated with this group",
         "emptyUsers": "No users available to associate",
         "can_purgeAdmin": "Logically and physically remove interrupted or terminated signature processes",
diff --git a/lang/fr.json b/lang/fr.json
index 29f85953e3b4068573c38cd672e4b3c1d51cbaea..676c837c78eb23a44cd5a9d4033b28700991ce93 100755
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -13,7 +13,7 @@
 		"annotationAdded"                    : "Annotation ajoutée",
 		"annotationMode"                     : "Mode de l'annotation",
 		"appleStylus"                        : "Stylet Apple",
-		"areYouSure"                         : "Êtes-vous sûr ?",
+		"areYouSure"                         : "Voulez-vous effectuer cette action ?",
 		"atRange"                            : "à",
 		"attachmentAdded"                    : "Pièce jointe ajoutée",
 		"attachmentViewed"                   : "Pièce jointe consulté",
@@ -650,6 +650,7 @@
 		"groupsToManage": "Choisir les groupes d'affectations autorisés",
 		"unlinkGroup": "Dissocier le groupe",
 		"emptyGroups": "Aucun groupe disponible à associer",
+		"errorConvertingDocument": "Erreur lors de la conversion du document",
 		"emptyGroupUsers": "Aucun utilisateur associé à ce groupe",
 		"emptyUsers": "Aucun utilisateur disponible à associer",
 		"can_purgeAdmin": "Supprimer logiquement les processus de signature interrompus ou terminés",
diff --git a/src/app/document/controllers/DocumentController.php b/src/app/document/controllers/DocumentController.php
index caaefab86d466e09085c95dfce038dc31e4009da..bf7a12956b938a04b5ba50b0a864071d8b80ef55 100755
--- a/src/app/document/controllers/DocumentController.php
+++ b/src/app/document/controllers/DocumentController.php
@@ -132,7 +132,7 @@ class DocumentController
 
     public function getById(Request $request, Response $response, array $args)
     {
-        if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'withDeleted' => true]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
+        if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'withDeleted' => true, 'readOnly' => true]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
             return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
         }
 
@@ -230,9 +230,15 @@ class DocumentController
             }
         }
 
-        if (!empty($currentId) && $currentId != $GLOBALS['id']) {
-            $substitute = UserModel::getById(['id' => $currentId, 'select' => ['substitute']]);
-            $formattedDocument['readOnly'] = $GLOBALS['id'] != $substitute['substitute'] && PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents']);
+        $formattedDocument['readOnly'] = !$canManageDocuments;
+        if ($formattedDocument['readOnly'] && !empty($currentId)) {
+            if ($currentId == $GLOBALS['id']) {
+                $formattedDocument['readOnly'] = false;
+            } else {
+                $substitute = UserModel::getById(['id' => $currentId, 'select' => ['substitute']]);
+                $substitute = $substitute['substitute'] ?? null;
+                $formattedDocument['readOnly'] = $GLOBALS['id'] != $substitute;
+            }
         }
 
         $formattedDocument['attachments'] = [];
@@ -387,7 +393,7 @@ class DocumentController
     {
         $canPurge = PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'can_purge']);
         if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])
-            && !DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'withDeleted' => $canPurge])) {
+            && !DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'readOnly' => true, 'withDeleted' => $canPurge])) {
             return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
         }
 
@@ -1161,7 +1167,8 @@ class DocumentController
 
     public function getThumbnailContent(Request $request, Response $response, array $args)
     {
-        if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
+        if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])
+            && !DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'readOnly' => true])) {
             return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
         }
 
@@ -1296,28 +1303,51 @@ class DocumentController
     {
         ValidatorModel::notEmpty($args, ['id', 'userId']);
         ValidatorModel::intVal($args, ['id', 'userId']);
+        ValidatorModel::boolType($args, ['readOnly']);
         ValidatorModel::boolType($args, ['withDeleted']);
 
+        $args['readOnly'] = $args['readOnly'] ?? false;
+        $args['id']       = (int)$args['id'];
+        $args['userId']   = (int)$args['userId'];
         $args['withDeleted'] = $args['withDeleted'] ?? false;
 
         $document = DocumentModel::getById(['select' => ['typist'], 'id' => $args['id'], 'withDeleted' => $args['withDeleted']]);
+
         if (!empty($document['typist']) && $document['typist'] == $GLOBALS['id']) {
             return true;
         }
 
-        $workflow = WorkflowModel::getCurrentStep(['select' => ['user_id'], 'documentId' => $args['id']]);
-        if (empty($workflow) || empty($workflow['user_id'])) {
-            return false;
-        }
-
-        if ($workflow['user_id'] != $args['userId']) {
-            $user = UserModel::getById(['id' => $workflow['user_id'], 'select' => ['substitute']]);
-            if ($user['substitute'] != $args['userId']) {
+        if (!$args['readOnly']) {
+            $currentStep = WorkflowModel::getCurrentStep(['select' => ['user_id'], 'documentId' => $args['id']]);
+            if (empty($currentStep) || empty($currentStep['user_id'])) {
                 return false;
             }
+
+            if ($currentStep['user_id'] == $args['userId']) {
+                return true;
+            } else {
+                $user = UserModel::getById(['id' => $args['userId'], 'select' => ['substitute']]);
+                return $currentStep['user_id'] == $user['substitute'];
+            }
         }
 
-        return true;
+        $canReadOnly = WorkflowModel::get([
+            'select' => [1],
+            'where'  => [
+                'main_document_id = ?',
+                '(process_date IS NOT NULL AND user_id = ?)
+                OR (
+                    user_id IN (
+                        SELECT (SELECT ?::int) UNION (SELECT id FROM users WHERE substitute = ?)
+                    )
+                    AND "order" = (
+                        SELECT min(ws2."order") FROM workflows ws2 WHERE ws2.process_date IS NULL AND ws2.main_document_id = main_document_id
+                    )
+                )'
+            ],
+            'data' => [$args['id'], $args['userId'], $args['userId'], $args['userId']]
+        ]);
+        return !empty($canReadOnly);
     }
 
     public static function getEncodedDocumentFromEncodedZip(array $args)
diff --git a/src/app/history/controllers/HistoryController.php b/src/app/history/controllers/HistoryController.php
index f5e5f2cfee6b4a727355150f9533f2da1faba443..80d1a3abfe77ae89d23cd6db55048537429dfabf 100644
--- a/src/app/history/controllers/HistoryController.php
+++ b/src/app/history/controllers/HistoryController.php
@@ -392,7 +392,8 @@ class HistoryController
             return $response->withStatus(400)->withJson(['errors' => 'Route id is not an integer']);
         }
 
-        if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']]) && !PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])) {
+        if (!PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents'])
+            && !DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id'], 'readOnly' => true])) {
             return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']);
         }
 
diff --git a/src/app/search/controllers/SearchController.php b/src/app/search/controllers/SearchController.php
index dbcb1c03b42701d81511bdd0164b35516999dd0a..9a6d4878e2c26b245aeaf863185a7eb870ab0eee 100755
--- a/src/app/search/controllers/SearchController.php
+++ b/src/app/search/controllers/SearchController.php
@@ -44,16 +44,21 @@ class SearchController
         $data  = [];
         $hasFullRights = PrivilegeController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_documents']);
         if (!$hasFullRights) {
-            $substitutedUsers = UserModel::get(['select' => ['id'], 'where' => ['substitute = ?'], 'data' => [$GLOBALS['id']]]);
-            $users = [$GLOBALS['id']];
-            foreach ($substitutedUsers as $value) {
-                $users[] = $value['id'];
-            }
-
-            $workflowSelect = "SELECT id FROM workflows ws WHERE workflows.main_document_id = main_document_id AND process_date IS NULL AND status IS NULL ORDER BY \"order\" LIMIT 1";
-            $workflowSelect = "SELECT main_document_id FROM workflows WHERE user_id in (?) AND id in ({$workflowSelect})";
-            $where          = ["(id in ({$workflowSelect}) OR typist = ?)"];
-            $data           = [$users, $GLOBALS['id']];
+            $where = ['id IN (
+                SELECT DISTINCT ws1.main_document_id
+                FROM workflows ws1
+                WHERE typist = ?
+                OR (ws1.process_date IS NOT NULL AND ws1.user_id = ?)
+                OR (
+                    ws1.user_id IN (
+                        SELECT (SELECT ?::int) UNION (SELECT id FROM users WHERE substitute = ?)
+                    )
+                    AND ws1."order" = (
+                        SELECT min(ws2."order") FROM workflows ws2 WHERE ws2.process_date IS NULL AND ws2.main_document_id = ws1.main_document_id
+                    )
+                )
+            )'];
+            $data = [$GLOBALS['id'], $GLOBALS['id'], $GLOBALS['id'], $GLOBALS['id']];
         }
 
         $whereWorkflow = [];
diff --git a/src/frontend/app/document/document.component.html b/src/frontend/app/document/document.component.html
index 674e1f790288af2578b8780d2f70ff4aa533c1da..8af9bd6c26e8cc07a626a547ebf7ad0bdb9a73d8 100755
--- a/src/frontend/app/document/document.component.html
+++ b/src/frontend/app/document/document.component.html
@@ -44,10 +44,20 @@
     *ngIf="authService.user.substitute !== null && docList[currentDoc]">
     <ion-label style="font-size: 13px;">{{'lang.substitutionInfo' | translate}}</ion-label>
 </ion-toolbar>
-<ion-toolbar class="ion-text-center" color="danger" *ngIf="mainDocument.id !== 0 && mainDocument.status !== 'READY'">
-    <ion-label style="font-size: 13px;">{{'lang.convertingDocument' | translate}}</ion-label>
+<ion-toolbar class="ion-text-center" color="danger" *ngIf="mainDocument.id !== 0 && mainDocument.status === 'CONVERTING'">
+    <div class="loading" style="display:flex;height:100%;">
+        <ion-label class="loadingMsg">{{'lang.convertingDocument' | translate}}</ion-label>
+        <ion-spinner name="dots" color="light" style="padding-top: 6.5%;"></ion-spinner>
+    </div>
+</ion-toolbar>
+<ion-toolbar class="ion-text-center" color="danger" *ngIf="mainDocument.id !== 0 && mainDocument.status === 'ERROR'">
+    <ion-label style="font-size: 14px; font-weight: bold;">{{'lang.errorConvertingDocument' | translate}}</ion-label>
 </ion-toolbar>
 <ion-content *ngIf="!loadingdocument" #mainContent>
+    <!-- <div *ngIf="mainDocument.status === 'READY'" class="loading" style="display:flex;height:100%;">
+        <ion-spinner name="lines" color="primary" style="margin: auto;"></ion-spinner>
+        <ion-label class="loadingMsg">{{'lang.convertingDocument' | translate}}</ion-label>
+    </div> -->
     <ng-container *ngIf="(mainDocument.notes !== undefined && mainDocument.notes !== null) || hasWorkflowNotes">
         <ion-fab-button *ngIf="!expandedNote" ngDraggable [bounds]="myBounds" [inBounds]="true"
             (movingOffset)="signaturesService.dragging=true" (endOffset)="signaturesService.dragging=false"
@@ -134,7 +144,7 @@
 
 </ion-content>
 <ion-footer *ngIf="!loadingImage && currentDoc === 0" class="ion-no-border footer-buttons" [ngStyle]="{'grid-template-columns': actionsList.length === 2 ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)'}">
-    <ion-button [disabled]="mainDocument.status === 'CONVERTING'" *ngFor="let action of actionsList" [color]="action.color" shape="round" size="large" fill="outline" (click)="launchEvent(action)" style="width: auto;">
+    <ion-button [disabled]="isNotReady()" *ngFor="let action of actionsList" [color]="action.color" shape="round" size="large" fill="outline" (click)="launchEvent(action)" style="width: auto;">
         <ion-icon *ngIf="action.logo !== ''" [slot]="'start'" [name]="action.logo"></ion-icon>
         <ion-label style="font-size: 13px;">{{getLabel(action)}}</ion-label>
     </ion-button>
diff --git a/src/frontend/app/document/document.component.scss b/src/frontend/app/document/document.component.scss
index 5719a22075e6f30b415b521a8942d1bbe251ce66..b1fbe6af75fa406123c136ec8de8051d820f3e60 100644
--- a/src/frontend/app/document/document.component.scss
+++ b/src/frontend/app/document/document.component.scss
@@ -398,7 +398,7 @@ button.disabled {
     .popover-content{
             height: 50%;
             top: 50px;
-        }
+    }
 }
 
 ::ng-deep .custom-popover-class {
@@ -407,3 +407,26 @@ button.disabled {
         top: 50px;
     }
 }
+
+::ng-deep.popover-viewport.sc-ion-popover-md {
+    overflow: auto;
+}
+
+.loading {
+    display: flex;
+    position: absolute;
+    justify-content: center;
+    top: 0;
+    left: 0px;
+    width: 100%;
+    height: 100%;
+    z-index: 2;
+    overflow: hidden;
+}
+
+.loadingMsg {
+    padding: 2%;
+    color: var(--ion-color-light);
+    font-weight: bold;
+    margin-right: -5px;
+}
\ No newline at end of file
diff --git a/src/frontend/app/document/document.component.ts b/src/frontend/app/document/document.component.ts
index ffbda0c365058190e2c5b137684a86ecd4f63c4b..99d0490b9dc56d5ddebadf88ee7f3833a3cea0de 100755
--- a/src/frontend/app/document/document.component.ts
+++ b/src/frontend/app/document/document.component.ts
@@ -18,7 +18,7 @@ import { LocalStorageService } from '../service/local-storage.service';
 import { ActionSheetController, AlertController, LoadingController, MenuController, ModalController, NavController } from '@ionic/angular';
 import { NgxExtendedPdfViewerService } from 'ngx-extended-pdf-viewer';
 import { catchError, exhaustMap, tap } from 'rxjs/operators';
-import { of, Subscription } from 'rxjs';
+import { of, Subscription, timer } from 'rxjs';
 import { SignatureMethodService } from '../service/signature-method/signature-method.service';
 import { FunctionsService } from '../service/functions.service';
 import { ActionsService } from '../service/actions.service';
@@ -74,6 +74,7 @@ export class DocumentComponent implements OnInit, OnDestroy {
     };
 
     subscription: Subscription;
+    timerSubscription: Subscription;
 
     signaturesContent: any = [];
     docList: any = [];
@@ -135,17 +136,15 @@ export class DocumentComponent implements OnInit, OnDestroy {
         });
     }
 
-    ngOnDestroy(): void {
-        this.subscription.unsubscribe();
-    }
-
     imageLoaded(ev: any) {
         this.userMode = this.mainDocument.workflow.find((item: any) => item.current)?.mode;
         if (this.userMode === 'note') {
             this.actionsList = this.actionsList.filter((item: any) => item.event !== 'openSignatures');
         }
         this.getImageDimensions(true);
-        this.load.dismiss();
+        if (this.mainDocument.status !== 'CONVERTING') {
+            this.load.dismiss();
+        }
         this.menu.enable(true, 'right-menu');
         this.loadingImage = false;
         document.getElementsByClassName('drag-scroll-content')[0].scrollTop = 0;
@@ -310,6 +309,11 @@ export class DocumentComponent implements OnInit, OnDestroy {
         });
     }
 
+    ngOnDestroy(): void {
+        this.timerSubscription?.unsubscribe();
+        this.subscription.unsubscribe();
+    }
+
     initDocumentInfos(params: any) {
         this.http.get('../rest/documents/' + params['id']).pipe(
             tap((data: any) => {
@@ -391,6 +395,29 @@ export class DocumentComponent implements OnInit, OnDestroy {
                 // this.renderPdf();
                 this.renderImage();
                 this.loadingdocument = false;
+                this.load.dismiss();
+
+                if (this.mainDocument.status === 'CONVERTING') {
+                    // timer(0, 10000) call the function immediately and every 10 seconds
+                    this.timerSubscription = timer(0, 10000).pipe(
+                        tap(() => {
+                            this.http.get('../rest/documents/' + params['id']).pipe(
+                                tap((res: any) => {
+                                    this.totalPages = res.document.pages;
+                                    if (res.document.status !== 'CONVERTING') {
+                                        this.mainDocument.status = res.document.status;
+                                        this.timerSubscription?.unsubscribe();
+                                    }
+                                })
+                            ).subscribe();
+                        }),
+                        catchError((err: any) => {
+                            this.load.dismiss();
+                            this.notificationService.handleErrors(err);
+                            return of(false);
+                        })
+                    ).subscribe();
+                }
             }),
             catchError((err: any) => {
                 console.log('error', err);
@@ -769,4 +796,8 @@ export class DocumentComponent implements OnInit, OnDestroy {
             }
         }
     }
+
+    isNotReady() {
+        return ['CONVERTING', 'ERROR'].indexOf(this.mainDocument.status) > -1;
+    }
 }
diff --git a/src/frontend/app/indexation/indexation.component.ts b/src/frontend/app/indexation/indexation.component.ts
index b9623e4795f80cae59591b3cb05dcab0e1683f5b..3273ee61fd0d942b82f4a7d2aa6d732b159e9001 100644
--- a/src/frontend/app/indexation/indexation.component.ts
+++ b/src/frontend/app/indexation/indexation.component.ts
@@ -301,35 +301,46 @@ export class IndexationComponent implements OnInit {
     }
 
     uploadTrigger(fileInput: any) {
-        if (fileInput.target.files && fileInput.target.files[0] && this.isExtensionAllowed(fileInput.target.files)) {
-            for (let index = 0; index < fileInput.target.files.length; index++) {
-                const filename = fileInput.target.files[index].name;
-                const file = {
-                    title: filename.substr(0, filename.lastIndexOf('.')),
-                    reference: filename.substr(0, filename.lastIndexOf('.')).substr(0, 53),
-                    mainDocument: true,
-                    content: ''
-                };
-                const reader = new FileReader();
-                reader.readAsArrayBuffer(fileInput.target.files[index]);
-                reader.onload = (value: any) => {
-                    file.mainDocument = this.filesToUpload.length === 0;
-                    file.reference = this.filesToUpload.length === 0 ? file.reference : '';
-                    file.content = this.getBase64Document(value.target.result);
-                    this.filesToUpload.push(file);
-                    if (this.filesToUpload.length === 1) {
-                        setTimeout(() => {
-                            this.menu.open('right-menu');
-                        }, 500);
+        this.loadingController.create({
+            message: this.translate.instant('lang.loadingDocument'),
+            spinner: 'dots'
+        }).then(async (load: HTMLIonLoadingElement) => {
+            load.present();
+            if (fileInput.target.files && fileInput.target.files[0] && this.isExtensionAllowed(fileInput.target.files)) {
+                for (let index = 0; index < fileInput.target.files.length; index++) {
+                    const filename = fileInput.target.files[index].name;
+                    const file = {
+                        title: filename.substr(0, filename.lastIndexOf('.')),
+                        reference: filename.substr(0, filename.lastIndexOf('.')).substr(0, 53),
+                        mainDocument: true,
+                        content: ''
+                    };
+                    const reader = new FileReader();
+                    reader.readAsArrayBuffer(fileInput.target.files[index]);
+                    reader.onload = (value: any) => {
+                        file.mainDocument = this.filesToUpload.length === 0;
+                        file.reference = this.filesToUpload.length === 0 ? file.reference : '';
+                        file.content = this.getBase64Document(value.target.result);
+                        this.filesToUpload.push(file);
+                        if (this.filesToUpload.length === 1) {
+                            setTimeout(() => {
+                                this.menu.open('right-menu');
+                            }, 500);
+                        }
+                    };
+                    if (index === fileInput.target.files.length - 1) {
+                        load.dismiss();
                     }
-                };
+                }
+                this.fileImport.nativeElement.value = '';
+            } else {
+                this.loading = false;
+                load.dismiss();
             }
-            this.fileImport.nativeElement.value = '';
-        } else {
-            this.loading = false;
-        }
+        });
     }
 
+
     isExtensionAllowed(files: any[]) {
         for (let index = 0; index < files.length; index++) {
             if (files[index].name.toLowerCase().split('.').pop() !== 'pdf') {