3.4.11. Asociar Alumnos y Empresas - diezMalena/api_FCTFiller GitHub Wiki

Objetivo

  • El objetivo de esto es generar una relaccion en la tabla FCT a través del alumno que asiste a las FCT y la empresa en la que va a realizar las practicas, la union la realizará un tutor a través de un drag and drop, guardará los cambios y finalmente generara los Anexos1 correspondientes.

Drag and Drop

  • El drag and drop nos va a permitir arrastrar los alumnos a la empresa correspondiente y asociarlos a esta.
    Screenshot_11

Pantalla Inicial

Codigo

  • Nada mas entrar en esta pantalla, lo primero que pasa es que se llama a estas funciones
  ngOnInit(): void {
    this.getNombreCiclo();
    this.getAlumnos();
    this.getEmpresas();
    this.getAnexos();
    this.getArrayAnexosDesdeModal();
  }
  • Voy a explicar una por una que hacen:
    • getNombreCiclo():
      • Llama a la funcion del servidor solicitarNombreCiclo() enviandole como parametro el dni del tutor, esta función se encarga de sacar el nombre del ciclo al que el tutor tutoriza
            public function solicitarNombreCiclo(string $dni){
                                                    
               $nombre = Tutoria::where('dni_profesor', '=', $dni)->get()[0]->cod_grupo;
        
               return response()->json($nombre, 200);
           }
    • getAlumnos():
      • Llama a la funcion del servidor solicitarAlumnosSinEmpresa() enviandole como parametro el dni del tutor, esta función se encarga de devolver los alumnos que no estan asociados aún a ninguna empresa, haciendo un whereNotIn, primero saca los que si tienen, entonces hace la select whereNotIn para sacar los que no tienen.
             public function solicitarAlumnosSinEmpresa(string $dni){
        
                $cursoAcademico = Auxiliar::obtenerCursoAcademico();
                $alumnosEnEmpresa = Alumno::join('matricula', 'matricula.dni_alumno', '=', 'alumno.dni')
                ->join('fct', 'fct.dni_alumno', '=', 'matricula.dni_alumno')
                ->join('grupo', 'grupo.cod', '=', 'matricula.cod_grupo')
                ->join('tutoria', 'tutoria.cod_grupo', '=', 'matricula.cod_grupo')
                ->where([['tutoria.dni_profesor', '=', $dni], ['tutoria.curso_academico', '=', $cursoAcademico]])
                ->pluck('alumno.dni')
                ->toArray();
        
                $alumnosSinEmpresa = Alumno::join('matricula', 'matricula.dni_alumno', '=', 'alumno.dni')
                ->join('grupo', 'grupo.cod', '=', 'matricula.cod_grupo')
                ->join('tutoria', 'tutoria.cod_grupo', '=', 'matricula.cod_grupo')
                ->where([['tutoria.dni_profesor', '=', $dni], ['tutoria.curso_academico', '=', $cursoAcademico]])
                ->whereNotIn('alumno.dni', $alumnosEnEmpresa)
                ->select(['alumno.dni', 'alumno.nombre', 'alumno.va_a_fct'])
                ->get();
                 return response()->json($alumnosSinEmpresa, 200);
                  }
    • getEmpresas():
      • Llama a la funcion del servidor solicitarEmpresasConAlumnos() enviandole como parametro el dni del tutor, al principio saca las empresas que estan interesadas por un grupo, junto a su convenio con el centro de estudios y el grupo en el que la empresa esta interesada.

          $empresas = Grupo::join('empresa_grupo', 'empresa_grupo.cod_grupo', '=', 'grupo.cod')
          ->join('empresa', 'empresa.id', '=', 'empresa_grupo.id_empresa')
          ->join('convenio', 'convenio.id_empresa', '=', 'empresa.id')
          ->join('tutoria', 'tutoria.cod_grupo', '=', 'grupo.cod')
          ->where('tutoria.dni_profesor', $dni)
          ->get();
      • Al recoger las empresas se hace un foreach para recorrerlas, se intercepta al responsable del centro, si este no tiene, se intercepta al representante legal. Después se asigna este responsabe/representante su nombre y dni a la empresa que le corresponde y finalmente se interceptan a los alumnos asignados a la empresa y se asigna a esta, finalmente se devuelve un array de empresas con arrays dentro de alumnos asignados a ellas.

        foreach ($empresas as  $empresa) {
        //Aquí rocojo el responsable de esa empresa. Si no hay, se saca al representante legal, que va a estar sí o sí
        $responsable = RolTrabajadorAsignado::join('trabajador', 'trabajador.dni', '=', 'rol_trabajador_asignado.dni')
            ->join('empresa', 'empresa.id', '=', 'trabajador.id_empresa')
            ->where([['rol_trabajador_asignado.id_rol', Parametros::RESPONSABLE_CENTRO], ['empresa.id', $empresa->id]])
            ->select(['trabajador.nombre', 'trabajador.dni'])
            ->first();
        //Por si acaso la empresa no tiene un responsable asignado, ponemos al representante legal
        if (!$responsable) {
            $responsable = RolTrabajadorAsignado::join('trabajador', 'trabajador.dni', '=', 'rol_trabajador_asignado.dni')
                ->join('empresa', 'empresa.id', '=', 'trabajador.id_empresa')
                ->where([['rol_trabajador_asignado.id_rol', Parametros::REPRESENTANTE_LEGAL], ['empresa.id', $empresa->id]])
                ->select(['trabajador.nombre', 'trabajador.dni'])
                ->first();
        }
        $empresa->nombre_responsable = $responsable->nombre;
        $empresa->dni_responsable = $responsable->dni;
        //Aquí rocojo los alumnos asociados a esa empresa
        $alumnos = Grupo::join('matricula', 'matricula.cod_grupo', '=', 'grupo.cod')
            ->join('alumno', 'alumno.dni', '=', 'matricula.dni_alumno')
            ->join('fct', 'fct.dni_alumno', '=', 'alumno.dni')
            ->join('tutoria', 'tutoria.cod_grupo', '=', 'matricula.cod_grupo')
            ->where([['tutoria.dni_profesor', $dni], ['fct.id_empresa', $empresa->id]])
            ->select(['alumno.nombre', 'alumno.dni', 'alumno.va_a_fct', 'fct.horario', 'fct.fecha_ini', 'fct.fecha_fin'])
            ->get();
        $empresa->alumnos = $alumnos;
         }
      • Ejemplo del array asociativo que puede devolver:

      {
      "cod": "2DAW",
      "nombre_largo": "2º de CFGS (LOE) - Desarrollo de Aplicaciones Web",
      "nombre_ciclo": "Desarrollo de Aplicaciones Web",
      "cod_nivel": "CFGS",
      "id_empresa": 69,
      "cod_grupo": "2DAW",
      "id": 69,
      "cif": "12345678U",
      "nombre": "Obiwan",
      "telefono": "458963658",
      "email": "[email protected]",
      "localidad": "A Laracha",
      "provincia": "A Coruña",
      "direccion": "Av/Avenida 42",
      "cp": "15988",
      "es_privada": 1,
      "cod_convenio": "VdG/C55/22",
      "cod_centro": "1111VDG",
      "fecha_ini": "2022-06-13",
      "fecha_fin": "2026-06-13",
      "ruta_anexo": "1A/Anexo0/Anexo0_VdG-C55-22.docx",
      "dni_profesor": "20a",
      "curso_academico": "21/22",
      "nombre_responsable": "Paco",
      "dni_responsable": "12365896L",
      "alumnos": [
         {
             "nombre": "Daniel",
             "dni": "11a",
             "va_a_fct": 0,
             "horario": "dgvx",
             "fecha_ini": "2022-06-17",
             "fecha_fin": "2022-06-18"
         }
      ]
      },   
    • getAnexos():
      • Llama a la funcion del servidor listarAnexos1() enviandole como parametro el dni del tutor. Esta funcion saca los Anexos1 de la tabla FCT asociados al tutor que hace la petición y siempre comprobando que los anexos existan tanto en base de datos como en las carpetas del servidor. Devuelve un array asociativo por cada Anexo1 que se encuentre que contiene el nombre del anexo y la id de la empresa asociada a este.
      public function listarAnexos1($dni_tutor){
      
       $datos = array();
       $Anexos1 = FCT::select('ruta_anexo', 'id_empresa')->where('ruta_anexo', 'like', "$dni_tutor%")->distinct()->get();
      
        if (count($Anexos1) > 0) {
            foreach ($Anexos1 as $a) {
               if (file_exists(public_path($a->ruta_anexo))) {
                 $empresa = Empresa::select('nombre')->where('id', '=', $a->id_empresa)->get();
                 $nombreArchivo = explode(DIRECTORY_SEPARATOR, $a->ruta_anexo);
      
                 $datos[] = [
                     'id_empresa' => $empresa[0]->nombre,
                     'codigo' => $nombreArchivo[2]
                 ];
             }
         }
      
          return response()->json($datos, 200);
        } else {
          return response()->json($datos, 204);
       }
      }
    • getArrayAnexosDesdeModal()
      • Cuando subimos un anexo, se abre un modal, gracias a esta función, podemos refrescar la ventana padre cuando hacemos cambios en la hija, como el modal en cuestion gestiona anexos, lo que refrescamos son los Anexos1 que llegan a la ventana padre.
         public getArrayAnexosDesdeModal() {
           this.alumnosEmpresas.anexosArray.subscribe((array) => {
           this.anexos = array;
           });
          }

