diff --git a/core/xml/actions_pages.xml b/core/xml/actions_pages.xml index d37818eee11288abffd596b773c6598e26c9ece6..4deacefef65edc405d877f5f5de7ee347e6b7ed4 100755 --- a/core/xml/actions_pages.xml +++ b/core/xml/actions_pages.xml @@ -359,7 +359,7 @@ An action page is described in a ACTIONPAGE tag : <LABEL>_SEND_TO_AVIS_WF</LABEL> <NAME>send_to_avis</NAME> <DESC>_SEND_TO_AVIS_WF_DESC</DESC> - <component>sendToAvisAction</component> + <component>sendToOpinionCircuitAction</component> <ORIGIN>module</ORIGIN> <MODULE>avis</MODULE> <FLAG_CREATE>false</FLAG_CREATE> diff --git a/migration/20.03/2003.sql b/migration/20.03/2003.sql index a872f87b0dd54510fc160a3c11aea42289ca874a..4739eacbfc1b057f9e8d76622401ba0d7fd2ed07 100644 --- a/migration/20.03/2003.sql +++ b/migration/20.03/2003.sql @@ -92,8 +92,8 @@ UPDATE actions SET component = 'resetVisaAction' WHERE action_page = 'rejection_ UPDATE actions SET component = 'interruptVisaAction' WHERE action_page = 'interrupt_visa'; UPDATE actions SET component = 'sendSignatureBookAction' WHERE action_page IN ('send_to_visa', 'send_signed_docs'); UPDATE actions SET component = 'continueVisaCircuitAction' WHERE action_page = 'visa_workflow'; -UPDATE actions SET component = 'sendToAvisAction' WHERE action_page = 'send_to_avis'; UPDATE actions SET component = 'closeMailWithAttachmentsOrNotesAction' WHERE action_page = 'close_mail_with_attachment'; +UPDATE actions SET component = 'sendToOpinionCircuitAction' WHERE action_page = 'send_to_avis'; UPDATE actions SET component = 'continueOpinionCircuitAction' WHERE action_page = 'avis_workflow'; /* FOLDERS */ diff --git a/src/app/action/controllers/ActionMethodController.php b/src/app/action/controllers/ActionMethodController.php index cf46d9699677e1d046d54a1502fa5a716b8b91a4..85452e274471bbfbdba0d5e9ef96d3fb70449e90 100644 --- a/src/app/action/controllers/ActionMethodController.php +++ b/src/app/action/controllers/ActionMethodController.php @@ -59,7 +59,7 @@ class ActionMethodController 'rejectVisaBackToPreviousAction' => 'rejectVisaBackToPrevious', 'resetVisaAction' => 'resetVisa', 'interruptVisaAction' => 'interruptVisa', - 'sendToAvisAction' => 'sendToAvis', + 'sendToOpinionCircuitAction' => 'sendToOpinionCircuit', 'continueOpinionCircuitAction' => 'continueOpinionCircuit', 'noConfirmAction' => null ]; @@ -535,7 +535,7 @@ class ActionMethodController return true; } - public static function sendToAvis(array $args) + public static function sendToOpinionCircuit(array $args) { ValidatorModel::notEmpty($args, ['resId']); ValidatorModel::intVal($args, ['resId']); diff --git a/src/frontend/app/actions/actions.service.ts b/src/frontend/app/actions/actions.service.ts index 6585b62af2d10eaaf72e827d19524667e6693380..67dd15744ef46af54198f334488f6548a2ed37bc 100644 --- a/src/frontend/app/actions/actions.service.ts +++ b/src/frontend/app/actions/actions.service.ts @@ -27,6 +27,7 @@ import { closeMailWithAttachmentsOrNotesActionComponent } from './close-mail-wit import { Router } from '@angular/router'; import { SendSignatureBookActionComponent } from './visa-send-signature-book-action/send-signature-book-action.component'; import { ContinueVisaCircuitActionComponent } from './visa-continue-circuit-action/continue-visa-circuit-action.component'; +import { SendAvisWorkflowComponent } from './avis-workflow-send-action/send-avis-workflow-action.component'; @Injectable() export class ActionsService { @@ -728,4 +729,26 @@ export class ActionsService { }) ).subscribe(); } + + sendToOpinionCircuitAction(options: any = null) { + const dialogRef = this.dialog.open(SendAvisWorkflowComponent, { + autoFocus: false, + disableClose: true, + data: this.setDatasActionToSend() + }); + dialogRef.afterClosed().pipe( + tap((data: any) => { + this.unlockResourceAfterActionModal(data); + }), + filter((data: string) => data === 'success'), + tap((result: any) => { + this.endAction(result); + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } } diff --git a/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.html b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.html new file mode 100644 index 0000000000000000000000000000000000000000..54c812860cdc59b608b8fe43eece04178de4eee0 --- /dev/null +++ b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.html @@ -0,0 +1,37 @@ +<h1 mat-dialog-title>{{data.action.label}}</h1> +<div mat-dialog-content> + <div *ngIf="loading" class="loading" style="display:flex;height:100%;"> + <mat-spinner style="margin:auto;"></mat-spinner> + </div> + <mat-sidenav-container autosize style="height:100%;"> + <mat-sidenav-content style="background: white;padding:10px;"> + <div> + {{lang.makeActionOn}} + <b *ngIf="data.resIds.length === 0" color="primary" class="highlight">{{lang.currentIndexingMail}}</b> + <b *ngIf="data.resIds.length == 1" color="primary" class="highlight">{{data.resource.chrono}}</b> + <b *ngIf="data.resIds.length > 1" color="primary" class="highlight">{{data.resIds.length}} + {{lang.elements}}</b> ? + </div> + <div *ngIf="resourcesError.length > 0" class="alert-message alert-message-danger mailList" role="alert"> + <p> + {{lang.canNotMakeAction}} : + </p> + <ul> + <li *ngFor="let ressource of resourcesError"> + <b>{{ressource.alt_identifier}}</b> : {{lang[ressource.reason]}} + </li> + </ul> + </div> + <app-avis-workflow *ngIf="data.resIds.length == 1 || (!noResourceToProcess && data.resIds.length > 1)" [adminMode]="true" #appAvisWorkflow> + </app-avis-workflow> + <div style="padding-top: 10px;"> + <app-note-editor #noteEditor [resIds]="data.resIds"></app-note-editor> + </div> + </mat-sidenav-content> + </mat-sidenav-container> +</div> +<div mat-dialog-actions class="actions"> + <button mat-raised-button mat-button color="primary" [disabled]="loading || !isValidAction()" + (click)="onSubmit()">{{lang.validate}}</button> + <button mat-raised-button mat-button [disabled]="loading" [mat-dialog-close]="">{{lang.cancel}}</button> +</div> \ No newline at end of file diff --git a/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.scss b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..9424211bb4af9027d454165e7417bf86fb23c6eb --- /dev/null +++ b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.scss @@ -0,0 +1,128 @@ +@import '../../../css/vars.scss'; + +.fullHeight { + height: 70vh; +} + +.fullWidth { + width: 70vw; +} + +.highlight { + font-size: 110%; +} + +.loading { + display: flex; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #ffffffb3; + z-index: 2; + overflow: hidden; +} + +.mailList { + ul { + font-size: 12px; + max-height: 100px; + overflow: auto; + padding-left: 25px; + padding-right: 5px; + padding-bottom: 10px; + margin-top: 10px; + } + + p { + //font-size: 18px; + margin: 0; + text-decoration: underline; + } + + b { + font-size: 120%; + } +} + +.formType { + align-items: center; + display: flex; + margin: 10px; + border-radius: 4px; + border: solid 1px #ccc; + position: relative; + padding: 10px; + + &-title { + white-space: pre; + overflow: hidden; + max-width: 85%; + text-overflow: ellipsis; + z-index: 1; + font-size: 10px; + font-weight: bold; + background: white; + position: absolute; + top: -7px; + left: 10px; + padding: 0px; + margin: 0px; + color: #135f7f; + } + + ::ng-deep.mat-form-field-suffix { + color: $secondary; + font-size: 15px; + top: 0; + } + + ::ng-deep.mat-form-field-wrapper { + padding: 0; + } +} + +.priceContent { + display: flex; + align-items: center; + justify-content: flex-end; + width: 100%; + + &-label { + text-align: right; + color: $primary; + flex: 1; + justify-content: flex-end; + display: flex; + padding-right: 10px; + } + + .mat-form-field { + width: 90px !important; + + input { + font-weight: bold; + user-select: none; + } + } +} + +.priceInfo { + padding-right: 20px; + font-size: 10px; + opacity: 0.5; + width: 100%; +} + +.pjList { + display: flex; + width: 100%; + overflow: auto; + flex-direction: column; + background: #666; + + img { + margin: 10px; + } +} \ No newline at end of file diff --git a/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.ts b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0239883639ac90af1496c67ac8fa8ade4745d5f --- /dev/null +++ b/src/frontend/app/actions/avis-workflow-send-action/send-avis-workflow-action.component.ts @@ -0,0 +1,131 @@ +import { Component, OnInit, Inject, ViewChild } from '@angular/core'; +import { LANG } from '../../translate.component'; +import { NotificationService } from '../../notification.service'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { HttpClient } from '@angular/common/http'; +import { NoteEditorComponent } from '../../notes/note-editor.component'; +import { tap, finalize, catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { FunctionsService } from '../../../service/functions.service'; +import { AvisWorkflowComponent } from '../../avis/avis-workflow.component'; + +@Component({ + templateUrl: "send-avis-workflow-action.component.html", + styleUrls: ['send-avis-workflow-action.component.scss'], +}) +export class SendAvisWorkflowComponent implements OnInit { + + lang: any = LANG; + loading: boolean = false; + + resourcesError: any[] = []; + + noResourceToProcess: boolean = null; + + @ViewChild('noteEditor', { static: true }) noteEditor: NoteEditorComponent; + @ViewChild('appAvisWorkflow', { static: false }) appAvisWorkflow: AvisWorkflowComponent; + + constructor( + public http: HttpClient, + private notify: NotificationService, + public dialogRef: MatDialogRef<SendAvisWorkflowComponent>, + @Inject(MAT_DIALOG_DATA) public data: any, + public functions: FunctionsService) { } + + async ngOnInit(): Promise<void> { + if (this.data.resIds.length > 0) { + this.loading = true; + await this.checkAvisWorkflow(); + this.loading = false; + } + if (this.data.resIds.length === 1) { + await this.appAvisWorkflow.loadWorkflow(this.data.resIds[0]); + if (this.appAvisWorkflow.emptyWorkflow()) { + this.appAvisWorkflow.loadDefaultWorkflow(this.data.resIds[0]); + } + } + } + + checkAvisWorkflow() { + this.resourcesError = []; + + // TO DO : WAIT BACK + return new Promise((resolve, reject) => { + this.http.post('../../rest/resourcesList/users/' + this.data.userId + '/groups/' + this.data.groupId + '/baskets/' + this.data.basketId + '/actions/' + this.data.action.id + '/checkSignatureBook', { resources: this.data.resIds }) + .subscribe((data: any) => { + if (!this.functions.empty(data.resourcesInformations.noAttachment)) { + this.resourcesError = data.resourcesInformations.noAttachment; + } + this.noResourceToProcess = this.data.resIds.length === this.resourcesError.length; + resolve(true); + }, (err: any) => { + this.notify.handleSoftErrors(err); + }); + }); + } + + async onSubmit() { + this.loading = true; + if (this.data.resIds.length === 0) { + let res = await this.indexDocument(); + if (res) { + res = await this.appAvisWorkflow.saveAvisWorkflow(this.data.resIds); + } + if (res) { + this.executeAction(this.data.resIds); + } + } else { + const realResSelected: number[] = this.data.resIds.filter((resId: any) => this.resourcesError.map(resErr => resErr.res_id).indexOf(resId) === -1); + + const res = await this.appAvisWorkflow.saveAvisWorkflow(realResSelected); + + if (res) { + this.executeAction(realResSelected); + } + } + this.loading = false; + } + + indexDocument() { + return new Promise((resolve, reject) => { + this.http.post('../../rest/resources', this.data.resource).pipe( + tap((data: any) => { + this.data.resIds = [data.resId]; + resolve(true); + }), + catchError((err: any) => { + this.notify.handleErrors(err); + resolve(false); + return of(false); + }) + ).subscribe(); + }); + } + + executeAction(realResSelected: number[]) { + + this.http.put(this.data.processActionRoute, { resources: realResSelected, note: this.noteEditor.getNoteContent() }).pipe( + tap((data: any) => { + if (!data) { + this.dialogRef.close('success'); + } + if (data && data.errors != null) { + this.notify.error(data.errors); + } + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleErrors(err); + return of(false); + }) + ).subscribe(); + } + + isValidAction() { + if (!this.noResourceToProcess && this.appAvisWorkflow !== undefined && !this.appAvisWorkflow.emptyWorkflow() && !this.appAvisWorkflow.workflowEnd()) { + return true; + } else { + return false; + } + } +} diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index b3425a5643ec0f6995ff093bccb8d7a96b32e33d..3e345ef6fce9760b7fedaf5db5763290fac6b01b 100755 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -61,6 +61,7 @@ import { SendSignatureBookActionComponent } from './actions/send-s import { ContinueVisaCircuitActionComponent } from './actions/continue-visa-circuit-action/continue-visa-circuit-action.component'; import { SendSignatureBookActionComponent } from './actions/visa-send-signature-book-action/send-signature-book-action.component'; import { ContinueVisaCircuitActionComponent } from './actions/visa-continue-circuit-action/continue-visa-circuit-action.component'; +import { SendAvisWorkflowComponent } from './actions/avis-workflow-send-action/send-avis-workflow-action.component'; import { FiltersListComponent } from './list/filters/filters-list.component'; import { FiltersToolComponent } from './list/filters/filters-tool.component'; @@ -155,6 +156,7 @@ import { AddAvisModelModalComponent } from './avis/addAvisModel/add-avis-model-m closeMailWithAttachmentsOrNotesActionComponent, SendSignatureBookActionComponent, ContinueVisaCircuitActionComponent, + SendAvisWorkflowComponent, ActionsListComponent, PrintSeparatorComponent, FolderPinnedComponent, @@ -201,6 +203,7 @@ import { AddAvisModelModalComponent } from './avis/addAvisModel/add-avis-model-m RejectVisaBackToPrevousActionComponent, ResetVisaActionComponent, InterruptVisaActionComponent, + SendAvisWorkflowComponent, UpdateDepartureDateActionComponent, SendExternalSignatoryBookActionComponent, SendExternalNoteBookActionComponent, diff --git a/src/frontend/app/avis/avis-workflow.component.ts b/src/frontend/app/avis/avis-workflow.component.ts index 362b06e1178b1782878873d620879003ed6dd452..8cb910893ba53f49e86ef5f80731fa5fc6ef75f5 100644 --- a/src/frontend/app/avis/avis-workflow.component.ts +++ b/src/frontend/app/avis/avis-workflow.component.ts @@ -125,7 +125,9 @@ export class AvisWorkflowComponent implements OnInit { } async loadAvisModelList() { - await this.loadDefaultModel(); + if (this.resId !== null) { + await this.loadDefaultModel(); + } return new Promise((resolve, reject) => { this.http.get(`../../rest/availableCircuits?circuit=opinion`).pipe( @@ -231,7 +233,8 @@ export class AvisWorkflowComponent implements OnInit { this.resId = resId; this.loading = true; this.avisWorkflow.items = []; - this.http.get("../../rest/resources/" + resId + "/opinionCircuit") + return new Promise((resolve, reject) => { + this.http.get("../../rest/resources/" + resId + "/opinionCircuit") .subscribe((data: any) => { data.forEach((element: any) => { this.avisWorkflow.items.push( @@ -242,9 +245,35 @@ export class AvisWorkflowComponent implements OnInit { }); this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items)) this.loading = false; + resolve(true); }, (err: any) => { this.notify.handleErrors(err); }); + }); + + } + + loadDefaultWorkflow(resId: number) { + this.loading = true; + this.avisWorkflow.items = []; + this.http.get("../../rest/resources/" + resId + "/defaultCircuit?circuit=opinion").pipe( + filter((data: any) => !this.functions.empty(data.circuit)), + tap((data: any) => { + data.circuit.items.forEach((element: any) => { + this.avisWorkflow.items.push( + { + ...element, + difflist_type: 'AVIS_CIRCUIT' + }); + }); + this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items)) + }), + finalize(() => this.loading = false), + catchError((err: any) => { + this.notify.handleSoftErrors(err); + return of(false); + }) + ).subscribe(); } deleteItem(index: number) { @@ -263,10 +292,10 @@ export class AvisWorkflowComponent implements OnInit { return this.avisWorkflow.items; } - saveAvisWorkflow() { + saveAvisWorkflow(resIds: number[] = [this.resId]) { return new Promise((resolve, reject) => { if (this.avisWorkflow.items.length === 0) { - this.http.delete(`../../rest/resources/${this.resId}/circuits/opinionCircuit`).pipe( + this.http.delete(`../../rest/resources/${resIds[0]}/circuits/opinionCircuit`).pipe( tap(() => { this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items)); this.notify.success(this.lang.avisWorkflowDeleted); @@ -278,7 +307,13 @@ export class AvisWorkflowComponent implements OnInit { }) ).subscribe(); } else { - this.http.put(`../../rest/circuits/opinionCircuit`, {resources: [{ resId: this.resId, listInstances: this.avisWorkflow.items }]} ).pipe( + const arrAvis = resIds.map(resId => { + return { + resId: resId, + listInstances: this.avisWorkflow.items + } + }); + this.http.put(`../../rest/circuits/opinionCircuit`, { resources: arrAvis }).pipe( tap((data: any) => { this.avisWorkflowClone = JSON.parse(JSON.stringify(this.avisWorkflow.items)); this.notify.success(this.lang.avisWorkflowUpdated); @@ -328,6 +363,22 @@ export class AvisWorkflowComponent implements OnInit { } } + emptyWorkflow() { + if (this.avisWorkflow.items.length === 0) { + return true; + } else { + return false; + } + } + + workflowEnd() { + if (this.avisWorkflow.items.filter((item: any) => !this.functions.empty(item.process_date)).length === this.avisWorkflow.items.length) { + return true; + } else { + return false; + } + } + openPromptSaveModel() { const dialogRef = this.dialog.open(AddAvisModelModalComponent, { data: { avisWorkflow: this.avisWorkflow.items } }); diff --git a/src/frontend/app/visa/visa-workflow.component.ts b/src/frontend/app/visa/visa-workflow.component.ts index a12975f2b3b6c376d930a975a269b55dc0d5ea08..6998ac09d1ec4026469e39af2081f241e632d9d2 100644 --- a/src/frontend/app/visa/visa-workflow.component.ts +++ b/src/frontend/app/visa/visa-workflow.component.ts @@ -136,7 +136,9 @@ export class VisaWorkflowComponent implements OnInit { } async loadVisaModelList() { - await this.loadDefaultModel(); + if (this.resId !== null) { + await this.loadDefaultModel(); + } return new Promise((resolve, reject) => { this.http.get(`../../rest/availableCircuits?circuit=visa`).pipe(