فهرست منبع

Avances Gestión de Mantenimiento Preventivo

Jose Brito 2 سال پیش
والد
کامیت
86e359796a

+ 26 - 11
sistema-mantenimiento-front/package-lock.json

@@ -37,6 +37,8 @@
         "assert": "^2.0.0",
         "chart.js": "^3.9.1",
         "crypto-browserify": "^3.12.0",
+        "dom-to-image": "^2.6.0",
+        "html2canvas": "^1.4.1",
         "https-browserify": "^1.0.0",
         "jspdf": "^2.5.1",
         "jspdf-autotable": "^3.5.25",
@@ -65,6 +67,7 @@
         "@angular-devkit/build-angular": "^16.0.2",
         "@angular/cli": "^16.0.2",
         "@angular/compiler-cli": "^16.0.1",
+        "@types/dom-to-image": "^2.6.4",
         "@types/jasmine": "~3.10.0",
         "@types/libsodium-wrappers": "^0.7.9",
         "jasmine-core": "~3.10.0",
@@ -4934,6 +4937,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/dom-to-image": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.4.tgz",
+      "integrity": "sha512-UddUdGF1qulrSDulkz3K2Ypq527MR6ixlgAzqLbxSiQ0icx0XDlIV+h4+edmjq/1dqn0KgN0xGSe1kI9t+vGuw==",
+      "dev": true
+    },
     "node_modules/@types/eslint": {
       "version": "8.4.10",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
@@ -5732,7 +5741,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
       "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
-      "optional": true,
       "engines": {
         "node": ">= 0.6.0"
       }
@@ -6896,7 +6904,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
       "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
-      "optional": true,
       "dependencies": {
         "utrie": "^1.0.2"
       }
@@ -7216,6 +7223,11 @@
         "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
       }
     },
+    "node_modules/dom-to-image": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz",
+      "integrity": "sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA=="
+    },
     "node_modules/domain-browser": {
       "version": "4.22.0",
       "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz",
@@ -8910,7 +8922,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
       "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
-      "optional": true,
       "dependencies": {
         "css-line-break": "^2.1.0",
         "text-segmentation": "^1.0.3"
@@ -14596,7 +14607,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
       "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
-      "optional": true,
       "dependencies": {
         "utrie": "^1.0.2"
       }
@@ -15042,7 +15052,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
       "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
-      "optional": true,
       "dependencies": {
         "base64-arraybuffer": "^1.0.2"
       }
@@ -19504,6 +19513,12 @@
         "@types/node": "*"
       }
     },
+    "@types/dom-to-image": {
+      "version": "2.6.4",
+      "resolved": "https://registry.npmjs.org/@types/dom-to-image/-/dom-to-image-2.6.4.tgz",
+      "integrity": "sha512-UddUdGF1qulrSDulkz3K2Ypq527MR6ixlgAzqLbxSiQ0icx0XDlIV+h4+edmjq/1dqn0KgN0xGSe1kI9t+vGuw==",
+      "dev": true
+    },
     "@types/eslint": {
       "version": "8.4.10",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
@@ -20173,8 +20188,7 @@
     "base64-arraybuffer": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
-      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
-      "optional": true
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="
     },
     "base64-js": {
       "version": "1.5.1",
@@ -21075,7 +21089,6 @@
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
       "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
-      "optional": true,
       "requires": {
         "utrie": "^1.0.2"
       }
@@ -21320,6 +21333,11 @@
         "entities": "^2.0.0"
       }
     },