Diseño

  • Al principio vamos a hacer un *ngFor para mostrar los alumnos que no tienen empresas, sacados de la funcion anterior this.getAlumnos()
    Screenshot_13
<div class="card-body ps-3" cdkDropList [cdkDropListData]="alumnos" (cdkDropListDropped)="drop($event)">
    <div *ngFor="let alumno of alumnos" ngClass="alumnos h6 text-black bg-secondary rounded border-secondary row p-1 mb-2" attr.id="{{alumno.dni}}" cdkDrag>
       {{alumno.nombre}}
    </div>
</div>
  • Luego, mostraremos el grueso de la cuestion, se hace un *ngFor de las empresas con alumnos de la anterior función getEmpresas(), primero se muestra el nombre de la empresa y el responsable y debajo los alumnos(si es que hay). Tambien, gracias a la función getAnexos()decidimos si mostrar los botones descargar y subir. Solo se muestran si existe ya Anexos1 generados, Si no hay, no hay nada que descargar, ni nada que subir firmado como .pdf.
<div *ngFor="let empresa of empresas" ngClass="row mb-2 bg-primary p-2 border border-primary rounded text-white" attr.id="{{empresa.id}}">
    <div class="col">
        <label class="row" for="responsable{{empresa.dni_responsable}}">
            <h3 class="col-4 h5">{{empresa.nombre}}</h3>
             <span class="col-4 text-end" for="responsableEmpresa">Responsable de la empresa:</span>
             <input aria-label="responsable de la empresa" class="col-4" type="text" 
               id="responsable{{empresa.dni_responsable}}" [(ngModel)]="empresa.nombre_responsable">
        </label>
            <div class="row">
                <div class="col-12">
                   <div cdkDropList [cdkDropListData]="empresa.alumnos" class="py-3" (cdkDropListDropped)="drop($event)">
                       <div *ngFor="let alumno2 of empresa.alumnos" ngClass="alumnos text-white bg-secondary rounded border-secondary row p-1  mb-2" attr.id="{{alumno2.dni}}" cdkDrag>
                         <span class="h6 text-black col-12 col-md-3">{{alumno2.nombre}}</span>
                         <input aria-label="horario del alumno" class="col-12 col-md-3" placeholder="Horario" type="text" [(ngModel)]="alumno2.horario">
                         <input aria-label="fecha de inicio de prácticas" class="col-12 col-md-3" type="date" [(ngModel)]="alumno2.fecha_ini">
                         <input aria-label="fecha de fin de prácticas" class="col-12 col-md-3" type="date" [(ngModel)]="alumno2.fecha_fin">
                       </div>
                   </div>
                </div>
            </div>
            <div *ngFor="let anexo of anexos; index as i" class="row d-flex justify-content-center">
               <input *ngIf="empresa.nombre==anexo.id_empresa  && this.hayAlumnosEnEmpresas==true" type="button" class="col-12 col-lg-5 btn btn-outline-light m-1" value="Subir Anexo" (click)="abrirModalUpload(anexo.codigo) ">
               <input *ngIf="empresa.nombre==anexo.id_empresa  && this.hayAlumnosEnEmpresas==true" type="button" class="col-12 col-lg-5 btn btn-outline-light m-1" (click)="descargarAnexo(anexo.codigo)" value="Descargar Anexo">
            </div>
            <div *ngIf="this.hayAlumnosEnEmpresas==false" class="row d-flex justify-content-center">
              <input type=" button" class="col-12 col-lg-5 btn btn-outline-light m-1" value="Subir Anexo" disabled>
              <input type=" button" class="col-12 col-lg-5 btn btn-outline-light m-1" value="Descargar Anexo" disabled>
            </div>
     </div>
