public void enviarFicheroGeneralEnPorciones(EnvioErroneo envio) {
    try {

      // Tenemos que dividir el archivo por cursos y unidades
      DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      documentBuilderFactory.setNamespaceAware(false);
      documentBuilderFactory.setValidating(false);
      DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
      File tmpX = new File(getCarpetaFallidos(), envio.getFicheroEnvio().getName() + ".tmp");
      Archivo.setContenido(
          Archivo.getContenido(envio.getFicheroEnvio(), "latin1"), "UTF-8", tmpX, false);
      Document doc = documentBuilder.parse(tmpX);
      tmpX.delete();
      // Ahore recorremos el documento generando un archivo nuevo por cada version
      Node nCursos = doc.getElementsByTagName("CURSOS").item(0);
      Node datosGenerales = doc.getElementsByTagName("DATOS_GENERALES").item(0);
      NodeList cursos = nCursos.getChildNodes();
      for (int i = 0; i < cursos.getLength(); i++) {
        Object obj = cursos.item(i);
        if (!(obj instanceof Element)) {
          continue;
        }
        Element cursoActual = (Element) obj;
        // Creamos un nuevo documento
        Document nuevoDoc = documentBuilder.newDocument();
        // Le añadimos un nodo de servicio
        Node servicio = nuevoDoc.createElement("SERVICIO");
        nuevoDoc.appendChild(servicio);
        // A este le añadimos los datos generales (clonados)
        Node nuevoDatosGenerales = datosGenerales.cloneNode(true);
        nuevoDoc.adoptNode(nuevoDatosGenerales);
        servicio.appendChild(nuevoDatosGenerales);
        // Creamos un nodo de cursos y se lo añadimos
        Node nuevoCursos = nuevoDoc.createElement("CURSOS");
        servicio.appendChild(nuevoCursos);
        // Creamos un nodo de curso y se lo añadimos
        Node nuevoCurso = nuevoDoc.createElement("CURSO");
        nuevoCursos.appendChild(nuevoCurso);
        // Le añadimos los datos al curso
        Node el1 = cursoActual.getElementsByTagName("X_OFERTAMATRIG").item(0).cloneNode(true);
        nuevoDoc.adoptNode(el1);
        String nombreCurso = el1.getTextContent();
        Node el2 = cursoActual.getElementsByTagName("D_OFERTAMATRIG").item(0).cloneNode(true);
        nuevoDoc.adoptNode(el2);
        nuevoCurso.appendChild(el1);
        nuevoCurso.appendChild(el2);
        // Creamos un nodo de unidades y se lo añadimos
        Node nuevoUnidades = nuevoDoc.createElement("UNIDADES");
        nuevoCurso.appendChild(nuevoUnidades);
        // Ahora recorremos las unidades creando un documento nuevo por cada una
        Element nodoUnidadesActual = (Element) cursoActual.getElementsByTagName("UNIDADES").item(0);
        NodeList unidades = nodoUnidadesActual.getChildNodes();
        for (int x = 0; x < unidades.getLength(); x++) {
          Node nObj = unidades.item(x);
          if (!(nObj instanceof Element)) {
            continue;
          }
          // Clonamos la unidad
          Node unidad = nObj.cloneNode(true);
          // Clonamos el documento completo
          Document docActual = (Document) nuevoDoc.cloneNode(true);
          // Adoptamos el nodo creado
          docActual.adoptNode(unidad);
          // Y se lo añadimos a las unidades
          docActual.getElementsByTagName("UNIDADES").item(0).appendChild(unidad);
          // Guardamos el documento y lo enviamos
          String contenido = docToString(docActual);
          File tmp = new File(getCarpetaFallidos(), "tmp_curso_" + nombreCurso + "_" + x + ".xml");
          Archivo.setContenido(contenido, "latin1", tmp, false);
          String codigoOperacion = Cripto.md5(tmp.getName() + "" + Math.random());
          int ret = enviarFichero(tmp, codigoOperacion);
          if (ret == GestorEnvioFaltas.RET_ERROR_PROCESANDO) {
            tmp.delete();
            // Sobre ese archivo tenemos que hacer envíos alumno por alumno
            // Clonamos el documento
            docActual = (Document) nuevoDoc.cloneNode(true);
            // Y procesamos el envío
            enviarFicheroUnidadEnPorciones(envio, docActual, (Element) unidad);
          } else if (ret == GestorEnvioFaltas.RET_ERROR_ENVIANDO) {
            envio.getFallidos().add(tmp);
          } else {
            tmp.delete();
          }
        }
      }
    } catch (Exception ex) {
      Logger.getLogger(GeneradorFicherosSeneca.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
 private void enviarFicheroUnidadEnPorciones(EnvioErroneo envio, Document doc, Element unidad)
     throws TransformerException {
   // Creamos un nuevo nodo de unidad
   Element nuevaUnidad = doc.createElement("UNIDAD");
   doc.getElementsByTagName("UNIDADES").item(0).appendChild(nuevaUnidad);
   // Le añadimos los parámetros
   Node xUnidad = unidad.getElementsByTagName("X_UNIDAD").item(0).cloneNode(true);
   doc.adoptNode(xUnidad);
   String codUnidad = xUnidad.getTextContent();
   Node xNombre = unidad.getElementsByTagName("T_NOMBRE").item(0).cloneNode(true);
   doc.adoptNode(xNombre);
   nuevaUnidad.appendChild(xUnidad);
   nuevaUnidad.appendChild(xNombre);
   // Ahora añadimos los alumnos
   Element alumnos = doc.createElement("ALUMNOS");
   nuevaUnidad.appendChild(alumnos);
   // Y vamos creando un fichero por cada alumno
   NodeList nl = unidad.getElementsByTagName("ALUMNOS").item(0).getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
     Object obj = nl.item(i);
     if (!(obj instanceof Element)) {
       continue;
     }
     Element al = (Element) obj;
     al = (Element) al.cloneNode(true);
     // Clonamos el documento
     Document docAlumno = (Document) doc.cloneNode(true);
     docAlumno.adoptNode(al);
     // Y le añadimos el
     docAlumno.getElementsByTagName("ALUMNOS").item(0).appendChild(al);
     // Guardamos el archivo y lo enviamos
     String contenido = docToString(docAlumno);
     File tmp = new File(getCarpetaFallidos(), "tmp_unidad_" + codUnidad + "_" + i + ".xml");
     Archivo.setContenido(contenido, "latin1", tmp, false);
     String codigoOperacion = Cripto.md5(tmp.getName() + "" + Math.random());
     int ret = enviarFichero(tmp, codigoOperacion);
     if (ret == GestorEnvioFaltas.RET_ERROR_PROCESANDO) {
       envio.getErroneos().add(tmp);
       // Cogemos el dato X_MATRICULA
       String datoMatricula = al.getElementsByTagName("X_MATRICULA").item(0).getTextContent();
       Alumno a = Alumno.getAlumnoDesdeCodFaltas(datoMatricula);
       if (a != null) {
         AlumnoEnvioErroneo aee = new AlumnoEnvioErroneo(a);
         envio.getAlumnosFallidos().add(aee);
         // Ahora tenemos que localizar las fechas distintas donde falla
         NodeList nlf = al.getElementsByTagName("F_FALASI");
         ArrayList<String> fechas = new ArrayList<String>();
         for (int x = 0; x < nlf.getLength(); x++) {
           String sFecha = nlf.item(x).getTextContent();
           if (!fechas.contains(sFecha)) {
             fechas.add(sFecha);
           }
         }
         aee.setInfo(Str.implode(fechas, "; "));
       }
     } else if (ret == GestorEnvioFaltas.RET_ERROR_ENVIANDO) {
       envio.getFallidos().add(tmp);
     } else {
       tmp.delete();
     }
   }
 }
 public ArrayList<String> exportarFaltas(
     GregorianCalendar fechaDesde, GregorianCalendar fechaHasta, ArrayList<String> cursos) {
   ArrayList<String> err = new ArrayList<String>();
   setFaltasExportadas(false);
   String texto = "Procesando... ";
   if (Fechas.getDiferenciaTiempoEn(fechaDesde, fechaHasta, GregorianCalendar.DATE) == 0) {
     texto += Fechas.format(fechaHasta, "dd/MM");
   } else {
     texto += Fechas.format(fechaDesde, "dd/MM") + " a " + Fechas.format(fechaHasta, "dd/MM");
   }
   PreparedStatement st = null;
   try {
     firePropertyChange("message", null, "Recuperando datos...");
     StringBuilder sbCursos = new StringBuilder();
     boolean primero = true;
     for (String c : cursos) {
       if (primero) {
         primero = false;
       } else {
         sbCursos.append(",");
       }
       sbCursos.append("\"").append(c).append("\"");
     }
     String sql =
         "SELECT p.id as idParte,p.fecha,t.cod AS codTramo,pa.asistencia,a.id AS idAlumno FROM partes_alumnos AS pa "
             + " JOIN partes AS p ON p.id=pa.parte_id "
             + " JOIN alumnos AS a ON a.id=pa.alumno_id "
             + " LEFT JOIN alumnos_problemas_envio AS ap ON a.id=ap.alumno_id "
             + " JOIN horarios AS h ON h.id=pa.horario_id "
             + " LEFT JOIN calendario_escolar AS ce ON ce.ano=p.ano AND p.fecha=ce.dia AND ce.docentes "
             + // TODO Esta linea elimina a caso hecho a los alumnos "MIXTOS" ya que séneca da un
               // error con ellos.
             " JOIN unidades AS u ON a.unidad_id=u.id AND a.curso_id=u.curso_id "
             + " JOIN tramos AS t ON t.id=h.tramo_id "
             + // Sólo hay que recuperar las que sean de actividad doncencia de alumnos con código
               // de faltas y que no están borrados, y que no son festivos
             " WHERE ce.id IS NULL AND (ap.id IS NULL || ap.excluir=0 ) AND h.actividad_id="
             + Actividad.getIdActividadDocencia(MaimonidesApp.getApplication().getAnoEscolar())
             + " AND a.borrado=0 AND a.codFaltas!='' AND p.fecha BETWEEN ? AND ? AND p.curso IN ("
             + sbCursos.toString()
             + ") AND pa.asistencia > "
             + ParteFaltas.FALTA_ASISTENCIA
             + " AND p.ano=? ORDER BY a.curso_id,a.unidad_id,idAlumno,p.fecha,asistencia,t.hora";
     System.out.println(sql);
     PreparedStatement actuParte =
         (PreparedStatement)
             MaimonidesApp.getApplication()
                 .getConector()
                 .getConexion()
                 .prepareStatement(
                     "UPDATE partes SET enviado=1, justificado=1 WHERE fecha BETWEEN ? AND ? AND curso IN ("
                         + sbCursos.toString()
                         + ") AND ano=?  ");
     st =
         (PreparedStatement)
             MaimonidesApp.getApplication().getConector().getConexion().prepareStatement(sql);
     st.setDate(1, new java.sql.Date(fechaDesde.getTime().getTime()));
     st.setDate(2, new java.sql.Date(fechaHasta.getTime().getTime()));
     st.setInt(3, MaimonidesApp.getApplication().getAnoEscolar().getId());
     actuParte.setDate(1, new java.sql.Date(fechaDesde.getTime().getTime()));
     actuParte.setDate(2, new java.sql.Date(fechaHasta.getTime().getTime()));
     actuParte.setInt(3, MaimonidesApp.getApplication().getAnoEscolar().getId());
     ResultSet res = st.executeQuery();
     Curso ultimoCurso = null;
     Unidad ultimaUnidad = null;
     Alumno ultimoAlumno = null;
     Element nCursos = getDocumento().createElement("CURSOS");
     Element nCurso = null;
     Element nUnidades = null;
     Element nUnidad = null;
     Element nAlumnos = null;
     Element nAlumno = null;
     Element nFaltas = null;
     while (res.next() && !isCancelado()) {
       setFaltasExportadas(true);
       GregorianCalendar fecha = Fechas.toGregorianCalendar(res.getDate("fecha"));
       // firePropertyChange("message", null, "Procesando asistencia ...");
       // int idParte = res.getInt("idParte");
       int tramo = res.getInt("codTramo");
       int asistencia = res.getInt("asistencia");
       int idAlumno = res.getInt("idAlumno");
       Alumno a = Alumno.getAlumno(idAlumno);
       Curso c = Curso.getCurso(a.getIdCurso());
       Unidad u = a.getUnidad();
       firePropertyChange("message", null, texto + " " + u + " " + a + "...");
       if (ultimoCurso == null || !ultimoCurso.equals(c)) {
         nCurso = getDocumento().createElement("CURSO");
         nCursos.appendChild(nCurso);
         // Creamos la linea <X_OFERTAMATRIG>2063</X_OFERTAMATRIG>
         nCurso.appendChild(crearTag("X_OFERTAMATRIG", c.getCodigo()));
         // Creamos la linea <D_OFERTAMATRIG>1º de Bachillerato (Humanidades y Ciencias
         // Sociales)</D_OFERTAMATRIG>
         nCurso.appendChild(crearTag("D_OFERTAMATRIG", c.getDescripcion()));
         // Ahora creamos el comienzo de la unidad
         nUnidades = getDocumento().createElement("UNIDADES");
         nCurso.appendChild(nUnidades);
       }
       if (ultimaUnidad == null || !ultimaUnidad.equals(u)) {
         // Creamos una nueva unidad
         nUnidad = getDocumento().createElement("UNIDAD");
         nUnidades.appendChild(nUnidad);
         // Creamos la linea <X_UNIDAD>601648</X_UNIDAD>
         nUnidad.appendChild(crearTag("X_UNIDAD", u.getCodigo()));
         // Creamos la linea <T_NOMBRE>1BTO-A</T_NOMBRE>
         nUnidad.appendChild(crearTag("T_NOMBRE", u.getCursoGrupo()));
         // Ahora creamos el comienzo de la unidad
         nAlumnos = getDocumento().createElement("ALUMNOS");
         nUnidad.appendChild(nAlumnos);
       }
       if (ultimoAlumno == null || !ultimoAlumno.equals(a)) {
         nAlumno = getDocumento().createElement("ALUMNO");
         nAlumno.appendChild(crearTag("X_MATRICULA", a.getCodFaltas()));
         // Y Creamos el inicio para las faltas de asistencia de este alumno
         nFaltas = getDocumento().createElement("FALTAS_ASISTENCIA");
         nAlumno.appendChild(nFaltas);
         nAlumnos.appendChild(nAlumno);
       }
       addFalta(fecha, tramo, asistencia, nFaltas);
       ultimoCurso = c;
       ultimaUnidad = u;
       ultimoAlumno = a;
     }
     if (isFaltasExportadas() && !isCancelado()) {
       firePropertyChange("message", null, "Generando fichero...");
       generarCabeceraFaltas(fechaDesde, fechaHasta, nCursos);
       File f = generarFicheroFaltas(fechaDesde, fechaHasta, cursos);
       if (isEnviarASeneca()) {
         firePropertyChange("message", null, "Enviando fichero a Séneca...");
         String nombre = f.getName().substring(0, f.getName().length() - 4);
         String codigoOperacion = Cripto.md5(nombre + "" + Math.random());
         int ret = enviarFichero(f, codigoOperacion);
         if (ret == GestorEnvioFaltas.RET_OK) {
           firePropertyChange(
               "message", null, "Fichero enviado correctamente. Marcando faltas como enviadas...");
           actuParte.executeUpdate();
           File enviados = new File(f.getParent(), "enviados");
           enviados.mkdirs();
           f.renameTo(new File(enviados, f.getName()));
           firePropertyChange("message", null, "Envío de faltas terminado.");
         } else {
           if (ret == GestorEnvioFaltas.RET_ERROR_PROCESANDO) {
             firePropertyChange(
                 "message", null, "Séneca ha dado errores procesando el fichero enviado.");
             File nuevo =
                 new File(getCarpetaFallidos(), nombre + "-ID" + codigoOperacion + ".xml");
             f.renameTo(nuevo);
             // Creamos el fichero de propiedades del nuevo
             File info = new File(nuevo.getParentFile(), nuevo.getName() + ".info");
             Properties p = new Properties();
             p.setProperty("desde", fechaDesde.getTimeInMillis() + "");
             p.setProperty("hasta", fechaHasta.getTimeInMillis() + "");
             p.setProperty("cursos", Str.implode(cursos, ","));
             p.setProperty("archivo", nuevo.getAbsolutePath());
             p.setProperty("codigo", codigoOperacion);
             p.setProperty("error", getClienteSeneca().getUltimoError());
             FileOutputStream fos = new FileOutputStream(info);
             p.store(fos, "Error enviando fichero de faltas");
             Obj.cerrar(fos);
             err.add(
                 "<html>Séneca ha dado errores procesando el fichero enviado:<br/> "
                     + nombre
                     + ".<br/><br/>");
             if (!getClienteSeneca().getUltimoError().equals("")) {
               err.add(getClienteSeneca().getUltimoError());
             }
           } else {
             firePropertyChange("message", null, "No se ha podido enviar el fichero a Séneca.");
             f.delete();
             err.add("<html>No se ha podido enviar el fichero:<br/> " + nombre + ".<br/><br/>");
             if (!getClienteSeneca().getUltimoError().equals("")) {
               err.add(getClienteSeneca().getUltimoError());
             }
           }
           // Si no se está loggeado cancelamos el proceso
           if (!getClienteSeneca().isLoggeado()) {
             setCancelado(true);
           }
         }
       } else {
         firePropertyChange("message", null, "Marcando faltas como enviadas...");
         actuParte.executeUpdate();
       }
     }
     Obj.cerrar(actuParte, st, res);
   } catch (Exception ex) {
     Logger.getLogger(GeneradorFicherosSeneca.class.getName()).log(Level.SEVERE, null, ex);
   }
   return err;
 }