+    "dom-to-image": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/dom-to-image/-/dom-to-image-2.6.0.tgz",
+      "integrity": "sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA=="
+    },
     "domain-browser": {
       "version": "4.22.0",
       "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz",
@@ -22612,7 +22630,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
       "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
-      "optional": true,
       "requires": {
         "css-line-break": "^2.1.0",
         "text-segmentation": "^1.0.3"
@@ -26845,7 +26862,6 @@
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
       "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
-      "optional": true,
       "requires": {
         "utrie": "^1.0.2"
       }
@@ -27163,7 +27179,6 @@
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
       "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
-      "optional": true,
       "requires": {
         "base64-arraybuffer": "^1.0.2"
       }

+ 3 - 0
sistema-mantenimiento-front/package.json

@@ -39,6 +39,8 @@
     "assert": "^2.0.0",
     "chart.js": "^3.9.1",
     "crypto-browserify": "^3.12.0",
+    "dom-to-image": "^2.6.0",
+    "html2canvas": "^1.4.1",
     "https-browserify": "^1.0.0",
     "jspdf": "^2.5.1",
     "jspdf-autotable": "^3.5.25",
@@ -67,6 +69,7 @@
     "@angular-devkit/build-angular": "^16.0.2",
     "@angular/cli": "^16.0.2",
     "@angular/compiler-cli": "^16.0.1",
+    "@types/dom-to-image": "^2.6.4",
     "@types/jasmine": "~3.10.0",
     "@types/libsodium-wrappers": "^0.7.9",
     "jasmine-core": "~3.10.0",

+ 50 - 35
sistema-mantenimiento-front/src/app/components/preventive-maintenance/maintenance-simulation/simulation-dialog/simulation-dialog.component.css

@@ -1,44 +1,59 @@
 .simulation-container{
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    height: calc(100vh - 290px);
-    overflow: auto;
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: calc(100vh - 328px);
+  overflow: auto;
+}
+
+.simulation-column{
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  overflow: hidden;
+}
+
+.actions-row{
+  width: 100%;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
 }
 
 .simulation-loader-container{
-    width: 100%;
-    display: flex;
-    flex-direction: column;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
 }
 
 .simulation-loader-container h1{
-    text-align: center;
+  text-align: center;
 }
 
 .sub_step_icon{
-    display: flex;
-    justify-content: center;
-    align-items: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
 }
 
 .simulation_main_icon{
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    padding: 8px;
-    width: 64px;
-    height: 64px;
-    border-radius: 64px;
-    border-style: solid;
-    border-width: 2px;
-    border-color: rgba(0, 0, 0, 0.6);
-    cursor: pointer;
-    box-sizing: border-box;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 8px;
+  width: 64px;
+  height: 64px;
+  border-radius: 64px;
+  border-style: solid;
+  border-width: 2px;
+  border-color: rgba(0, 0, 0, 0.6);
+  cursor: pointer;
+  box-sizing: border-box;
 }
 
 .simulation_main_icon:hover{
-    background-color: rgba(0, 0, 0, 0.08);
+  background-color: rgba(0, 0, 0, 0.08);
 }
 
 .simulation_main_icon mat-icon{
@@ -46,23 +61,23 @@
 }
 
 .simulation-label{
-    font-size: 20px;
-    color: rgba(0, 0, 0, 0.6);
+  font-size: 20px;
+  color: rgba(0, 0, 0, 0.6);
 }
 
 .simulation-row{
-    display: flex;
-    flex-direction: row;
-    align-items: center;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
 }
 
 .simulation_mb_8{
-    margin-bottom: 8px;
+  margin-bottom: 8px;
 }
 
 .sub_step{
-    padding-left: 80px;
-    display: flex;
-    flex-direction: row;
-    align-items: center;
+  padding-left: 80px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
 }

+ 22 - 13
sistema-mantenimiento-front/src/app/components/preventive-maintenance/maintenance-simulation/simulation-dialog/simulation-dialog.component.html

@@ -8,23 +8,32 @@
     <mat-icon class="red_primary_font">error</mat-icon>
     <h2>{{ errorStr }}</h2>
   </div>
-  <div class="simulation-loader-container animated fadeIn" *ngIf="!isLoading && !hasError && starting">
-    <h1>Iniciando simulación</h1>
-    <mat-progress-bar mode="indeterminate"></mat-progress-bar>
-  </div>
-  <div class="simulation-container animated fadeIn" *ngIf="!isLoading && !hasError && !starting" #simulationContainer>
-    <div class="simulation-row animated fadeIn" *ngFor="let step of steps; let i = index" [ngClass]="{ simulation_mb_8: i < steps.length - 1, 
-    sub_step: !step.isMain }">
-      <div [ngClass]="{ simulation_main_icon: step.isMain, sub_step_icon: !step.isMain }">
-        <mat-icon>{{ step.icon }}</mat-icon>
-      </div>
-      <div class="simulation-label ml-16">
-        {{ step.label }}
+  <div class="simulation-column animated fadeIn" *ngIf="!isLoading && !hasError">
+    <div class="actions-row mb-16 mt-16">
+      <mat-slide-toggle [disabled]="simulating" [(ngModel)]="includeCharge">Incluir carga inducida</mat-slide-toggle>
+      <button mat-flat-button color="primary" class="ml-8" (click)="startSimulation()" [disabled]="simulating">
+        <mat-icon>play_arrow</mat-icon> Iniciar simulación
+      </button>
+      <a class="hidden" target="_blank" id="download"></a>
+    </div>
+    <div class="simulation-loader-container animated fadeIn" *ngIf="starting">
+      <h1>Iniciando simulación</h1>
+      <mat-progress-bar mode="indeterminate"></mat-progress-bar>
+    </div>
+    <div class="simulation-container animated fadeIn" *ngIf="!starting" #simulationContainer id="simulationContainer">
+      <div class="simulation-row animated fadeIn" *ngFor="let step of steps; let i = index" [ngClass]="{ simulation_mb_8: i < steps.length - 1, 
+      sub_step: !step.isMain }" [id]="'step-' + step.id">
+        <div [ngClass]="{ simulation_main_icon: step.isMain, sub_step_icon: !step.isMain }">
+          <mat-icon>{{ step.icon }}</mat-icon>
+        </div>
+        <div class="simulation-label ml-16">
+          {{ step.label }}
+        </div>
       </div>
     </div>
   </div>
 </div>
 <div mat-dialog-actions align="end">
   <button mat-button mat-dialog-close>Cerrar</button>