</div>

Posicionar Alumnos

  • Gracias a la linea que voy a mostrar a continuación podemos detectar cuando se arrastra un alumno de un sitio a otro
<div class="card-body ps-3" cdkDropList [cdkDropListData]="alumnos" (cdkDropListDropped)="drop($event)">
  • Cuando este se mueve, se llama a la funcion drop() y se le envia el $event, después se mueven los alumnos de un array a otro`gracias a esto:
  drop(event: CdkDragDrop<any>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    }
  }

Guardar

  • Este boton nos va a permitir guardar los cambios de la asignacion alumnos/empresas que haya realizado el tutor
    Screenshot_12

Cliente

  • Se va a llamar a la función setCambiosEmpresas(). Si decidimos hacer estas modificaciones, esta función primero va a comprobar que no te hayas dejado ningun campo en blanco, solo si no te has dejado ningun campo vacio te permitira continuar con la asignacion,de lo contrario, la variable $bandera se pondra a false y saltarà un error con toastar, esto también fallara si has puesto la fecha de inicio despues que la de fin.

Servidor

  • Si todo esta bien, se llamará a la función del servidor actualizarEmpresaAsignadaAlumno() que recibirá una $Request que contendrá el dni del tutor, los alumnos solos y las empresas con alumnos asignados. También hará limpieza de archivos, eliminando los archivos actuales y generando los nuevos.
            $cursoAcademico = Auxiliar::obtenerCursoAcademico();
            $alumnos_solos = $request->get('alumnos_solos');
            $empresas = $request->get('empresas');
            $dni_tutor = $request->get('dni_tutor');
            $this->borrarAnexosTablaFCT($dni_tutor);
  • Después se hace limpieza de la tabla fct de los alumnos que ya no pertenezcan a una empresa, eliminando los registros actuales correspondientes a los anexos que hubiera o no antes y generando los nuevos. No se puede borrar a un alumno que haya comenzado las Fct, o sea, que ya tengan seguimiento, si esto ocurre, retornara un error de codigo 406.
            foreach ($alumnos_solos as $alumno) {
                $AlumnoTieneSeguimiento =  FCT::join('seguimiento', 'seguimiento.id_fct', '=', 'fct.id')
                    ->where([['fct.dni_alumno', $alumno['dni']]])
                    ->select(['seguimiento.id'])
                    ->first();

                if (!$AlumnoTieneSeguimiento) {
                    Fct::where([['dni_alumno', $alumno['dni']], ['curso_academico', $cursoAcademico]])->delete();
                } else {
                    return response()->json(['message' => 'No puedes mover un alumno que ya está en prácticas: ' . $alumno['nombre']], 406);
                }
            }
  • Comienza un foreach de las empresas, actualizando el responsable si es que hay uno nuevo, se asigna a una variable $alumnos los alumnos nuevos que se hayan podido asignar a cada empresa y se comienza un foreach de los alumnos.
  • Se hace una comprobación para ver si el alumno ya tiene un seguimiento e ha inicializado sus FCT, en tal caso este no podra eliminarse de la tabla fct para asignarlo a otra parte ya que se comprueba también que el id de la empresa actual coincide con el que se esta intentando enviar y se hara una response con un codigo 406. En caso de que el alumno no haya comenzado sus practicas se borrara de la tabla fct y volverá a generarse su registro de manera correcta.
            //este for mete el nuevo nombre del responsable, se haya cambiado o no.
            //elimina el registro de la tabla fct de los alumnos que están en una empresa y
            //los inserta de nuevo con los cambios que se han hecho.
            foreach ($empresas as $empresa) {
                Trabajador::find($empresa['dni_responsable'])->update(['nombre' => $empresa['nombre_responsable']]);
                
                $alumnos = $empresa['alumnos'];

                foreach ($alumnos as $alumno) {

                    $AlumnoTieneSeguimiento =  FCT::join('seguimiento', 'seguimiento.id_fct', '=', 'fct.id')
                        ->where([['fct.dni_alumno', $alumno['dni']]])
                        ->select(['seguimiento.id'])
                        ->first();

                    if (!$AlumnoTieneSeguimiento) {
                        Fct::where([['dni_alumno', $alumno['dni']], ['curso_academico', $cursoAcademico]])->delete();

                        Fct::create([
                            'id_empresa' => $empresa['id'],
                            'dni_alumno' => $alumno['dni'],
                            'dni_tutor_empresa' => $empresa['dni_responsable'],
                            'curso_academico' => $cursoAcademico,
                            'horario' => $alumno['horario'],
                            'num_horas' => '400',
                            'fecha_ini' => $alumno['fecha_ini'],
                            'fecha_fin' => $alumno['fecha_fin'],
                            'firmado_director' => '0',
                            'firmado_empresa' => '0',
                            'ruta_anexo' => '',
                            'departamento' => ''
                        ]);
                    } else {
                        $empresaAux = Fct::select('id_empresa')->where('dni_alumno', '=', $alumno['dni'])->first();
                        if ($empresaAux->id_empresa != $empresa['id']) {
                            return response()->json(['message' => 'No puedes mover un alumno que ya está en prácticas: ' . $alumno['nombre']], 406);
                        }
                    }
                }
            }
  • Finalmente y si todo sale bien, se enviara una response que indicara que todo es correcto
return response()->json(['message' => 'Actualizacion completada'], 200);

Anexo1

  • Una vez se han generado las relacciones procedemos a generar el Anexo1 , lo primero que hay que hacer es pulsar este boton
    Screenshot_9

  • Una vez pulsado se va a llamar a la funcion GenerarAnexos() y después se llama a la funcion del servidor rellenarAnexo1() que recibe como parametro el dni del tutor que esta generando estos anexos.

  • Primero obtenemos el dni del tutor en una variable y la informacion del centro a través de peticiones a la bbdd, que estan rescatadas de funciones Auxiliares o no.

        $dni_tutor = $val->get('dni_tutor');
        $cod_centro = Profesor::select('cod_centro_estudios')->where('dni', $dni_tutor)->first();
        $nombre_tutor = Profesor::select('nombre', 'apellidos')->where('dni', $dni_tutor)->first();
        $directora = Profesor::join('rol_profesor_asignado', 'rol_profesor_asignado.dni', '=', 'profesor.dni')
            ->select('profesor.nombre', 'profesor.apellidos')
            ->where('profesor.cod_centro_estudios', '=', $cod_centro->cod_centro_estudios)
            ->where('rol_profesor_asignado.id_rol', '=', Parametros::DIRECTOR)
            ->first();
        $ciudad_centro_estudios = CentroEstudios::select('localidad')->where('cod', $cod_centro->cod_centro_estudios)->first();
  • Borro los Anexos1 de este tutor que estén habilitados en la tabla Anexo, lo cual quiere decir, que son los activos y actuales.También obtengo las empresas interesadas en el grupo que tutoriza el profesor, la fecha y un nombre auxiliar para el zip
$this->borrarAnexosTablaAnexos('Anexo1', $dni_tutor);
$grupo = Tutoria::select('cod_grupo')->where('dni_profesor', $dni_tutor)->first();
$empresas_id = EmpresaGrupo::select('id_empresa')->where('cod_grupo', $grupo->cod_grupo)->get();
$fecha = Carbon::now();
 $AuxNombre = $dni_tutor . '_' . $fecha->day . '_' . Parametros::MESES[$fecha->month] . '_' . $fecha->year . $fecha->format('_h_i_s_A');
  • Se genera el objeto zip
$zip = new ZipArchive;
$nombreZip = 'tmp' . DIRECTORY_SEPARATOR . 'anexos' . DIRECTORY_SEPARATOR . 'myzip_' . $AuxNombre . '.zip';
  • Se realiza un foreach de las empresas, para ir generando un anexo por cada empresa interesada, en este foreach se recuperan datos necesarios en variables para rellenar los Anexos, se establecen las rutas y nombres de archivos , se añaden los anexos a la base de datos, tanto a la tabla Fct como Anexo.
  • Se genera una tabla interactiva con formato en el .docx y se plasman los datos en el. Finalmente, devuelve un zip al cliente para que el usuario disfrute de todos sus Anexos1 a su alcance.
  foreach ($empresas_id as $id) {

                //Alumnos
                $alumnos = Fct::join('alumno', 'alumno.dni', '=', 'fct.dni_alumno')
                    ->join('matricula', 'matricula.dni_alumno', '=', 'fct.dni_alumno')
                    ->select('alumno.nombre', 'alumno.apellidos', 'alumno.dni', 'alumno.localidad', 'fct.horario', 'fct.num_horas', 'fct.fecha_ini', 'fct.fecha_fin')
                    ->where('fct.id_empresa', '=', $id->id_empresa)
                    ->where('matricula.cod_grupo', '=', $grupo->cod_grupo)
                    ->get();

                if (count($alumnos) > 0) {
                    #region Recogida de datos

                    // Numero de Convenio
                    $convenio = Convenio::select('cod_convenio')->where('id_empresa', '=', $id->id_empresa)->where('cod_centro', '=', $cod_centro->cod_centro_estudios)->first();

                    //Nombre del ciclo
                    $nombre_ciclo = Grupo::select('nombre_ciclo')->where('cod', $grupo->cod_grupo)->first();

                    //Codigo Ciclo
                    $cod_ciclo = Grupo::select('cod')->where('nombre_ciclo',  $nombre_ciclo->nombre_ciclo)->get();

                    //ARCHIVO
                    $rutaOriginal = 'anexos' . DIRECTORY_SEPARATOR . 'plantillas' . DIRECTORY_SEPARATOR . 'Anexo1';
                    $convenioAux = str_replace('/', '-', $convenio->cod_convenio);
                    $AuxNombre = '_' . $id->id_empresa . '_' . $convenioAux . '_' . $cod_ciclo[0]->cod . '_' . $fecha->year . '_';
                    $rutaDestino = $dni_tutor  . DIRECTORY_SEPARATOR . 'Anexo1' . DIRECTORY_SEPARATOR . 'Anexo1' . $AuxNombre;
                    $template = new TemplateProcessor($rutaOriginal . '.docx');

                    //Almacenamos las rutas de los anexos en la bbdd

                    foreach ($alumnos as $a) {
                        Fct::where('id_empresa', '=', $id->id_empresa)->where('dni_alumno', '=', $a->dni)->update(['ruta_anexo' => $rutaDestino . '.docx']);
                        Anexo::create(['tipo_anexo' => 'Anexo1', 'ruta_anexo' => $rutaDestino . '.docx']);
                    }

                    //Nombre de la empresa y Direccion
                    $nombre_empresa = Empresa::select('nombre', 'direccion')->where('id', $id->id_empresa)->first();
                    //Nombre del centro
                    $nombre_centro = CentroEstudios::select('nombre')->where('cod', $cod_centro->cod_centro_estudios)->first();
                    //Año del curso
                    $curso_anio = Carbon::createFromFormat('Y-m-d', Convenio::where('cod_convenio', $convenio->cod_convenio)->select('fecha_ini')->get()->first()->fecha_ini)->year;

                    //Responsable de la empresa
                    $representante_legal = Empresa::join('trabajador', 'trabajador.id_empresa', '=', 'empresa.id')
                        ->join('rol_trabajador_asignado', 'rol_trabajador_asignado.dni', '=', 'trabajador.dni')
                        ->select('trabajador.nombre', 'trabajador.apellidos')
                        ->where('trabajador.id_empresa', '=', $id->id_empresa)
                        ->where('rol_trabajador_asignado.id_rol', '=', Parametros::REPRESENTANTE_LEGAL)
                        ->first();

                    //representante del centro de trabajo
                    $responsable_centro = Empresa::join('trabajador', 'trabajador.id_empresa', '=', 'empresa.id')
                        ->join('rol_trabajador_asignado', 'rol_trabajador_asignado.dni', '=', 'trabajador.dni')
                        ->select('trabajador.nombre', 'trabajador.apellidos')
                        ->where('trabajador.id_empresa', '=', $id->id_empresa)
                        ->where('rol_trabajador_asignado.id_rol', '=', Parametros::RESPONSABLE_CENTRO)
                        ->first();


                    if (!$responsable_centro) {
                        $responsable_centro = $representante_legal;
                    }

                    #endregion

                    #Estilo tabla
                    $styleTable = array('borderSize' => 6, 'borderColor' => '888888', 'cellMargin' => 40);

                    #region Construcción de la tabla
                    $table = new Table(array('unit' => TblWidth::TWIP));
                    $table->addRow();
                    $table->addCell(1500, $styleTable)->addText('APELLIDOS Y NOMBRE');
                    $table->addCell(1500, $styleTable)->addText('D.N.I');
                    $table->addCell(1500, $styleTable)->addText('LOCALIDAD DE RESIDENCIA DEL ALUMNO/A (**)');
                    $table->addCell(1500, $styleTable)->addText('HORARIO DIARIO');
                    $table->addCell(1500, $styleTable)->addText('NUMERO HORAS');
                    $table->addCell(1500, $styleTable)->addText('FECHA DE COMIENZO');
                    $table->addCell(1500, $styleTable)->addText('FECHA DE FINALIZACION');
                    foreach ($alumnos as $a) {
                        $table->addRow();
                        $table->addCell(1500, $styleTable)->addText($a->apellidos . ' ' . $a->nombre);
                        $table->addCell(1500, $styleTable)->addText($a->dni);
                        $table->addCell(1500, $styleTable)->addText($a->localidad);
                        $table->addCell(1500, $styleTable)->addText($a->horario);
                        $table->addCell(1500, $styleTable)->addText($a->num_horas);
                        $table->addCell(1500, $styleTable)->addText($a->fecha_ini);
                        $table->addCell(1500, $styleTable)->addText($a->fecha_fin);
                    }
                    #endregion

                    #region Relleno de datos en Word
                    $auxPrefijos = ['convenio', 'centro', 'empresa', 'ciclo', 'responsable', 'centro', 'directora', 'representante', 'tutor'];
                    $auxDatos = [$convenio, $nombre_centro, $nombre_empresa, $nombre_ciclo, $representante_legal, $ciudad_centro_estudios, $directora, $responsable_centro, $nombre_tutor];

                    $datos = Auxiliar::modelsToArray($auxDatos, $auxPrefijos);
                    $datos = $datos +  [
                        'anio.curso' => $curso_anio,
                        'dia' => $fecha->day,
                        'mes' => Parametros::MESES[$fecha->month],
                        'year' => $fecha->year,
                    ];


                    $rutaCarpeta = public_path($dni_tutor . DIRECTORY_SEPARATOR . 'Anexo1');
                    Auxiliar::existeCarpeta($rutaCarpeta);
                    $rutaCarpeta = public_path('tmp' . DIRECTORY_SEPARATOR . 'anexos');
                    Auxiliar::existeCarpeta($rutaCarpeta);

                    $template->setValues($datos);
                    $template->setComplexBlock('{table}', $table);
                    $template->saveAs($rutaDestino . '.docx');
                    #endregion

                    //Convertir en Zip
                    $nombreZip = $this->montarZipConCondicion($dni_tutor . DIRECTORY_SEPARATOR . 'Anexo1', $zip, $nombreZip);
⚠️ **GitHub.com Fallback** ⚠️