Bladeren bron

Avances empleado

Jose Brito 2 jaren geleden
bovenliggende
commit
72305ea7eb

+ 23 - 13
sistema-mantenimiento-back/app/Http/Controllers/EncryptionController.php

@@ -96,19 +96,29 @@ class EncryptionController extends Controller{
     }
 
     public function decrypt(string $value) : string {
-        $value_string = base64_decode($value);
-        $value_array = explode('|', $value_string);
-
-        $encoded_string = base64_decode($value_array[0]);
-        $nonce_string = base64_decode($value_array[1]);
-
-        $encryption_key_dec = sodium_crypto_box_keypair_from_secretkey_and_publickey(
-            base64_decode($this->secret_key), 
-            base64_decode($this->public_key)
-        );
-
-        $decrypted = sodium_crypto_box_open($encoded_string, $nonce_string, $encryption_key_dec);
-        return $decrypted;
+      if(gettype($value) != 'string') return false;
+      
+      $value_string = base64_decode($value);
+      $value_array = explode('|', $value_string);
+
+      if(count($value_array) <  2) return false;
+
+      $encoded_string = base64_decode($value_array[0]);
+      $nonce_string = base64_decode($value_array[1]);
+
+      $encryption_key_dec = sodium_crypto_box_keypair_from_secretkey_and_publickey(
+          base64_decode($this->secret_key), 
+          base64_decode($this->public_key)
+      );
+
+      try{
+          $decrypted = sodium_crypto_box_open($encoded_string, $nonce_string, $encryption_key_dec);
+          return $decrypted;
+      }catch(ErrorException $e){
+          return false;
+      }catch(SodiumException $e){
+          return false;
+      }
     }
 
     public function shortDec($enc){

+ 1 - 0
sistema-mantenimiento-front/.gitignore

@@ -41,6 +41,7 @@ yarn-error.log
 testem.log
 /typings
 /src/assets/icons.json
+/src/assets/img/flags/
 
 # System Files
 .DS_Store

+ 31 - 0
sistema-mantenimiento-front/src/app/components/personal-management/employee/employee-form/employee-form.component.css

@@ -62,4 +62,35 @@ input::-webkit-inner-spin-button {
 
 .fw_42{
   width: calc(500% / 12);
+}
+
+.fw_17{
+  width: calc(200% / 12);
+}
+
+.fw_75{
+  width: 75%;
+}
+
+.fw_100{
+  width: 100%;
+}
+
+.column-advices{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 100%;
+}
+
+.column-advices p{
+    animation-name: loading;
+    animation-duration: 5s;
+    animation-iteration-count: infinite;
+}
+
+@keyframes loading{
+    0% {color: black;}
+    50% {color: rgba(0, 0, 0, 0.25);}
+    100% {color: black;}
 }

+ 112 - 241
sistema-mantenimiento-front/src/app/components/personal-management/employee/employee-form/employee-form.component.html

@@ -25,8 +25,8 @@
       <div class="form-order-container" *ngIf="!isLoading && !hasError">
         <div class="form-column" [formGroup]="formGroup">
           <div class="form-row">
-            <!-- 100 => 12, 50 => 6, 42 => 5, 33 => 4, 25 => 3, 8 => 1 -->
-            <div class="form-cell pt-8 fw_50">
+            <!-- 100 => 12, 75 => 9, 50 => 6, 42 => 5, 33 => 4, 25 => 3, 17 => 2, 8 => 1 -->
+            <div class="form-cell pt-8" [ngClass]="{ fw_50: screenSize > 920, fw_100: screenSize <= 920 }">
               <mat-form-field appearance="outline">
                 <mat-label>Empleado</mat-label>
                 <mat-select formControlName="userId">
@@ -34,45 +34,48 @@
                     {{ user.NOMBRE }}
                   </mat-option>
                 </mat-select>
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['userId'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_50">
+            <div class="form-cell pt-8" [ngClass]="{ fw_50: screenSize > 920, fw_100: screenSize <= 920 }">
               <mat-form-field appearance="outline">
                 <mat-label>Equipo de Trabajo</mat-label>
-                <mat-select formControlName="workteamId">
-                  <mat-option *ngFor="let workteam of workteams" [value]="workteam.WORKTEAM_ID">{{ workteam.NAME }}</mat-option>
+                <mat-select formControlName="workteamId" (valueChange)="workteamChange(team.value)" #team>
+                  <mat-option value="-">Ninguno</mat-option>
+                  <mat-option *ngFor="let workteam of workteams" [value]="workteam.WORKTEAM_ID">
+                    {{ workteam.NAME }} - {{ workteam.SPECIALITY }}
+                  </mat-option>
                 </mat-select>
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['workteamId'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline">
                 <mat-label>Tipo de Contrato</mat-label>
                 <mat-select (ngModelChange)="changeContractType()" formControlName="contractType">
                   <mat-option value="Subcontratista">Subcontratista</mat-option>
                   <mat-option value="Interno">Interno</mat-option>
                 </mat-select>
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['contractType'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline">
                 <mat-label>Subcontratista</mat-label>
                 <mat-select formControlName="subcontratistId">
                   <mat-option *ngFor="let subcontratist of subcontratists" [value]="subcontratist.ID_SUBCONTRATIST">{{ subcontratist.NAME }}</mat-option>
                 </mat-select>
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['subcontratistId'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline">
                 <mat-label>Especialidad</mat-label>
                 <input matInput formControlName="speciality">
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['speciality'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>Nacionalidad</mat-label>
                 <mat-select (ngModelChange)="changeNationality()" formControlName="foreigner" #nat>
@@ -82,29 +85,29 @@
                 <mat-error *ngIf="formGroup.controls['foreigner'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline">
                 <mat-label>R.F.C.</mat-label>
-                <input matInput placeholder="Ingrese su R.F.C." oninput="javascript: this.value= this.value.toUpperCase();" formControlName="RFC" 
+                <input matInput oninput="javascript: this.value= this.value.toUpperCase();" formControlName="RFC" 
                 (input)="formGroup.controls['RFC'].markAsTouched()">
                 <mat-error *ngIf="formGroup.controls['RFC'].hasError('required')">Este campo es obligatorio.</mat-error>
-                <mat-error *ngIf="formGroup.controls['RFC'].hasError('minlength')">Este campo debe tener al menos 12 caracteres.</mat-error>
-                <mat-error *ngIf="formGroup.controls['RFC'].hasError('maxlength')">Este campo puede tener máximo 12 caracteres.</mat-error>
+                <mat-error *ngIf="formGroup.controls['RFC'].hasError('minlength')">Este campo debe tener al menos 13 caracteres.</mat-error>
+                <mat-error *ngIf="formGroup.controls['RFC'].hasError('maxlength')">Este campo puede tener máximo 13 caracteres.</mat-error>
                 <mat-error *ngIf="formGroup.controls['RFC'].hasError('pattern')">El formato del RFC ingresado es inválido.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_33">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>TAX ID</mat-label>
-                <input matInput placeholder="Ingrese su TAX ID" oninput="javascript: this.value= this.value.toUpperCase();" formControlName="tax" 
+                <input matInput oninput="javascript: this.value= this.value.toUpperCase();" formControlName="tax" 
                 (input)="formGroup.controls['tax'].markAsTouched()">
                 <mat-error *ngIf="formGroup.controls['tax'].hasError('required')">Este campo es obligatorio.</mat-error>
-                <mat-error *ngIf="formGroup.controls['tax'].hasError('minlength')">Este campo debe tener al menos 12 caracteres.</mat-error>
-                <mat-error *ngIf="formGroup.controls['tax'].hasError('maxlength')">Este campo puede tener máximo 12 caracteres.</mat-error>
+                <mat-error *ngIf="formGroup.controls['tax'].hasError('minlength')">Este campo debe tener al menos 13 caracteres.</mat-error>
+                <mat-error *ngIf="formGroup.controls['tax'].hasError('maxlength')">Este campo puede tener máximo 13 caracteres.</mat-error>
                 <mat-error *ngIf="formGroup.controls['tax'].hasError('pattern')">El formato del TAX ID ingresado es inválido.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>País</mat-label>
                 <input matInput formControlName="country" [matAutocomplete]="autocompleteCountry" id="country" 
@@ -118,7 +121,7 @@
                 <mat-error *ngIf="formGroup.controls['country'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100" *ngIf="!searchingStates">
                 <mat-label>Estado</mat-label>
                 <input matInput formControlName="federalEntity" [matAutocomplete]="autocompleteState" id="state">
@@ -131,7 +134,7 @@
               </mat-form-field>
               <mat-progress-bar mode="indeterminate" class="w-100" *ngIf="searchingStates"></mat-progress-bar>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100" *ngIf="!searchingCities">
                 <mat-label>Municipio</mat-label>
                 <input matInput formControlName="city" [matAutocomplete]="autocompleteCity" id="city">
@@ -144,7 +147,7 @@
               </mat-form-field>
               <mat-progress-bar mode="indeterminate" class="w-100" *ngIf="searchingCities"></mat-progress-bar>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100" *ngIf="!searchingTowns">
                 <mat-label>Localidad</mat-label>
                 <input matInput formControlName="town" [matAutocomplete]="autocompleteTown" id="town">
@@ -157,7 +160,7 @@
               </mat-form-field>
               <mat-progress-bar mode="indeterminate" class="w-100" *ngIf="searchingTowns"></mat-progress-bar>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100" *ngIf="!searchingSettings">
                 <mat-label>Colonia</mat-label>
                 <input matInput formControlName="suburb" [matAutocomplete]="autocompleteSetting" id="setting">
@@ -170,72 +173,75 @@
               </mat-form-field>
               <mat-progress-bar mode="indeterminate" class="w-100" *ngIf="searchingSettings"></mat-progress-bar>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100" *ngIf="!searchingAddress">
                 <mat-label>Código Postal</mat-label>
-                <input matInput placeholder="Ingrese su Código Postal" formControlName="postalCode" (input)="searchZipCode(zipCode.value)" #zipCode id="zipCode">
+                <input matInput formControlName="postalCode" (input)="searchZipCode(zipCode.value)" #zipCode id="zipCode">
                 <mat-error *ngIf="formGroup.controls['postalCode'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
               <mat-progress-bar mode="indeterminate" class="w-100" *ngIf="searchingAddress"></mat-progress-bar>
             </div>
-            <div class="form-cell pt-8 fw_50">
+            <div class="form-cell pt-8" [ngClass]="{ fw_50: screenSize > 920, fw_100: screenSize <= 920 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>Calle</mat-label>
-                <input matInput placeholder="Ingrese su Calle" formControlName="street" (input)="formGroup.controls['street'].markAsTouched()">
+                <input matInput formControlName="street" (input)="formGroup.controls['street'].markAsTouched()">
                 <mat-error *ngIf="formGroup.controls['street'].hasError('required')">Este campo es obligatorio.</mat-error>
                 <mat-error *ngIf="formGroup.controls['street'].hasError('maxlength')">Este campo puede tener máximo 150 caracteres.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>Número Exterior</mat-label>
-                <input type="text" matInput placeholder="Ingrese su Número Exterior" formControlName="exteriorNumber" 
-                (input)="formGroup.controls['exteriorNumber'].markAsTouched()">
+                <input type="text" matInput formControlName="exteriorNumber" (input)="checkNumber('exterior', exteriorNumber.value)" #exteriorNumber id="exterior">
                 <mat-error *ngIf="formGroup.controls['exteriorNumber'].hasError('required')">Este campo es obligatorio.</mat-error>
                 <mat-error *ngIf="formGroup.controls['exteriorNumber'].hasError('maxlength')">Este campo puede tener máximo 10 caracteres.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 920, fw_50: screenSize <= 920 && screenSize > 690, fw_100: screenSize <= 690 }">
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>Número Interior</mat-label>
-                <input type="text" matInput placeholder="Ingrese su Número Interior" formControlName="interiorNumber" 
-                (input)="formGroup.controls['interiorNumber'].markAsTouched()">
+                <input type="text" matInput formControlName="interiorNumber" (input)="checkNumber('interior', interiorNumber.value)" #interiorNumber id="interior">
                 <mat-error *ngIf="formGroup.controls['interiorNumber'].hasError('maxlength')">Este campo puede tener máximo 10 caracteres.</mat-error>
               </mat-form-field>
             </div>
             <div class="section-divider">Configuración del contacto de emergencia</div>
-            <div class="form-cell pt-8 fw_42">
+            <div class="form-cell pt-8" [ngClass]="{ fw_33: screenSize > 1320, fw_100: screenSize <= 1320 }">
               <mat-form-field appearance="outline">
                 <mat-label>Nombre del contacto de emergencia</mat-label>
-                <input  matInput formControlName="contactName">
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <input matInput formControlName="contactName">
+                <mat-error *ngIf="formGroup.controls['contactName'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_8">
+            <div class="form-cell pt-8" [ngClass]="{ fw_17: screenSize > 1320, fw_25: screenSize <= 1320 && screenSize > 840, fw_100: screenSize <= 840 }">
               <mat-form-field appearance="outline">
                 <mat-label>Lada</mat-label>
-                <mat-select formControlName="contactLada">
-                  <mat-option *ngFor="let country of countries" [value]="country.LADA">+{{country.LADA}}</mat-option>
-                </mat-select>
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <input matInput formControlName="contactLada" [matAutocomplete]="autocompleteLada1">
+                <mat-autocomplete autoActiveFirstOption #autocompleteLada1>
+                  <mat-option *ngFor="let lada1 of filteredLadas1 | async" [value]="lada1">
+                    <img [src]="'assets/img/flags/' + flags.get(lada1) + '.png'" width="16px" height="16px" style="margin: 4px 4px 0 0;">{{ lada1 }}
+                  </mat-option>
+                </mat-autocomplete>
+                <mat-error *ngIf="formGroup.controls['contactLada'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 1320, fw_75: screenSize <= 1320 && screenSize > 840, fw_100: screenSize <= 840 }">
               <mat-form-field appearance="outline">
                 <mat-label>Número Telefónico</mat-label>
-                <input matInput formControlName="contactTelephone">
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <input matInput formControlName="contactTelephone" (input)="checkPhoneNumber(phoneNumber.value)" #phoneNumber>
+                <mat-error *ngIf="formGroup.controls['contactTelephone'].hasError('required')">Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['contactTelephone'].hasError('minlength')">Este campo debe tener al menos 7 caracteres.</mat-error>
+                <mat-error *ngIf="formGroup.controls['contactTelephone'].hasError('maxlength')">Este campo puede tener máximo 11 caracteres.</mat-error>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_25">
+            <div class="form-cell pt-8" [ngClass]="{ fw_25: screenSize > 1320, fw_100: screenSize <= 1320 }">
               <mat-form-field appearance="outline">
                 <mat-label>Dirección</mat-label>
                 <input matInput formControlName="contactAddress">
-                <mat-error>Este campo es obligatorio.</mat-error>
+                <mat-error *ngIf="formGroup.controls['contactAddress'].hasError('required')">Este campo es obligatorio.</mat-error>
               </mat-form-field>
             </div>
             <div class="section-divider">Documentación del empleado</div>
-            <div class="form-cell pt-8 fw_50">
+            <div class="form-cell pt-8" [ngClass]="{ fw_50: screenSize > 920, fw_100: screenSize <= 920 }">
               <mat-form-field appearance="outline">
                 <mat-label>Tipo de Documento</mat-label>
                 <mat-select #documentType>
@@ -245,202 +251,67 @@
                 </mat-select>
               </mat-form-field>
             </div>
-            <div class="form-cell pt-8 fw_50">
+            <div class="form-cell pt-8" [ngClass]="{ fw_50: screenSize > 920, fw_100: screenSize <= 920 }">
               <button mat-stroked-button [disabled]="documentType.value == null || documentType.value == undefined || documentType.value == ''" 
-              style="margin-bottom: 20px; width: 100%;">
+              style="margin-bottom: 20px; width: 100%;" (click)="checkAttached(documentType.value)" *ngIf="!isUploading">
                 Adjuntar Archivo
               </button>
+              <div class="column-advices" *ngIf="isUploading">
+                <p>Subiendo archivo...</p>
+                <mat-progress-bar mode="buffer" class="w-100"></mat-progress-bar>
+              </div>
+              <input type="file" name="file" id="file" class="hidden" (change)="checkFile($event, documentType.value)">
+            </div>
+            <div class="form-cell pt-8 fw_100">
+              <table mat-table [dataSource]="dataSource!" style="width:100%">
+                <ng-container matColumnDef="ID">
+                  <th mat-header-cell *matHeaderCellDef>#</th>
+                  <td mat-cell *matCellDef="let row; let i = index">{{ i + 1 }}</td>
+                </ng-container>
+
+                <ng-container matColumnDef="NOMBRE">
+                  <th mat-header-cell *matHeaderCellDef>Nombre del archivo</th>
+                  <td mat-cell *matCellDef="let row">{{ row.name }}</td>
+                </ng-container>
+
+                <ng-container matColumnDef="PESO">
+                  <th mat-header-cell *matHeaderCellDef>Tamaño del archivo</th>
+                  <td mat-cell *matCellDef="let row">{{ row.size }}</td>
+                </ng-container>
+
+                <ng-container matColumnDef="TIPO">
+                  <th mat-header-cell *matHeaderCellDef>Tipo</th>
+                  <td mat-cell *matCellDef="let row">{{ fileTypes.get(row.type) }}</td>
+                </ng-container>
+
+                <ng-container matColumnDef="ACCIONES">
+                  <th mat-header-cell *matHeaderCellDef>Acciones</th>
+                  <td mat-cell *matCellDef="let row">
+                    <button mat-mini-fab color="warn" class="no-shadow" (click)="deleteFile(row.id)" [disabled]="disableDelete">
+                      <mat-icon>delete</mat-icon>
+                    </button>
+                    <a target="_blank" [id]="'download-' + row.id"></a>
+                  </td>
+                </ng-container>
+
+                <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+                <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
+
+                <tr class="mat-row" *matNoDataRow>
+                  <td class="mat-cell" colspan="100%" *ngIf="attached.length == 0" style="text-align: center;">
+                    Aún no se ha adjuntado nungún archivo
+                  </td>
+                </tr>
+              </table>
             </div>
           </div>
         </div>
       </div>
     </mat-card-content>
-  </mat-card>
-</div>
-<!-- CONTENEDOR PRINCIPAL -->
-<!--<main class="main-container animated fadeIn" (window:resize)="onResize()">
-
-<- REGRESAR A LA PANTALLA ANTERIOR ->
-<mat-card class="override-card-form override-elevation-z8" style="max-height: calc(100vh - 188px); overflow: auto;">
-  <section class="override-section">
-    <div class="ph-32" [formGroup]="formGroup">
-      <mat-grid-list  [cols]="12" rowHeight="82px">
-        <mat-grid-tile colspan="6" *ngIf="enable === '1' || enable === '2'">
-          <mat-form-field appearance="outline">
-            <mat-label>Empleado</mat-label>
-            <mat-select formControlName="userId">
-              <mat-option *ngFor="let user of users" [value]="user.ID_USER">{{ user.NAME }}</mat-option>
-            </mat-select>
-            <mat-error>El Campo Empleado es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="6" *ngIf="enable === '1' || enable === '2'">
-          <mat-form-field appearance="outline">
-            <mat-label>Equipo de Trabajo</mat-label>
-            <mat-select formControlName="workteamId">
-              <mat-option *ngFor="let workteam of workteams" [value]="workteam.WORKTEAM_ID">{{ workteam.NAME }}</mat-option>
-            </mat-select>
-            <mat-error>El Campo Equipo de Trabajo es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4">
-          <mat-form-field appearance="outline">
-            <mat-label>Nombre</mat-label>
-            <input  matInput placeholder="Ingrese el Nombre de su Contacto de Emergencia" maxlength="150" [readonly]="enable == '0' ? true : false"
-            formControlName="contactName">
-            <mat-error>El Campo Nombre es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="1" *ngIf="enable === '1' || enable === '2'">
-          <mat-form-field appearance="outline">
-            <mat-label>Lada</mat-label>
-            <mat-select formControlName="contactLada">
-              <mat-option *ngFor="let country of countries" [value]="country.LADA">+{{country.LADA}}</mat-option>
-            </mat-select>
-            <mat-error>El Campo Lada es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="3">
-          <mat-form-field appearance="outline">
-            <mat-label>Número Telefónico</mat-label>
-            <input matInput placeholder="Ingrese el Número Telefónico de su Contacto de Emergencia" [readonly]="enable == '0' ? true : false"
-            type="number" oninput="javascript: if (this.value.length> this.maxLength) this.value = this.value.slice(0, this.maxLength);" minlength="7" maxlength="10"
-            formControlName="contactTelephone">
-            <mat-error>El Campo Número Telefónico es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4">
-          <mat-form-field appearance="outline">
-            <mat-label>Dirección</mat-label>
-            <input matInput placeholder="Ingrese la Dirección de su Contacto de Emergencia" maxlength="150" [readonly]="enable == '0' ? true : false"
-            formControlName="contactAddress">
-            <mat-error>El Campo Dirección es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4" *ngIf="enable === '1' || enable === '2'">
-          <mat-form-field appearance="outline">
-            <mat-label>Tipo de Contrato</mat-label>
-            <mat-select (ngModelChange)="changeContractType()" formControlName="contractType">
-              <mat-option value="Subcontratista">Subcontratista</mat-option>
-              <mat-option value="Interno">Interno</mat-option>
-            </mat-select>
-            <mat-error>El Campo Tipo de Contrato es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4" *ngIf="enable === '1' || enable === '2'">
-          <mat-form-field appearance="outline">
-            <mat-label>Subcontratista</mat-label>
-            <mat-select formControlName="subcontratistId">
-              <mat-option *ngFor="let subcontratist of subcontratists" [value]="subcontratist.ID_SUBCONTRATIST">{{ subcontratist.NAME }}</mat-option>
-            </mat-select>
-            <mat-error>El Campo Subcontratista es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4">
-          <mat-form-field appearance="outline" >
-            <mat-label>Especialidad</mat-label>
-            <input  matInput placeholder="Ingrese su Especialidad" maxlength="150" [readonly]="enable == '0' ? true : false" formControlName="speciality">
-            <mat-error>El Campo Especialidad es Obligatorio</mat-error>
-          </mat-form-field>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4" *ngIf="enable == '1' || enable == '2'">
-          <input appearance="outline" hidden type="file" name="fileAutho" id="fileAutho" #fileAutho (change)="checkFile($event,'Autho')" accept="application/pdf">
-          <button  type="button" mat-stroked-button matTooltip="Seleccione 1 Archivo PDF"
-          (click)="fileAutho.click()" [disabled]="authoDoc.length >= 1 || authoButton" >
-              Adjuntar Autorización
-          </button>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4" *ngIf="enable == '1' || enable == '2'">
-          <input appearance="outline" hidden type="file" name="fileAutho2" id="fileAutho2" #fileAutho2 (change)="checkFile($event,'Autho2')" accept="application/pdf">
-          <button type="button" mat-stroked-button matTooltip="Seleccione 1 Archivo PDF"
-          (click)="fileAutho2.click()" [disabled]="authoDoc2.length >= 1 || authoButton" >
-            Adjuntar Autorización
-          </button>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="4" *ngIf="enable == '1' || enable == '2'">
-          <input hidden type="file" name="fileOffi" id="fileOffi" #fileOffi (change)="checkFile($event,'Office')" accept="application/pdf">
-          <button type="button" mat-stroked-button matTooltip="Seleccione 1 Archivo PDF"
-          (click)="fileOffi.click()" [disabled]="officeDoc.length >= 1 || officeButton" >
-            Adjuntar Oficio
-          </button>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="6" *ngIf="enable == '1' || enable == '2'">
-          <input hidden type="file" name="fileCert" id="fileCert" #fileCert (change)="checkFile($event,'Cert')" accept="application/pdf">
-          <button  type="button" mat-stroked-button matTooltip="Seleccione 1 Archivo PDF"
-          (click)="fileCert.click()" [disabled]="certDoc.length >= 1 || certButton" >
-            Adjuntar Certificación
-          </button>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="6" *ngIf="enable == '1' || enable == '2'">
-          <input hidden type="file" name="fileCert2" id="fileCert2" #fileCert2 (change)="checkFile($event,'Cert2')" accept="application/pdf">
-          <button  type="button" mat-stroked-button matTooltip="Seleccione 1 Archivo PDF"
-          (click)="fileCert2.click()" [disabled]="certDoc2.length >= 1 || certButton" >
-            Adjuntar Certificación
-          </button>
-        </mat-grid-tile>
-        <mat-grid-tile colspan="12" *ngIf="enable === '1' && idEmployee !== '0'">
-          <mat-form-field appearance="outline" style="width: 50%!important;">
-            <mat-label>Actualizar documento</mat-label>
-            <mat-select formControlName="updateDocuments" (valueChange)="saveDocumentToUpdate($event)">
-              <mat-option *ngFor="let document of documentsEmployee" [value]="document">{{ document }}</mat-option>
-            </mat-select>
-          </mat-form-field>
-        </mat-grid-tile>
-      </mat-grid-list>
-      <table *ngIf="enable == '1' || enable == '2'"  mat-table [dataSource]="dataSource!" style="padding: 0x 20px 10px 20px!important;">
-        <ng-container matColumnDef="name">
-          <th mat-header-cell *matHeaderCellDef > Nombre </th>
-          <td mat-cell *matCellDef="let element"> {{element.file.name}} </td>
-        </ng-container>
-        <ng-container matColumnDef="size">
-          <th mat-header-cell *matHeaderCellDef > Tamaño </th>
-          <td mat-cell *matCellDef="let element"> {{element.file.size}} </td>
-        </ng-container>
-        <ng-container matColumnDef="type">
-          <th mat-header-cell *matHeaderCellDef > Tipo Documento </th>
-          <td mat-cell *matCellDef="let element"> {{element.type}} </td>
-        </ng-container>
-        <ng-container matColumnDef="update" style="width:25%!important;">
-          <th mat-header-cell *matHeaderCellDef > Actualizar documento </th>
-          <td mat-cell *matCellDef="let element"> {{element.update === '' ? 'No Aplica' : element.update}} </td>
-        </ng-container>
-        <ng-container matColumnDef="action">
-          <th mat-header-cell *matHeaderCellDef > Acciones </th>
-          <td mat-cell *matCellDef="let element"> <button
-            mat-mini-fab
-            color="warn"
-            class="override_no_shadow mr-4"
-            matTooltip="Eliminar Archivo"
-            (click)="deleteFiles(element)">
-            <mat-icon>delete</mat-icon>
-          </button></td>
-        </ng-container>
-        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
-        <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
-      </table>
-    </div>
-    <div style="padding: 10px 32px 22px 32px;" align="end">
-      <button mat-raised-button color="primary" class="override_no_shadow" (click)="this.alert()" *ngIf="enable == '1' || enable == '2'" [disabled]="formGroup.invalid" ><mat-icon>save</mat-icon>{{ idEmployee == "0" ? "Registrar" : "Guardar" }}</button>
-      <button
-        *ngIf="enable === '0'"
-        mat-mini-fab
-        color="warn"
-        class="override_no_shadow mr-4"
-        [disabled]="isLoadingForm"
-        matTooltip="Ficha en Pdf"
-        (click)="openDialog('PDF')">
-        <mat-icon>picture_as_pdf</mat-icon>
-      </button>
-      <button *ngIf="enable === '0'"
-        mat-mini-fab
-        color="primary"
-        class="override_no_shadow mr-4"
-        [disabled]="isLoadingForm"
-        matTooltip="Ficha en Excel"
-        (click)="openDialog('EXC')"
-        downoload="asd.csv">
-        <mat-icon>text_snippet</mat-icon>
+    <mat-card-actions align="end">
+      <button mat-button [disabled]="formGroup.invalid || isLoadingForm" (click)="alert()">
+        <mat-icon>save</mat-icon>{{ idEmployee == "0" ? "Registrar" : "Guardar" }}
       </button>
-    </div>
-  </section>
-</mat-card>-->
+    </mat-card-actions>
+  </mat-card>
+</div>

+ 482 - 168
sistema-mantenimiento-front/src/app/components/personal-management/employee/employee-form/employee-form.component.ts

@@ -1,10 +1,8 @@
-import { HttpErrorResponse } from '@angular/common/http';
 import { Component, Inject, OnInit } from '@angular/core';
 import { FormControl, FormGroup, Validators } from '@angular/forms';
 import { MatDialog } from '@angular/material/dialog';
 import { MatTableDataSource } from '@angular/material/table';
-import { ActivatedRoute, Router } from '@angular/router';
-import countryCodeEmoji from 'country-code-emoji';
+import { ActivatedRoute } from '@angular/router';
 import { Observable, lastValueFrom, map, startWith } from 'rxjs';
 import { AlertComponent } from 'src/app/components/resources/dialogs/alert/alert.component';
 import { CitySettings, CitySettingsResponse } from 'src/app/interfaces/city-settings.interface';
@@ -13,12 +11,10 @@ import { CountriesList, CountriesListResponse, LadaSelection } from 'src/app/int
 import { CountryState, CountryStatesResponse } from 'src/app/interfaces/country-states.interface';
 import { DownoloadEmployeeInfo, Employee, FilesDocuments } from 'src/app/interfaces/personal-managment/employee.interface';
 import { SubcontratistItem, SubcontratistsResponse } from 'src/app/interfaces/personal-managment/subcontratists.interface';
-import { ResponseData } from 'src/app/interfaces/response-data';
 import { StateCitiesResponse, StateCity } from 'src/app/interfaces/state-cities.interface';
 import { UsersResponse, UserResponse } from 'src/app/interfaces/users.interface';
 import { EncService } from 'src/app/services/enc/enc.service';
 import { FunctionsService } from 'src/app/services/functions.service';
-import { LocateService } from 'src/app/services/locate/locate.service';
 import { EmployeeService } from 'src/app/services/personal-management/employee.service';
 import { SubcontratistService } from 'src/app/services/personal-management/subcontratist.service';
 import { WorkTeamService } from 'src/app/services/personal-management/work-team.service';
@@ -26,9 +22,11 @@ import { ResourcesService } from 'src/app/services/resources/resources.service';
 import { SystemAdminService } from 'src/app/services/system-admin.service';
 import { UsersProfilesService } from 'src/app/services/users-profiles.service';
 import { hostJean } from 'src/environments/environment';
-import flag from 'country-code-emoji';
 import { DOCUMENT } from '@angular/common';
 import { AddressByZipCodeResponse } from 'src/app/interfaces/address-by-zip-code.interface';
+import { TempFileInfo } from 'src/app/interfaces/temp-file-info.interface';
+import { availableFiles } from 'src/environments/environment.prod';
+import { GdelService } from 'src/app/services/document-management/gdel.service';
 
 @Component({
   selector: 'app-employee-form',
@@ -63,13 +61,15 @@ export class EmployeeFormComponent implements OnInit {
   public formData: FormData = new FormData();
   public documentsEmployee!: Array<any>;
 
-  public dataSource: MatTableDataSource<any>;
-  public displayedColumns: string[] = ['name', 'size', 'type', 'update', 'action'];
+  public dataSource: MatTableDataSource<TempFileInfo>;
+  public displayedColumns: string[] =  ['ID', 'NOMBRE', 'PESO', 'TIPO', 'ACCIONES'];
   public documents!: Array<FilesDocuments>;
 
   public isLoading: boolean;
   public hasError: boolean;
   public errorStr: string;
+  public isUploading: boolean;
+  public disableDelete: boolean;
   public users: UserResponse[];
   public enableSearchByZipCode: boolean;
   public countryStates: CountryState[];
@@ -90,6 +90,7 @@ export class EmployeeFormComponent implements OnInit {
   filteredCities?: Observable<string[]>;
   filteredTowns?: Observable<string[]>;
   filteredSettings?: Observable<string[]>;
+  filteredLadas1?: Observable<string[]>;
 
   public searchingStates: boolean;
   public citiesInput: boolean;
@@ -98,6 +99,11 @@ export class EmployeeFormComponent implements OnInit {
   public searchingSettings: boolean;
   public searchingAddress: boolean;
 
+  flags: Map<string, string>;
+  attached: TempFileInfo[];
+  fileTypes: Map<string, string>;
+  screenSize: number;
+
   constructor(
     @Inject(DOCUMENT) private _document: Document,
     private _activatedRoute: ActivatedRoute,
@@ -107,10 +113,10 @@ export class EmployeeFormComponent implements OnInit {
     private _subcontratistsService: SubcontratistService,
     private _encService: EncService,
     private _sysAdminService: SystemAdminService,
-    private _router: Router,
     private _dialog: MatDialog,
     private _usersProfilesService: UsersProfilesService,
     private _functionsService: FunctionsService,
+    private _gdelService: GdelService,
   ) {
     this.action = "";
     this.idEmployee = this._activatedRoute.snapshot.paramMap.get('id')!;
@@ -139,6 +145,8 @@ export class EmployeeFormComponent implements OnInit {
     this.isLoading = true;
     this.hasError = false;
     this.errorStr = "";
+    this.isUploading = false;
+    this.disableDelete = false;
     this.users = [];
     this.enableSearchByZipCode = true;
     this.countryStates = [];
@@ -160,14 +168,19 @@ export class EmployeeFormComponent implements OnInit {
     this.searchingTowns = false;
     this.searchingSettings = false;
     this.searchingAddress = false;
+
+    this.flags = new Map();
+    this.attached = [];
+    this.fileTypes = new Map();
+    this.screenSize = window.innerWidth;
   }
 
   ngOnInit(): void {
     this.init();
-    /*this.decript();
-    this.getWorkteams();
-    this.getSubcontratists();
-    this.getCountries();*/
+
+    this.fileTypes.set('AU', 'Autorización');
+    this.fileTypes.set('OF', 'Oficio');
+    this.fileTypes.set('CE', 'Certificación');
 
     this.formGroup.controls['federalEntity'].valueChanges.subscribe(value => {
       let countryVal = this.formGroup.controls['country'].value;
@@ -272,6 +285,11 @@ export class EmployeeFormComponent implements OnInit {
       startWith(''),
       map(value => this._filterSettings(value || ''))
     );
+
+    this.filteredLadas1 = this.formGroup.controls['contactLada'].valueChanges.pipe(
+      startWith(''),
+      map(value => this._fiterLada1(value || ''))
+    );
   }
 
   private _filterCountries(value: string): string[]{
@@ -299,6 +317,11 @@ export class EmployeeFormComponent implements OnInit {
     return this.citySettingsStr.filter(item => item.toLowerCase().includes(filterValue));
   }
 
+  private _fiterLada1(value: string): string[]{
+    const filterValue = value.toLowerCase();
+    return this.ladasStr.filter(item => item.toLowerCase().includes(filterValue));
+  }
+
   checkIfAddress(value: string){
     if(this.formGroup.controls['foreigner'].value == 'Si' && value == 'México (MEX)') return;
     if(this.formGroup.controls['foreigner'].value == 'No' && value != 'México (MEX)') return;
@@ -764,11 +787,11 @@ export class EmployeeFormComponent implements OnInit {
       userId: new FormControl({ value: '', disabled: false }, Validators.required),
       workteamId: new FormControl('', Validators.required),
       contractType: new FormControl('', Validators.required),
-      subcontratistId: new FormControl('', Validators.required),
-      speciality: new FormControl('', Validators.required),
+      subcontratistId: new FormControl({value: '', disabled: true}, Validators.required),
+      speciality: new FormControl({value: '', disabled: true}, Validators.required),
       foreigner: new FormControl('', Validators.required),
-      RFC: new FormControl({value: '', disabled: true}, [Validators.required, Validators.minLength(12), Validators.maxLength(12), Validators.pattern(new RegExp(/^([A-ZÑ&]{3,4}) ?(?:- ?)?(\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])) ?(?:- ?)?([A-Z\d]{2})([A\d])$/))]),
-      tax: new FormControl({value: '', disabled: true}, [Validators.required, Validators.minLength(12), Validators.maxLength(12), Validators.pattern(new RegExp(/^([A-ZÑ&]{3,4}) ?(?:- ?)?(\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])) ?(?:- ?)?([A-Z\d]{2})([A\d])$/))]),
+      RFC: new FormControl({value: '', disabled: true}, [Validators.required, Validators.minLength(13), Validators.maxLength(13), Validators.pattern(new RegExp(/^([A-ZÑ&]{3,4}) ?(?:- ?)?(\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])) ?(?:- ?)?([A-Z\d]{2})([A\d])$/))]),
+      tax: new FormControl({value: '', disabled: true}, [Validators.required, Validators.minLength(13), Validators.maxLength(13), Validators.pattern(new RegExp(/^([A-ZÑ&]{3,4}) ?(?:- ?)?(\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])) ?(?:- ?)?([A-Z\d]{2})([A\d])$/))]),
       country: new FormControl({value: '', disabled: true}, Validators.required),
       federalEntity: new FormControl({value: '', disabled: true}, Validators.required),
       city: new FormControl({value: '', disabled: true}, Validators.required),
@@ -778,14 +801,10 @@ export class EmployeeFormComponent implements OnInit {
       exteriorNumber: new FormControl('', [Validators.required, Validators.maxLength(10)]),
       interiorNumber: new FormControl('', Validators.maxLength(10)),
       postalCode: new FormControl('', Validators.required),
-      documentsAutho: new FormControl(''),
-      documentOffice: new FormControl(''),
-      documentsCert: new FormControl(''),
       contactName: new FormControl('', Validators.required),
       contactLada: new FormControl('', Validators.required),
-      contactTelephone: new FormControl('', Validators.required),
+      contactTelephone: new FormControl('', [Validators.required, Validators.minLength(7), Validators.maxLength(11)]),
       contactAddress: new FormControl('', Validators.required),
-      updateDocuments: new FormControl(''),
     })
   }
 
@@ -798,18 +817,24 @@ export class EmployeeFormComponent implements OnInit {
   }
 
   public changeContractType() {
-    if (this.enable == "1" || this.enable == "2") {
-      if (this.contractType?.value == "Subcontratista") {
-        this.subcontratistId?.enable();
-      } else {
-        this.subcontratistId?.disable();
-        this.subcontratistId?.setValue(null);
-      }
+    let contractType = this.formGroup.controls['contractType'].value;
+
+    if (contractType == "Subcontratista") {
+      this.formGroup.controls['subcontratistId'].enable();
+    } else {
+      this.formGroup.controls['subcontratistId'].disable();
+      this.formGroup.controls['subcontratistId'].setValue(null);
     }
   }
 
   public alert() {
     if (this.idEmployee == "0") {
+      let docsAU = this.attached.filter(item => item.type == 'AU');
+      if(docsAU.length < 1){
+        this._resourcesService.openSnackBar("Para registrar al empleado se requiere adjuntar al menos un documento de autorización.");
+        return;
+      }
+
       this._dialog.open(AlertComponent, {
         data: {
           title: 'Registrar Empleado',
@@ -840,25 +865,222 @@ export class EmployeeFormComponent implements OnInit {
   }
 
   private async storeEmployee() {
-    this.isLoadingForm = true;
+    try{
+      if(this.formGroup.invalid){
+        this._resourcesService.openSnackBar("Por favor complete todos los campos requeridos del formulario.");
+        this.formGroup.markAllAsTouched();
+      }else{
+        let formValue = this.formGroup.getRawValue();
+        let formData = new FormData();
+        let subcontratist = formValue.subcontratistId == null ? '-' : formValue.subcontratistId;
+        let idUser = localStorage.getItem('idusuario')!;
+
+        formData.append('id_user', idUser);
+        formData.append('linea', '1');
+        formData.append('USER_ID', formValue.userId);
+        formData.append('WORKTEAM_ID', formValue.workteamId);
+        formData.append('CONTRACT_TYPE', formValue.contractType);
+        formData.append('SUBCONTRATIST_ID', subcontratist);
+        formData.append('SPECIALITY', formValue.speciality);
+        formData.append('FOREIGNER', formValue.foreigner);
+
+        let rfc = formValue.RFC == null ? '-' : formValue.RFC;
+        let tax = formValue.tax == null ? '-' : formValue.tax;
+
+        formData.append('RFC', rfc);
+        formData.append('TAX', tax);
+  
+        let country = formValue.country;
+        let countryArr = country.split('(').reverse();
+        let countryCode = countryArr[0].replace(')', '').trim();
+        let validCountry = this.countriesStr.filter(item => item == country);
+    
+        if(validCountry.length == 0){
+          this._resourcesService.openSnackBar(`El país ${country} no se encuentra registrado en el catálogo de Paises del SAT.`);
+          this.formGroup.controls['country'].setValue(null);
+          return;
+        }
+    
+        formData.append('COUNTRY', countryCode);
+        if(countryCode == 'MEX' || countryCode == 'USA' || countryCode == 'CAN'){
+          let stateCode = "";
+    
+          if(this.enableSearchByZipCode){
+            let stateInput = this._document.getElementById('state') as HTMLInputElement;
+            let state = stateInput.value;
+            let stateArr = state.split('(').reverse();
+            stateCode = stateArr[0].replace(')', '').trim();
+          }else{
+            let state = formValue.federalEntity;
+            let stateArr = state.split('(').reverse();
+            stateCode = stateArr[0].replace(')', '').trim();
+    
+            let validState = this.countryStatesStr.filter(item => item == state);
+    
+            if(validState.length == 0){
+              this._resourcesService.openSnackBar(`El estado ${state} no está asociado al país ${country}.`);
+              this.formGroup.controls['federalEntity'].setValue(null);
+              return;
+            }
+          }
+    
+          formData.append('FEDERAL_ENTITY', stateCode);
+          if(countryCode == 'MEX'){
+            let cityCode = "";
+    
+            if(this.enableSearchByZipCode){
+              let cityInput = this._document.getElementById('city') as HTMLInputElement;
+    
+              if(cityInput.value == 'Sin municipio'){
+                cityCode = '-';
+              }else{
+                let city = cityInput.value;
+                let cityArr = city.split('(').reverse();
+                cityCode = cityArr[0].replace(')', '').trim();
+              }
+            }else{
+              let city = formValue.city;
+    
+              if(city == 'Sin municipio'){
+                cityCode = '-';
+              }else{
+                let cityArr = city.split('(').reverse();
+                cityCode = cityArr[0].replace(')', '').trim();
+                let validCity = this.stateCitiesStr.filter(item => item == city);
+    
+                if(validCity.length == 0){
+                  this._resourcesService.openSnackBar(`La ciudad ${city} no se encuentra asociada al estado ${stateCode}.`);
+                  this.formGroup.controls['city'].setValue(null);
+                  return;
+                }
+              }
+            }
+    
+            formData.append('CITY', cityCode);
+            let townCode = "";
+    
+            if(this.enableSearchByZipCode){
+              let townInput = this._document.getElementById('town') as HTMLInputElement;
+    
+              if(townInput.value == 'Sin localidad'){
+                townCode = '-';
+              }else{
+                let town = townInput.value;
+                let townArr = town.split('(').reverse();
+                townCode = townArr[0].replace(')', '').trim();
+              }
+            }else{
+              let town = formValue.town;
+    
+              if(town == 'Sin localidad'){
+                townCode = '-';
+              }else{
+                let townArr = town.split('(').reverse();
+                townCode = townArr[0].replace(')', '').trim();
+                let validTown = this.cityTownsStr.filter(item => item == town);
+    
+                if(validTown.length == 0){
+                  this._resourcesService.openSnackBar(`La localidad ${town} no se encuentra asociada a la ciudad ${cityCode}.`);
+                  this.formGroup.controls['town'].setValue(null);
+                  return;
+                }
+              }
+            }
+    
+            formData.append('TOWN', townCode);
+            let setting = formValue.suburb;
+            let settingArr = setting.split('(').reverse();
+            let settingCode = settingArr[0].replace(')', '').trim();
+            let validSetting = this.citySettingsStr.filter(item => item == setting);
+    
+            if(validSetting.length == 0){
+              this._resourcesService.openSnackBar(`La colonia ${country} no se encuentra relacionada a la ciudad ${cityCode}.`);
+              this.formGroup.controls['suburb'].setValue(null);
+              return;
+            }
+    
+            formData.append('SUBURB', settingCode);
+          }else{
+            let townExt = '-';
+            if(formValue.town != '' && formValue.town != null && formValue.town != undefined){
+              townExt = formValue.town;
+            }
+  
+            formData.append('CITY', formValue.city);
+            formData.append('TOWN', townExt);
+            formData.append('SUBURB', formValue.suburb);
+          }
+    
+          formData.append('POSTAL_CODE', formValue.postalCode);
+        }else{
+          let townExt = '-';
+          if(formValue.town != '' && formValue.town != null && formValue.town != undefined){
+            townExt = formValue.town;
+          }
+  
+          formData.append('FEDERAL_ENTITY', formValue.federalEntity);
+          formData.append('CITY', formValue.city);
+          formData.append('TOWN', townExt);
+          formData.append('SUBURB', formValue.suburb);
+          formData.append('POSTAL_CODE', formValue.postalCode);
+        }
+  
+        let intNum = '0';
+        if(formValue.interiorNumber != '' && formValue.interiorNumber != null && formValue.interiorNumber != undefined){
+          intNum = formValue.interiorNumber;
+        }
+  
+        formData.append('STREET', formValue.street);
+        formData.append('EXTERIOR_NUMBER', formValue.exteriorNumber);
+        formData.append('INTERIOR_NUMBER', intNum);
+        formData.append('CONTACT_NAME', formValue.contactName);
+  
+        let lada = formValue.contactLada;
+        let ladaArr = lada.split('-');
+        ladaArr.pop();
+        
+        let ladaStr = ladaArr.join('-');
+        formData.append('CONTACT_LADA', ladaStr);
+        formData.append('CONTACT_TELEPHONE', formValue.contactTelephone);
+        formData.append('CONTACT_ADDRESS', formValue.contactAddress);
+
+        let attachedStr = JSON.stringify(this.attached);
+        formData.append('ATTACHED', attachedStr);
+        console.log(formData);
+      }
+    }catch(error: any){
+      this.isUploading = false;
+      let msg = "";
+      if(error.error == undefined){
+        msg = 'Ocurrió un error insperado.';
+      }else if(error.error.msg == undefined){
+        msg = 'Ocurrió un error insperado.';
+      }else{
+        msg = error.error.msg;
+      }
+
+      this._resourcesService.openSnackBar(msg);
+      this.disableDelete = false;
+    }
+    /*this.isLoadingForm = true;
     await lastValueFrom(this._employeeService.storeEmployee(await this.employeeObject())).then(
       (responseData: ResponseData) => {
         this._resourcesService.openSnackBar("¡Registro Exitoso!");
         this._router.navigate(["/sam/GPRS/employee"]);
       }, (error: HttpErrorResponse) => this._resourcesService.checkErrors(error)
     );
-    this.isLoadingForm = false;
+    this.isLoadingForm = false;*/
   }
 
   private async updateEmployee() {
-    this.isLoadingForm = true;
+    /*this.isLoadingForm = true;
     await lastValueFrom(this._employeeService.updateEmployee(await this.employeeObject(), this.idEmployee)).then(
       (responseData: ResponseData) => {
         this._resourcesService.openSnackBar("Actualización Exitosa!");
         this._router.navigate(["/sam/GPRS/employee"]);
       }, (error: HttpErrorResponse) => this._resourcesService.checkErrors(error)
     );
-    this.isLoadingForm = false;
+    this.isLoadingForm = false;*/
   }
 
   private async getWorkteams() {
@@ -937,64 +1159,34 @@ export class EmployeeFormComponent implements OnInit {
         let mex = countries.response.filter(item => item.COUNTRY_ID == 'MEX');
         this.countries.push(mex[0]);
         this.countriesStr.push(`${mex[0].NAME} (${mex[0].COUNTRY_ID})`);
-
-        let mexLada: LadaSelection = {
-          FLAG: [mex[0].NOMECLARUTA_ISO2!].map(countryCodeEmoji)[0],
-          LADA: mex[0].LADA!
-        };
-        
-        this.ladas.push(mexLada);
-        this.ladasStr.push(`${mexLada.FLAG} ${mexLada.LADA}`);
+        this.flags.set(`${mex[0].LADA!}-${mex[0].NOMECLARUTA_ISO2!}`, mex[0].NOMECLARUTA_ISO2!);
+        this.ladasStr.push(`${mex[0].LADA!}-${mex[0].NOMECLARUTA_ISO2!}`);
 
         let usa = countries.response.filter(item => item.COUNTRY_ID == 'USA');
         this.countries.push(usa[0]);
         this.countriesStr.push(`${usa[0].NAME} (${usa[0].COUNTRY_ID})`);
-
-        let usaLada: LadaSelection = {
-          FLAG: [usa[0].NOMECLARUTA_ISO2!].map(countryCodeEmoji)[0],
-          LADA: usa[0].LADA!
-        };
-        
-        this.ladas.push(usaLada);
-        this.ladasStr.push(`${usaLada.FLAG} ${usaLada.LADA}`);
+        this.flags.set(`${usa[0].LADA!}-${usa[0].NOMECLARUTA_ISO2!}`, usa[0].NOMECLARUTA_ISO2!);
+        this.ladasStr.push(`${usa[0].LADA!}-${usa[0].NOMECLARUTA_ISO2!}`);
 
         let can = countries.response.filter(item => item.COUNTRY_ID == 'CAN');
         this.countries.push(can[0]);
         this.countriesStr.push(`${can[0].NAME} (${can[0].COUNTRY_ID})`);
-
-        let canLada: LadaSelection = {
-          FLAG: [can[0].NOMECLARUTA_ISO2!].map(countryCodeEmoji)[0],
-          LADA: can[0].LADA!
-        };
-        
-        this.ladas.push(canLada);
-        this.ladasStr.push(`${canLada.FLAG} ${canLada.LADA}`);
+        this.flags.set(`${can[0].LADA!}-${can[0].NOMECLARUTA_ISO2!}`, can[0].NOMECLARUTA_ISO2!);
+        this.ladasStr.push(`${can[0].LADA!}-${can[0].NOMECLARUTA_ISO2!}`);
         
         countries.response.forEach(country => {
           if(country.COUNTRY_ID != null && country.COUNTRY_ID != 'MEX' && country.COUNTRY_ID != 'USA' && country.COUNTRY_ID != 'CAN'){
             this.countries.push(country);
             this.countriesStr.push(`${country.NAME} (${country.COUNTRY_ID})`);
-
-            try{
-              flag(country.NOMECLARUTA_ISO2!);
-              let hasLada = country.LADA != '';
-
-              if(hasLada){
-                let lada: LadaSelection = {
-                  FLAG: [country.NOMECLARUTA_ISO2!].map(countryCodeEmoji)[0],
-                  LADA: country.LADA!
-                };
-
-                this.ladas.push(lada);
-                this.ladasStr.push(`${lada.FLAG} ${lada.LADA}`);
-              }
-            }catch(error: any){
-              //Saltar país
+            
+            if(country.LADA != '' && country.NOMECLARUTA_ISO2 != '' && country.NOMECLARUTA_ISO2 != '0'){
+              this.flags.set(`${country.LADA!}-${country.NOMECLARUTA_ISO2!}`, country.NOMECLARUTA_ISO2!);
+              this.ladasStr.push(`${country.LADA!}-${country.NOMECLARUTA_ISO2!}`);
             }
           }
         });
       }
-
+      
       this.isLoading = false;
     }catch(error: any){
       if(error.error == undefined){
@@ -1056,7 +1248,7 @@ export class EmployeeFormComponent implements OnInit {
   }
 
   private async putDataOnForm(employee: Employee) {
-    this.userId.setValue(employee.USER_ID)
+    /*this.userId.setValue(employee.USER_ID)
     this.contactName.setValue(await this._encService.decrypt(employee.CONTACT_NAME))
     this.contactTelephone.setValue(await this._encService.decrypt(employee.CONTACT_TELEPHONE))
     this.contactLada.setValue(await this._encService.decrypt(employee.CONTACT_LADA))
@@ -1064,10 +1256,10 @@ export class EmployeeFormComponent implements OnInit {
     this.contractType.setValue(employee.CONTRACT_TYPE)
     this.speciality.setValue(employee.SPECIALITY)
     this.subcontratistId.setValue(employee.SUBCONTRATIST_ID)
-    this.workteamId.setValue(employee.WORKTEAM_ID)
+    this.workteamId.setValue(employee.WORKTEAM_ID)*/
   }
 
-  private async employeeObject(): Promise<FormData> {
+  /*private async employeeObject(): Promise<FormData> {
     this.formData.append('USER_ID', this.userId.value);
 
     if (this.officeDoc.length > 0) { this.formData.append('DOCUMENT_OFFICE', this.officeDoc[0]); }
@@ -1096,7 +1288,7 @@ export class EmployeeFormComponent implements OnInit {
     this.formData.append('SAVED_BY_USER', this._resourcesService.getUser());
     this.formData.append('LINE_NUMBER', "1");
     return this.formData;
-  }
+  }*/
 
   public saveDocumentToUpdate(event: string) {
     this.nameUpdateDocument = event;
@@ -1132,84 +1324,110 @@ export class EmployeeFormComponent implements OnInit {
   }
 
   public checkFile(event: any, type: string) {
-    let file: any = event.target.files[0];
+    if(type != 'AU' && type != 'OF' && type != 'CE'){
+      this._resourcesService.openSnackBar(`El tipo de archivo ${type} no está soportado.`);
+    }else{
+      let extArr = event.target.files[0].name.split(".").reverse();
+      let extStr = extArr[0];
 
-    if (file.name.split(".")[1] != 'pdf') {
-      return this._resourcesService.openSnackBar('Solo se permiten archivos PDF');
-    }
+      if(type == 'AU'){
+        let supportedFilesAU = ['pdf', 'doc', 'docx', 'xls', 'xlsx'];
 
-    if (type == "Autho") {
-      this.documents.push({
-        index: 0,
-        file: file,
-        type: "Autorización",
-        update: this.nameUpdateDocument
-      });
-      this.authoDoc.push(file);
-      if (this.nameUpdateDocument != "") {
-        this.authoDoc.push(this.nameUpdateDocument);
+        if(!supportedFilesAU.includes(extStr)){
+          this._resourcesService.openSnackBar(`La extensión ${extStr} no está soportado para autorizaciones.`);
+          return;
+        }
       }
-    }
 
-    if (type == "Autho2") {
-      this.documents.push({
-        index: 1,
-        file: file,
-        type: "Autorización",
-        update: this.nameUpdateDocument
-      });
-      this.authoDoc2.push(file);
-      if (this.nameUpdateDocument != "") {
-        this.authoDoc2.push(this.nameUpdateDocument);
+      if(type == 'OF'){
+        let supportedFilesOF = ['pdf', 'xlsx', 'pptx', 'doc', 'docx'];
+
+        if(!supportedFilesOF.includes(extStr)){
+          this._resourcesService.openSnackBar(`La extensión ${extStr} no está soportado para oficios.`);
+          return;
+        }
       }
-    }
 
-    if (type == "Office") {
-      this.documents.push({
-        index: 2,
-        file: file,
-        type: "Oficio",
-        update: this.nameUpdateDocument
+      if(type == 'CE'){
+        let supportedFilesCE = ['doc', 'docx', 'pdf', 'jpg', 'png'];
+
+        if(!supportedFilesCE.includes(extStr)){
+          this._resourcesService.openSnackBar(`La extensión ${extStr} no está soportado para certificaciones.`);
+          return;
+        }
+      }
+      
+      let fileTypes = availableFiles;
+      let sizes = Object.keys(fileTypes);
+      let extensions = Object.values(fileTypes);
+      let maxSizeMB = 0;
+
+      let i = 0;
+      extensions.forEach(ext => {
+        let size = parseInt(sizes[i]);
+  
+        if(ext.includes(extStr)) maxSizeMB = size;
+        i++;
       });
-      this.officeDoc.push(file);
-      if (this.nameUpdateDocument != "") {
-        this.officeDoc.push(this.nameUpdateDocument);
+
+      if(maxSizeMB == 0){
+        this._resourcesService.openSnackBar(`Los archivos ${extStr} no están soportados`);
+      }else{
+        let maxFileSize = 1048576 * maxSizeMB;
+        let fileSize = event.target.files[0].size;
+
+        if(fileSize > maxFileSize){
+          this._resourcesService.openSnackBar(`El tamaño del archivo supera el límite de ${maxSizeMB} MB`);
+        }else{
+          let alreadyAttached = this.attached.filter(file => file.name == event.target.files[0].name && file.size == this.formatBytes(event.target.files[0].size));
+
+          if(alreadyAttached.length > 0){
+            this._resourcesService.openSnackBar("El archivo seleccionado ya fue adjuntado.");
+          }else{
+            this.isUploading = true;
+            this.uploadTmp(event.target.files[0], type);
+          }
+        }
       }
     }
+  }
 
-    if (type == "Cert") {
-      this.documents.push({
-        index: 3,
-        file: file,
-        type: "Certificación",
-        update: this.nameUpdateDocument
-      });
-      this.certDoc.push(file);
-      if (this.nameUpdateDocument != "") {
-        this.certDoc.push(this.nameUpdateDocument);
+  async uploadTmp(fileData: any, type: string){
+    try{
+      let idUser = localStorage.getItem('idusuario');
+      let formData = new FormData();
+
+      formData.append('file', fileData);
+      formData.append('id_user', idUser!);
+      formData.append('linea', "1");
+
+      let response = await lastValueFrom(this._gdelService.uploadTempFile(formData));
+
+      if(response.error){
+        this._resourcesService.openSnackBar(response.msg);
+      }else{
+        let uploadedFile: TempFileInfo = {
+          id: response.response.idArchivo,
+          name: fileData.name,
+          size: this.formatBytes(fileData.size),
+          type: type,
+        };
+
+        this.attached.push(uploadedFile);
+        this.dataSource = new MatTableDataSource(this.attached);
       }
-    }
 
-    if (type == "Cert2") {
-      this.documents.push({
-        index: 4,
-        file: file,
-        type: "Certificación",
-        update: this.nameUpdateDocument
-      });
-      this.certDoc2.push(file);
-      if (this.nameUpdateDocument != "") {
-        this.certDoc2.push(this.nameUpdateDocument);
+      this.isUploading = false;
+    }catch(error: any){
+      if(error.error == undefined){
+        this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
+      }else if(error.error.msg == undefined){
+        this._resourcesService.openSnackBar('Ocurrió un error inesperado.');
+      }else{
+        this._resourcesService.openSnackBar(error.error.msg);
       }
+      this.isUploading = false;
     }
-
-    this.dataSource = new MatTableDataSource(this.documents);
-    event.target.value = '';
-    // Limpia el select de los documentos
-    this.removeItem(this.nameUpdateDocument);
-    this.nameUpdateDocument = "";
-    this.enableDocumentButtons()
-    this.updateDocuments.setValue("");
   }
 
   public deleteFiles(element: any) {
@@ -1240,7 +1458,7 @@ export class EmployeeFormComponent implements OnInit {
       }
     }
     this.documents = arrayTemp;
-    this.dataSource = new MatTableDataSource(this.documents);
+    //this.dataSource = new MatTableDataSource(this.documents);
   }
 
   public async openDialog(typeForm: string, element?: any) {
@@ -1280,6 +1498,7 @@ export class EmployeeFormComponent implements OnInit {
   // Método para reajustar los botones según sus dimensiones
   public onResize(): void {
     this.btnSmall = window.innerWidth <= 1405;
+    this.screenSize = window.innerWidth;
   }
 
   public changeNationality() {
@@ -1296,42 +1515,137 @@ export class EmployeeFormComponent implements OnInit {
     }
   }
 
-  get contactName() {
-    return this.formGroup.get('contactName')!;
+  workteamChange(value: string){
+    if(value == '-'){
+      this.formGroup.controls['speciality'].setValue(null);
+      this.formGroup.controls['speciality'].enable();
+    }else{
+      let team = this.workteams.filter(item => item.WORKTEAM_ID == value)[0];
+      
+      this.formGroup.controls['speciality'].setValue(team.SPECIALITY);
+      this.formGroup.controls['speciality'].disable();
+    }
   }
 
-  get contactTelephone() {
-    return this.formGroup.get('contactTelephone')!;
+  checkAttached(type: string){
+    if(type == 'AU' || type == 'OF' || type == 'CE'){
+      let filesByType = this.attached.filter(item => item.type == type);
+      let fileInput = this._document.getElementById('file') as HTMLInputElement;
+      
+      if(type == 'AU' && filesByType.length < 2){
+        fileInput.click();
+      }else if(type == 'AU' && filesByType.length >= 2){
+        this._resourcesService.openSnackBar("El límite de archivos de autorización es de 2 documentos.");
+      }
+      
+      if(type == 'OF' && filesByType.length < 1){
+        fileInput.click();
+      }else if(type == 'OF' && filesByType.length >= 1){
+        this._resourcesService.openSnackBar("El límite de archivos de oficio es de 1 documento.");
+      }
+      
+      if(type == 'CE' && filesByType.length < 2){
+        fileInput.click();
+      }else if(type == 'CE' && filesByType.length >= 2){
+        this._resourcesService.openSnackBar("El límite de archivos de certificación es de 2 documentos.");
+      }
+    }
   }
 
-  get contactLada() {
-    return this.formGroup.get('contactLada')!;
-  }
+  formatBytes(bytes: number, decimals = 2) {
+    if (bytes === 0) return '0 Bytes';
 
-  get contactAddress() {
-    return this.formGroup.get('contactAddress')!;
-  }
+    const k = 1024;
+    const dm = decimals < 0 ? 0 : decimals;
+    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
 
-  get contractType() {
-    return this.formGroup.get('contractType')!;
-  }
+    const i = Math.floor(Math.log(bytes) / Math.log(k));
 
-  get speciality() {
-    return this.formGroup.get('speciality')!;
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
   }
 
-  get subcontratistId() {
-    return this.formGroup.get('subcontratistId')!;
-  }
+  async deleteFile(id: string){
+    try{
+      this.disableDelete = true;
+      this._resourcesService.openSnackBar('Eliminando archivo...');
 
-  get workteamId() {
-    return this.formGroup.get('workteamId')!;
-  }
-  get userId() {
-    return this.formGroup.get('userId')!;
+      let idUser = localStorage.getItem('idusuario')!;
+      let response = await lastValueFrom(this._gdelService.deleteTempFile({
+        'id_user': idUser,
+        'id_file': id,
+        'linea': 1,
+      }));
+
+      if(response.error){
+        this._resourcesService.openSnackBar(response.msg);
+      }else{
+        let attachedAux: TempFileInfo[] = [];
+        this.attached.forEach(file => {
+          if(file.id != id) attachedAux.push(file);
+        });
+
+        this.attached = attachedAux;
+        this.dataSource = new MatTableDataSource(this.attached);
+        this._resourcesService.openSnackBar('El archivo se eliminó correctamente.');
+      }
+
+      this.disableDelete = false;
+    }catch(error: any){
+      this.isUploading = false;
+      let msg = "";
+      if(error.error == undefined){
+        msg = 'Ocurrió un error insperado.';
+      }else if(error.error.msg == undefined){
+        msg = 'Ocurrió un error insperado.';
+      }else{
+        msg = error.error.msg;
+      }
+
+      this._resourcesService.openSnackBar(msg);
+      this.disableDelete = false;
+    }
   }
-  get updateDocuments() {
-    return this.formGroup.get('updateDocuments')!;
+
+  checkNumber(type: string, value: string){
+    let validChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
+    let valueArr = value.split('');
+    let validStr = "";
+
+    valueArr.forEach(char => {
+      if(validChars.includes(char)){
+        validStr += char;
+      }
+    });
+
+    if(validStr.length > 5){
+      validStr = validStr.substring(0, 5);
+    }
+
+    if(type == 'exterior'){
+      this.formGroup.controls['exteriorNumber'].setValue(validStr);
+      this.formGroup.controls['exteriorNumber'].markAsTouched();
+    }else{
+      this.formGroup.controls['interiorNumber'].setValue(validStr);
+      this.formGroup.controls['interiorNumber'].markAsTouched();
+    }
   }
 
+  checkPhoneNumber(value: string){
+    let validChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
+    let valueArr = value.split('');
+    let validStr = "";
+
+    valueArr.forEach(char => {
+      if(validChars.includes(char)){
+        validStr += char;
+      }
+    });
+
+    if(validStr.length > 11){
+      validStr = validStr.substring(0, 11);
+    }
+
+    this.formGroup.controls['contactTelephone'].setValue(validStr);
+    this.formGroup.controls['contactTelephone'].markAsTouched();
+  }
 }

+ 0 - 1
sistema-mantenimiento-front/src/app/components/personal-management/subcontratist/subcontratist-form/subcontratist-form.component.html

@@ -199,7 +199,6 @@
               <mat-form-field appearance="outline" class="w-100">
                 <mat-label>Lada 1</mat-label>
                 <input matInput formControlName="lada1" [matAutocomplete]="autocompleteLada1">
-                <mat-error>Este campo es obligatorio.</mat-error>
                 <mat-autocomplete autoActiveFirstOption #autocompleteLada1>
                   <mat-option *ngFor="let lada1 of filteredLadas1 | async" [value]="lada1">
                     {{ lada1 }}