-  <button mat-button>Imprimir simulación</button>
+  <button mat-button (click)="printPDF()" [disabled]="!hasBeenSimulated">Imprimir simulación</button>
 </div>

+ 113 - 3
sistema-mantenimiento-front/src/app/components/preventive-maintenance/maintenance-simulation/simulation-dialog/simulation-dialog.component.ts

@@ -5,6 +5,8 @@ import { lastValueFrom } from 'rxjs';
 import { PreventiveOrderSimulation, PreventiveOrderSimulationResponse } from 'src/app/interfaces/preventive-order-simulation.interface';
 import { EncService } from 'src/app/services/enc/enc.service';
 import { PreventiveMaintenanceService } from 'src/app/services/preventive-maintenance.service';
+import { MatSnackBar } from '@angular/material/snack-bar';
+import { DocumentManagementService } from 'src/app/services/document-management/document-management.service';
 
 interface ToolInfo{
   ID: string;
@@ -59,6 +61,7 @@ const SPARE_PARTS_TMP: ToolInfo[] = [
   {ID: 'S20', NOMB: 'Cable HDMI 2m', MODE: 'ASD-181', CANTDISP: 6, TYPE: 'Refaccción', UNIT: 'Pieza'},
 ];
 interface ExecutionStep{
+  id: number;
   icon: string;
   label: string;
   isMain: boolean;
@@ -82,6 +85,9 @@ export class SimulationDialogComponent implements OnInit {
   eventIcon: string;
   collectResources: boolean;
   steps: ExecutionStep[];
+  simulating: boolean;
+  includeCharge: boolean;
+  hasBeenSimulated: boolean;
 
   @ViewChild('simulationContainer', {static: false}) simulationContainer?: ElementRef;
   private scrollContainer: any;
@@ -90,7 +96,9 @@ export class SimulationDialogComponent implements OnInit {
     @Inject(MAT_DIALOG_DATA) private _data: any,
     @Inject(DOCUMENT) private _document: Document,
     private _encService: EncService,
-    private _prevMaintService: PreventiveMaintenanceService
+    private _prevMaintService: PreventiveMaintenanceService,
+    private _snackBar: MatSnackBar,
+    private _docMangeService: DocumentManagementService,
   ){
     this.idOrder = 0;
     this.isLoading = true;
@@ -103,6 +111,9 @@ export class SimulationDialogComponent implements OnInit {
     this.eventIcon = "";
     this.collectResources = false;
     this.steps = [];
+    this.simulating = false;
+    this.includeCharge = false;
+    this.hasBeenSimulated = false;
 
     this.scrollContainer = this.simulationContainer?.nativeElement;
   }
@@ -130,7 +141,6 @@ export class SimulationDialogComponent implements OnInit {
 
       if(!this.hasError){
         this.orderSimulation = simulation.response;
-        this.makeSimulation();
       }
       
       this.isLoading = false;
@@ -148,13 +158,22 @@ export class SimulationDialogComponent implements OnInit {
     }
   }
 
+  startSimulation(){
+    this.steps = [];
+    this.makeSimulation();
+  }
+
   async makeSimulation(){
+    this.hasBeenSimulated = false;
+    this.simulating = true;
     this.starting = true;
     await this.timeout(2500);
     this.starting = false;
+    let idStep = 1;
 
     if(this.orderSimulation?.TIPOACTIVADOR == 'Calendario'){
       let step: ExecutionStep = {
+        id: idStep,
         icon: 'event',
         label: ` Activación de la ejecución de la órden #${this.idOrder}`,
         isMain: true
@@ -163,6 +182,7 @@ export class SimulationDialogComponent implements OnInit {
       this.steps.push(step);
     }else if(this.orderSimulation?.TIPOACTIVADOR == 'Sintoma'){
       let step: ExecutionStep = {
+        id: idStep,
         icon: 'sensors',
         label: ` Activación de la ejecución de la órden #${this.idOrder}`,
         isMain: true
@@ -171,6 +191,7 @@ export class SimulationDialogComponent implements OnInit {
       this.steps.push(step);
     }else if(this.orderSimulation?.TIPOACTIVADOR == 'Medida'){
       let step: ExecutionStep = {
+        id: idStep,
         icon: 'device_thermostat',
         label: ` Activación de la ejecución de la órden #${this.idOrder}`,
         isMain: true
@@ -179,6 +200,7 @@ export class SimulationDialogComponent implements OnInit {
       this.steps.push(step);
     }else if(this.orderSimulation?.TIPOACTIVADOR == 'Valor'){
       let step: ExecutionStep = {
+        id: idStep,
         icon: 'speed',
         label: ` Activación de la ejecución de la órden #${this.idOrder}`,
         isMain: true
@@ -190,8 +212,10 @@ export class SimulationDialogComponent implements OnInit {
     await this.timeout(750);
     this.scrollToBottom();
     this.collectResources = true;
+    idStep++;
 
     let stepTwo: ExecutionStep = {
+      id: idStep,
       icon: 'fact_check',
       label: 'Recolección de los materiales necesarios',
       isMain: true
@@ -200,6 +224,7 @@ export class SimulationDialogComponent implements OnInit {
     this.steps.push(stepTwo);
     this.scrollToBottom();
     await this.timeout(750);
+    idStep++;
     
     let resources = JSON.parse(this.orderSimulation?.RECURSOS!);
     let noResources = 0;
@@ -214,6 +239,7 @@ export class SimulationDialogComponent implements OnInit {
         if(type == 'S'){
           let part = this.spareParts.filter(item2 => item2.ID == id)[0];
           let subStep: ExecutionStep = {
+            id: idStep,
             icon: 'remove',
             label: `${part.NOMB}, ${resource.REQ} ${part.UNIT}(s) requerida(s).`,
             isMain: false
@@ -223,6 +249,7 @@ export class SimulationDialogComponent implements OnInit {
         }else{
           let tool = this.tools.filter(item2 => item2.ID == id)[0];
           let subStep: ExecutionStep = {
+            id: idStep,
             icon: 'remove',
             label: `${tool.NOMB}, ${resource.REQ} ${tool.UNIT}(s) requerida(s).`,
             isMain: false
@@ -232,6 +259,7 @@ export class SimulationDialogComponent implements OnInit {
         }
 
         this.scrollToBottom();
+        idStep++;
 
         if(cont == resources.length){
           await this.timeout(750);
@@ -245,6 +273,7 @@ export class SimulationDialogComponent implements OnInit {
 
     if(noResources > 1){
       let resStep: ExecutionStep = {
+        id: idStep,
         icon: 'remove',
         label: 'No se requieren materiales',
         isMain: false
@@ -253,6 +282,7 @@ export class SimulationDialogComponent implements OnInit {
       this.steps.push(resStep);
       this.scrollToBottom();
       await this.timeout(750);
+      idStep++;
     }
 
     let startArr = this.orderSimulation?.FECHAINICIO.split(' ')!;
@@ -263,6 +293,7 @@ export class SimulationDialogComponent implements OnInit {
     startHour = startHour > 12 ? startHour - 12 : startHour;
     
     let stepThree: ExecutionStep = {
+      id: idStep,
       icon: 'play_arrow',
       label: `Inicio de las actividades de mantenimiento, ${startHour}:${startHourArr[1]} ${startPeriod}`,
       isMain: true
@@ -271,11 +302,13 @@ export class SimulationDialogComponent implements OnInit {
     this.steps.push(stepThree);
     this.scrollToBottom();
     await this.timeout(750);
+    idStep++;
 
     let instructions = JSON.parse(this.orderSimulation?.INSTRUCCIONES!);
     cont = 1;
     for(const instruction of instructions){
       let insStep: ExecutionStep = {
+        id: idStep,
         icon: 'remove',
         label: instruction.INSTRUCCION,
         isMain: false
@@ -290,7 +323,8 @@ export class SimulationDialogComponent implements OnInit {
         await this.timeout(500);
       }
       
-      cont++
+      cont++;
+      idStep++;
     }
     
     let intHourTot = Math.trunc(this.orderSimulation?.TIETOTEST!);
@@ -334,6 +368,7 @@ export class SimulationDialogComponent implements OnInit {
     }
 
     let stepFour: ExecutionStep = {
+      id: idStep,
       icon: 'stop',
       label: fourLabel,
       isMain: true
@@ -342,8 +377,10 @@ export class SimulationDialogComponent implements OnInit {
     this.steps.push(stepFour);
     this.scrollToBottom();
     await this.timeout(750);
+    idStep++;
 
     let stepFive: ExecutionStep = {
+      id: idStep,
       icon: 'attach_money',
       label: 'Resultado del costo de los operadores de mantenimiento',
       isMain: true
@@ -352,6 +389,7 @@ export class SimulationDialogComponent implements OnInit {
     this.steps.push(stepFive);
     this.scrollToBottom();
     await this.timeout(750);
+    idStep++;
 
     let analisisObj = JSON.parse(this.orderSimulation?.ANALISIS!);
     let presonalObj = JSON.parse(this.orderSimulation?.PERSONAL!);
@@ -366,6 +404,7 @@ export class SimulationDialogComponent implements OnInit {
       for(const user of team){
         let usrLabel = `${user.USER}, ${teamConf.NOMB} - ${teamConf.ESP}, costo por hora: $${user.COST}, horas requeridas: ${user.HOURS}, costo total: $${parseInt(user.COST) * parseInt(user.HOURS)}`;
         let usrStep: ExecutionStep = {
+          id: idStep,
           icon: 'remove',
           label: usrLabel,
           isMain: false
@@ -376,10 +415,12 @@ export class SimulationDialogComponent implements OnInit {
         this.steps.push(usrStep);
         this.scrollToBottom();
         await this.timeout(500);
+        idStep++;
       }
     }
 
     let stepSix: ExecutionStep = {
+      id: idStep,
       icon: 'payments',
       label: `Costo total de los operadores: $${totalCost}`,
       isMain: true
@@ -387,6 +428,31 @@ export class SimulationDialogComponent implements OnInit {
 
     this.steps.push(stepSix);
     this.scrollToBottom();
+    idStep++;
+
+    this.simulating = false;
+    this.hasBeenSimulated = true;
+
+    try{
+      let idUser = localStorage.getItem('idusuario');
+      let idOrderEnc = await this._encService.encrypt(`${this.idOrder}`);
+      let formData = new FormData();
+
+      formData.append('id_user', idUser!);
+      formData.append('linea', '1');
+      formData.append('id_order', idOrderEnc);
+      formData.append('content', JSON.stringify(this.steps));
+
+      await lastValueFrom(this._prevMaintService.saveOrderSimulation(formData));
+    }catch(error: any){
+      if(error.error == undefined){
+        this.openSnackBar('Ocurrió un error inesperado.');
+      }else if(error.error.msg == undefined){
+        this.openSnackBar('Ocurrió un error inesperado.');
+      }else{
+        this.openSnackBar(error.error.msg );
+      }
+    }
   }
 
   async timeout(ms: number): Promise<void>{
@@ -404,4 +470,48 @@ export class SimulationDialogComponent implements OnInit {
       });
     }, 1);
   }
+
+  async printPDF(){
+    try{
+      this.openSnackBar('Generando PDF...');
+      let idUser = localStorage.getItem('idusuario');
+      let shortEnc = await lastValueFrom(this._encService.shortEncrypt(idUser!));
+      let idOrderEnc = await this._encService.encrypt(`${this.idOrder}`);
+      let idOrderShort = await lastValueFrom(this._encService.shortEncrypt(idOrderEnc));
+
+      let orderSimulation = await lastValueFrom(this._prevMaintService.printOrderSimulation(
+        idOrderShort.response.encrypted,
+        shortEnc.response.encrypted,
+        1
+      ))
+
+      let idFileEnc = await this._encService.encrypt(orderSimulation.response.fileID);
+      let idFileShort = await lastValueFrom(this._encService.shortEncrypt(idFileEnc));
+      let download = this._document.getElementById(`download`) as HTMLAnchorElement;
+
+      let downloadToken = await lastValueFrom(this._docMangeService.getDownloadToken(
+        idFileShort.response.encrypted,
+        shortEnc.response.encrypted,
+        1
+      ));
+
+      download.href = `http://git.ittec.mx/sam/public/api/download-file/${downloadToken.response.TOKEN}/${shortEnc.response.encrypted}/1`;
+      download.download = orderSimulation.response.fileID;
+      download.click();
+    }catch(error: any){
+      if(error.error == undefined){
+        this.openSnackBar('Ocurrió un error inesperado.');
+      }else if(error.error.msg == undefined){
+        this.openSnackBar('Ocurrió un error inesperado.');
+      }else{
+        this.openSnackBar(error.error.msg);
+      }
+    }
+  }
+
+  openSnackBar(msg: string){
+    this._snackBar.open(msg, undefined, {
+      duration: 2500,
+    })
+  }
 }

+ 3 - 1
sistema-mantenimiento-front/src/app/services/document-management/document-management.service.ts

@@ -23,7 +23,9 @@ export class DocumentManagementService {
     return this.postQuery('delete-file', body).pipe(map((data: any) => data));
   }
 
-  
+  getDownloadToken(idFile: string, idUser: string, line: number){
+    return this.getQuery(`get-download-token/${idFile}/${idUser}/${line}`).pipe(map((data: any) => data))
+  }
 
   getQuery(query: string) {
     const JWT = `Bearer ${localStorage.getItem('token')}`;

+ 8 - 0
sistema-mantenimiento-front/src/app/services/preventive-maintenance.service.ts

@@ -58,6 +58,10 @@ export class PreventiveMaintenanceService {
     return this.getQuery(`get-order-with-activator/${idOrder}/${idUser}/${line}`).pipe(map((data: any) => data))
   }
 
+  printOrderSimulation(idOrder: string, idUser: string, line: number){
+    return this.getQuery(`print-order-simulation/${idOrder}/${idUser}/${line}`).pipe(map((data: any) => data))
+  }
+
   registerWorkOrder(body: any){
     return this.postQuery("register-work-order", body).pipe(map((data: any) => data))
   }
@@ -109,6 +113,10 @@ export class PreventiveMaintenanceService {
   updateOrderWithActivator(body: any){
     return this.postQuery("update-order-with-activator", body).pipe(map((data: any) => data))
   }
+
+  saveOrderSimulation(body: any){
+    return this.postQuery("save-order-simulation", body).pipe(map((data: any) => data))
+  }
   
   getQuery(query: string, tk?: string){
     const URL = `${apiTemp}${query}`;