/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package com.arrebol.taxiservicios.beans.tours;

import com.arrebol.taxiservicios.beans.TaxiGenericBean;
import com.arrebol.taxiservicios.controller.admin.GenericPersonController;
import com.arrebol.taxiservicios.controller.admin.GenericValidationController;
import com.arrebol.taxiservicios.controller.predio.PreventaController;
import com.arrebol.taxiservicios.controller.tours.ToursController;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.core.Person;
import com.arrebol.taxiservicios.model.enums.EstatusPagoMultiple;
import com.arrebol.taxiservicios.model.enums.EstatusTour;
import com.arrebol.taxiservicios.model.enums.GenericEnumType;
import com.arrebol.taxiservicios.model.enums.MetodoPago;
import com.arrebol.taxiservicios.model.enums.PeriodoPago;
import com.arrebol.taxiservicios.model.enums.PersonType;
import com.arrebol.taxiservicios.model.enums.TipoAbono;
import com.arrebol.taxiservicios.model.enums.TipoAvitacion;
import com.arrebol.taxiservicios.model.enums.TipoTour;
import com.arrebol.taxiservicios.model.tours.DetallesPagoEspacios;
import com.arrebol.taxiservicios.model.tours.EspaciosTour;
import com.arrebol.taxiservicios.model.tours.EspaciosTourView;
import com.arrebol.taxiservicios.model.tours.HistorialAbonoMultipleTours;
import com.arrebol.taxiservicios.model.tours.PagosEspacioTour;
import com.arrebol.taxiservicios.model.tours.PagosEspaciosTourDetalle;
import com.arrebol.taxiservicios.model.tours.PagosEspaciosTourView;
import com.arrebol.taxiservicios.model.tours.Tours;
import com.arrebol.taxiservicios.model.view.PersonListView;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.imageio.ImageIO;
import javax.inject.Named;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.primefaces.model.DualListModel;

/**
 *
 * @author Crov
 */
@Named("toursBean")
@ViewScoped
public class ToursBean extends TaxiGenericBean implements Serializable {

   private static final long serialVersionUID = -7284027390656669184L;
   final Logger logger = LogManager.getLogger(getClass());

   private ToursController toursController;
   private PreventaController controladorPreventa;
   private GenericPersonController personController;

   private List<Tours> listTours;
   private List<EspaciosTour> listEspaciosTour;
   private EspaciosTour selectEspaciosTour;
   private Tours selectTours;
   private List<PersonListView> personas;
   private List<PagosEspacioTour> listPagosEspacioTour;
   private List<Tours> listTourCliente;
   private PagosEspacioTour selectPago;
   private String metodoPago;
   private Integer noViajes;
   private String pagoSiguiente;
   private GenericValidationController genericController;
   private Boolean pagoExitoso;
   private String imagen;
   private Double cantidadPagada;
   private List<PagosEspaciosTourDetalle> listDetallesPago;
   private PagosEspaciosTourDetalle selectDetallePago;
   private List<DetallesPagoEspacios> listDetallesPagoEspacios;
   private DetallesPagoEspacios selectDetallesPagoEspacios;

   private DualListModel<EspaciosTour> dualEspaciosTourLst;
   private DualListModel<EspaciosTour> dualEspaciosTourLstEditar;
   private Double costoPorEspacio;
   private Boolean ninos;
   private Boolean PagaAlAbordar;
   private int numeroNinos;
   private Double costoPorNino;
   private Double totalPago;
   private Double totalPagoEstatico;

   private String descripcion;
   private Date fechaInicio;
   private Date fechaFin;
   private int numeroEspacios;
   private String comentario;
   private Double costoIndividual;

   private String selectPersona;
   private String selectTipotour;
   private String selectTipoAbitacion;
   private String periodoPago;
   private Date fechaAPagar;
   private Integer numeroPagos;
   private Double anticipo;

   private String selectPersonaCliente;
   private String selectPersonaAconpanate;

   private Boolean adutosMayores;
   private int numeroAdultosMayores;
   private Double costoPorAdultoMayor;

   private String comentariosVenta;
   private String observacionEspacio;

   private Double pagoMultiple;
   private HistorialAbonoMultipleTours selectAbonoMultiple;
   private Boolean PagoMultipleExitoso;

   private Person provider;
   private Date sixteenYearsOld;

   private JasperPrint print;
   private Boolean agregarCampos;

   @PostConstruct
   public void init() {
      logger.info("Start  init()");
      try {
         setNinos(false);
         setAdutosMayores(false);
         setPagaAlAbordar(false);
         setGenericController(new GenericValidationController());
         setControladorPreventa(new PreventaController());
         setToursController(new ToursController());
         setImagen("images/logo2t.png");
         cleanProvider();
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         setListTourCliente(new ArrayList<>());
         setPersonas(getControladorPreventa().findListOfPersonsByTypeAndTipoCliente(PersonType.CUSTOMER, getLoggedUser().getLocation(), getLoggedUser().getPerson().getClienteSpa()));
         setNumeroEspacios(1);
         setAnticipo(0.0);
         setDualEspaciosTourLst(new DualListModel<>(new ArrayList<>(), new ArrayList<>()));
         setDualEspaciosTourLstEditar(new DualListModel<>(new ArrayList<>(), new ArrayList<>()));
         setAgregarCampos(true);
      } catch (Exception e) {
         logger.error("init(): ", e);
      }
   }

   public void addTour() {
      logger.info("addTour");
      try {
         setToursController(new ToursController());
         // se genera el nuevo tour
         Tours tour = new Tours();
         tour.setEstatusActivo(GenericEnumType.ENABLED);
         tour.setLocation(getLoggedUser().getLocation());
         tour.setDescripcion(getDescripcion());
         tour.setFechaInicio(getFechaInicio());
         tour.setFechaFin(getFechaFin());
         tour.setNumeroEspacios(getNumeroEspacios());
         tour.setNumeroReservados(0);
         tour.setComentario(getComentario());
         tour.setEstatus(EstatusTour.CURSO);
         tour.setCostoIndividual(getCostoIndividual());
         tour.setCostoTotal(getCostoIndividual() * getNumeroEspacios());
         switch (getSelectTipotour()) {
            case "Concierto":
               tour.setTipo(TipoTour.CONCIERTO);
               break;
            case "Viaje familiar":
               tour.setTipo(TipoTour.VIAJE_FAMILIAR);
               break;
            case "Viaje escolar":
               tour.setTipo(TipoTour.VIAJE_ESCOLAR);
               break;
            case "Viaje turístico o traslado":
               tour.setTipo(TipoTour.VIAJE_TURISTICO_O_TRASLADO);
               break;
            case "Otro":
               tour.setTipo(TipoTour.OTRO);
               break;
            default:
               showMessage(FacesMessage.SEVERITY_ERROR, "Error", "El tipo de tour seleccionado no es válido");
               return;
         }
         getToursController().addTour(tour, getLoggedUser(), true);

         // se generan los espacios disponibles para el tour
         for (int i = 1; i <= getNumeroEspacios(); i++) {
            EspaciosTour espacio = new EspaciosTour();
            espacio.setEstatusActivo(GenericEnumType.ENABLED);
            espacio.setEstatusEspacio(GenericEnumType.DISABLED);
            espacio.setTour(tour);
            espacio.setCosto(tour.getCostoIndividual());
            espacio.setEspacioClientePago(false);
            espacio.setConsecutivo(i);
            getToursController().addEspaciosTour(espacio, getLoggedUser(), true);
         }

         getListTours().add(tour);
         showMessage(FacesMessage.SEVERITY_INFO, "Tour agregado", "El tour se agregó correctamente");
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al agregar el tour");
         logger.error("addTour(): " + e);
      }
   }

   public void cargarEspacios() {
      logger.info("cargarEspacios()");
      try {
         setToursController(new ToursController());
         setObservacionEspacio(null);
         setListEspaciosTour(getToursController().fillEspaciosTourByTour(getSelectTours()));
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al cargar la lista de espacios");
         logger.error("cargarEspacios() " + e);
      }

   }

   public void cargarEspaciosDisponibles() {
      logger.info("cargarEspaciosDisponibles()");
      try {
         setNumeroPagos(1);
         setAnticipo(0.0);
         setNumeroNinos(0);
         setCostoPorNino(0.0);
         setTotalPago(0.0);
         setCostoPorAdultoMayor(0.0);
         setNumeroAdultosMayores(0);
         setCostoPorEspacio(getSelectTours().getCostoIndividual());
         setComentariosVenta(null);
         setSelectTipoAbitacion("0");
         setToursController(new ToursController());
         List<EspaciosTour> disponobles = getToursController().fillEspaciosTourByTourDisponibles(getSelectTours());
         List<EspaciosTour> apartados = new ArrayList<>();
         setDualEspaciosTourLst(new DualListModel<>(disponobles, apartados));
         setListTourCliente(new ArrayList<>());
         actulizarTotalAPagar();
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al cargar la lista de espacios");
         logger.error("cargarEspaciosDisponibles() " + e);
      }
   }

   public void cargarEdicionEspacios(Boolean agregar) {
      logger.info("cargarEdicionEspacios()");
      setAgregarCampos(agregar);
      try {
         setToursController(new ToursController());
         List<EspaciosTour> espaciosCargados = new ArrayList<>();
         DetallesPagoEspacios detallePago = getToursController().fillDetallesPagoEspaciosById(getSelectEspaciosTour().getDetallesPagoEspacios().getId());

         List<PagosEspacioTour> pagosIn = getToursController().fillPagosEspacioTourByDetalleEspaciosTourAndEstus(detallePago, GenericEnumType.INCOMPLETE);
         if (!pagosIn.isEmpty()) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "Ahí abonos incompletos, estos no se contemplarán en el total a devolver");
         }

         if (getAgregarCampos()) {
            espaciosCargados = getToursController().fillEspaciosTourByTourDisponibles(getSelectTours());
         } else {
            espaciosCargados = getToursController().fillEspaciosTourByDetallesPagoEspacios(detallePago);
         }
         List<EspaciosTour> espaciosSeleccionados = new ArrayList<>();
         setDualEspaciosTourLstEditar(new DualListModel<>(espaciosCargados, espaciosSeleccionados));

         // Se calcula el pago restante de los abonos no pagados
         List<PagosEspacioTour> pagos = getToursController().fillPagosEspacioTourByDetalleEspaciosTourAndEstus(detallePago, GenericEnumType.ENABLED);
         Double totaP = 0.0;
         for (PagosEspacioTour pa : pagos) {
            totaP = totaP + pa.getPago();
         }

         setSelectPersona(detallePago.getCliente().getId());
         setSelectTipoAbitacion(detallePago.getIncluyeAviacion().getTipo());
         setComentariosVenta(detallePago.getComentario());

         setTotalPago(detallePago.getPagoTotal() - totaP);
         setTotalPagoEstatico(detallePago.getPagoTotal() - totaP);

         setPeriodoPago(detallePago.getPeriodoPago().getType());
         setFechaAPagar(new Date());
         setNumeroPagos(1);
         setAnticipo(0.0);

      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un erro al cargar la venta");
         logger.error("cargarEdicionEspacios() " + e);
      }
   }

   public void definirEspaciosNinos() {
      if (getNinos()) {
         logger.info("si");
      } else {
         logger.info("no");
         setNumeroNinos(0);
         setCostoPorNino(0.0);
      }
      actulizarTotalAPagar();
   }

   public void definirAdultosMayores() {
      if (getAdutosMayores()) {
         logger.info("si");
      } else {
         logger.info("no");
         setNumeroAdultosMayores(0);
         setCostoPorAdultoMayor(0.0);
      }
      actulizarTotalAPagar();
   }

   public void actulizarTotalAPagar() {
      // se actualiza el total de los pagos segun el numero de asientos, el costo por espacio
      try {
         Double totalAPagar = 0.0;
         totalAPagar = totalAPagar + ((getDualEspaciosTourLst().getTarget().size() - getNumeroNinos() - getNumeroAdultosMayores()) * getCostoPorEspacio());
         if (getNinos()) {
            totalAPagar = totalAPagar + (getNumeroNinos() * getCostoPorNino());
         }
         if (getAdutosMayores()) {
            totalAPagar = totalAPagar + (getNumeroAdultosMayores() * getCostoPorAdultoMayor());
         }
         setTotalPago(totalAPagar);
         setSelectTipoAbitacion("0");
      } catch (Exception e) {
         logger.info("Ocurrió un error al tratar de calcular el total a pagar por la compra de los espacios");
      }
   }

   public void actulizarTotalAPagarEdit() {
      // se actualiza el total de los pagos segun el numero de asientos, el costo por espacio
      try {
         Double totalAPagar = getTotalPagoEstatico();
         if (!getDualEspaciosTourLstEditar().getTarget().isEmpty()) {
            if (getAgregarCampos()) {
               totalAPagar = totalAPagar + (getDualEspaciosTourLstEditar().getTarget().size() * getSelectEspaciosTour().getTour().getCostoIndividual());
            } else {
               totalAPagar = totalAPagar - (getDualEspaciosTourLstEditar().getTarget().size() * getSelectEspaciosTour().getTour().getCostoIndividual());
            }
         }
         setTotalPago(totalAPagar);
         setSelectTipoAbitacion("0");
      } catch (Exception e) {
         logger.info("Ocurrió un error al tratar de calcular el total a pagar por la compra de los espacios");
      }
   }

   public void cargarEspaciosPorCliente() {
      logger.info("cargarEspaciosPorCliente()");
      try {
         setListTourCliente(getToursController().getlistToursByCliente(getSelectPersona()));
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al tratar de cargar el historial de tours");
         logger.error("cargarEspaciosPorCliente(): " + e);
      }
   }

   public void updateTour() {
      logger.info("updateTour");
      try {
         setToursController(new ToursController());
         getSelectTours().setDescripcion(getDescripcion());
         getSelectTours().setFechaInicio(getFechaInicio());
         getSelectTours().setFechaFin(getFechaFin());
         getSelectTours().setComentario(getComentario());
         switch (getSelectTipotour()) {
            case "Concierto":
               getSelectTours().setTipo(TipoTour.CONCIERTO);
               break;
            case "Viaje familiar":
               getSelectTours().setTipo(TipoTour.VIAJE_FAMILIAR);
               break;
            case "Viaje escolar":
               getSelectTours().setTipo(TipoTour.VIAJE_ESCOLAR);
               break;
            case "Viaje turístico o traslado":
               getSelectTours().setTipo(TipoTour.VIAJE_TURISTICO_O_TRASLADO);
               break;
            case "Otro":
               getSelectTours().setTipo(TipoTour.OTRO);
               break;
            default:
               throw new AssertionError();
         }
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         clearSelected();
         showMessage(FacesMessage.SEVERITY_INFO, "Tour actualizado", "El tour se actualizo correctamente");
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al agregar el tour");
         logger.error("updateTour(): " + e);
      }
   }

   public void updatedForm() {
      setDescripcion(getSelectTours().getDescripcion());
      setFechaInicio(getSelectTours().getFechaInicio());
      setFechaFin(getSelectTours().getFechaFin());
      setNumeroEspacios(getSelectTours().getNumeroEspacios());
      setComentario(getSelectTours().getComentario());
      setSelectTipotour(getSelectTours().getTipo().getTransfer());
   }

   public void cancelarTour() {
      logger.info("cancelarTour");
      try {
         setToursController(new ToursController());
         getSelectTours().setEstatus(EstatusTour.CANCELADO);
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         clearSelected();
         showMessage(FacesMessage.SEVERITY_INFO, "Tour eliminado", "El tour se eliminó correctamente");
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al eliminar el tour");
         logger.error("cancelarTour(): " + e);
      }
   }

   public void terminararTour() {
      logger.info("terminararTour");
      try {
         setToursController(new ToursController());
         getSelectTours().setEstatus(EstatusTour.TERMINADO);
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         clearSelected();
         showMessage(FacesMessage.SEVERITY_INFO, "Tour eliminado", "El tour se marcó como terminado  correctamente ");
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al terminado  el tour");
         logger.error("terminararTour(): " + e);
      }
   }

   public void clearSelected() {
      setSelectTours(null);
      setDescripcion(null);
      setFechaInicio(null);
      setFechaFin(null);
      setNumeroEspacios(1);
      setComentario(null);
      setSelectTipotour("0");
   }

   public void clearVentaForm() {
      setAgregarCampos(true);
      setSelectPersona(null);
      setPeriodoPago(null);
      setFechaAPagar(null);
      setMetodoPago(null);
      setNumeroPagos(0);
      setAnticipo(0.0);
      setTotalPago(0.0);
      setTotalPagoEstatico(0.0);
      setSelectTipoAbitacion("0");
      setComentariosVenta("");
   }

   public void clearMultipleForm() {
      setPagoMultiple(0.0);
      setMetodoPago(null);
   }

   public void addVentaEspacio() {
      logger.info("addVentaEspacio()");
      try {

         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Dia cerrado", "No se puede hacer ninguna acción porque ya existe un cuadre de caja chica del día");
            return;
         }

         if (getNumeroPagos() < 1) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El número de pagos debe de ser mayor a 0");
            return;
         }

         if (getTotalPago() < 0) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El total del pago debe ser mínimo 0");
            return;
         }

         if (getAnticipo() > getTotalPago()) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El anticipo no puede ser mayor que el costo total delos espacios del tour");
            return;
         }

         if (getDualEspaciosTourLst().getTarget().size() < (getNumeroNinos() + getNumeroAdultosMayores())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El número de espacios especiales es mayor al número de espacios seleccionados");
            return;
         }

         setToursController(new ToursController());

         // se crea el detalle de los campos asociados 
         DetallesPagoEspacios detalleEspacios = new DetallesPagoEspacios();
         detalleEspacios.setCliente(new Person(getSelectPersona()));
         detalleEspacios.setEstatusActivo(GenericEnumType.ENABLED);
         detalleEspacios.setTour(getSelectTours());
         detalleEspacios.setCostoEspacio(getCostoPorEspacio());
         detalleEspacios.setNoEspacios(getDualEspaciosTourLst().getTarget().size() - getNumeroNinos() - getNumeroAdultosMayores());
         detalleEspacios.setCostoEspacioNino(getCostoPorNino());
         detalleEspacios.setNoEspaciosNino(getNumeroNinos());
         detalleEspacios.setCostoEspacioAdultoMayor(getCostoPorAdultoMayor());
         detalleEspacios.setNoEspaciosAdultoMayor(getNumeroAdultosMayores());
         detalleEspacios.setTotalEspacios(getDualEspaciosTourLst().getTarget().size());
         detalleEspacios.setAnticipo(getAnticipo());
         detalleEspacios.setPago(getTotalPago());
         detalleEspacios.setPagoTotal(getTotalPago() - getAnticipo());
         detalleEspacios.setCantidadPagada(0.0);
         if (getPagaAlAbordar()) {
            detalleEspacios.setPagaAlAbordar(GenericEnumType.ENABLED);
         } else {
            detalleEspacios.setPagaAlAbordar(GenericEnumType.DISABLED);
         }
         detalleEspacios.setComentario(getComentariosVenta());
         detalleEspacios.setEstatus(GenericEnumType.INCOMPLETE);
         // se define el periodo de pago para espacios del tour
         switch (getPeriodoPago()) {
            case "MENSUAL":
               detalleEspacios.setPeriodoPago(PeriodoPago.MENSUAL);
               break;
            case "QUINCENAL":
               detalleEspacios.setPeriodoPago(PeriodoPago.QUINCENAL);
               break;
            default:
               showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se seleccionó un periodo válido ");
               logger.error("No se seleccionó un periodo válido ");
               return;
         }
         switch (getSelectTipoAbitacion()) {
            case "Sencilla":
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.SENCILLA);
               break;
            case "Doble":
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.DOBLE);
               break;
            case "Triple":
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.TRIPLE);
               break;
            case "Cuádruple":
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.CUADRUPLE);
               break;
            case "No Incluye":
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.NO_INCLUYE);
               break;
            default:
               detalleEspacios.setIncluyeAviacion(TipoAvitacion.NO_INCLUYE);
               break;
         }
         getToursController().addDetallesPagoEspacios(detalleEspacios, getLoggedUser(), true);

         // se actualizan los espacios seleccionados
         Boolean valPrimerCampo = true;
         for (EspaciosTour espaciosTour : getDualEspaciosTourLst().getTarget()) {
            // se le asigna el primer espacio al cliente que pagara 
            if (valPrimerCampo) {
               espaciosTour.setCliente(new Person(getSelectPersona()));
               espaciosTour.setEspacioClientePago(true);
            }
            valPrimerCampo = false;
            espaciosTour.setEstatusEspacio(GenericEnumType.ENABLED);
            espaciosTour.setClientePago(new Person(getSelectPersona()));
            espaciosTour.setDetallesPagoEspacios(detalleEspacios);
            getToursController().addEspaciosTour(espaciosTour, getLoggedUser(), false);
         }

         //Se actualiza el número de espacios vendidos del tour
         getSelectTours().setNumeroReservados(getSelectTours().getNumeroReservados() + getDualEspaciosTourLst().getTarget().size());
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);

         // se genera el listado de fechas para los pagos
         Calendar fechaInicial = Calendar.getInstance();
         fechaInicial.setTime(getFechaAPagar());
         fechaInicial.add(Calendar.DATE, 1);
         Boolean mensual;
         mensual = detalleEspacios.getPeriodoPago() == PeriodoPago.MENSUAL;
         List<Calendar> fechasPago = new ArrayList<>();
         fechasPago = generarFechasDePago(fechaInicial, getNumeroPagos(), mensual);

         // monto a pagar sin el anticipo
         Double montoInd = detalleEspacios.getPagoTotal() / getNumeroPagos();

         //Se valida si se dio algun anticipo
         if (getAnticipo() > 0.0) {
            // se optiene el folio consecutivo
            String folio = obtenerFolio("T");
            if (folio != null) {
               PagosEspacioTour ant = new PagosEspacioTour();
               ant.setEstatusActivo(GenericEnumType.ENABLED);
               ant.setDetallesPagoEspacios(detalleEspacios);
               ant.setTipoPago(TipoAbono.ANTICIPO);
               ant.setPagoEstatus(GenericEnumType.ENABLED);
               switch (getMetodoPago()) {
                  case "TARJETA_DEBITO":
                     ant.setMetodoPago(MetodoPago.TARJETA_DEBITO);
                     break;
                  case "TARJETA_CREDITO":
                     ant.setMetodoPago(MetodoPago.TARJETA_CREDITO);
                     break;
                  case "DEPOSITO_TRANSFERENCIA":
                     ant.setMetodoPago(MetodoPago.DEPOSITO_TRANSFERENCIA);
                     break;
                  case "EFECTIVO":
                     ant.setMetodoPago(MetodoPago.EFECTIVO);
                     break;
                  case "TRES_MESES_SIN_INTERESES":
                     ant.setMetodoPago(MetodoPago.TRES_MESES_SIN_INTERESES);
                     break;
                  case "SEIS_MESES_SIN_INTERESES":
                     ant.setMetodoPago(MetodoPago.SEIS_MESES_SIN_INTERESES);
                     break;
                  case "NUEVE_MESES_SIN_INTERESES":
                     ant.setMetodoPago(MetodoPago.NUEVE_MESES_SIN_INTERESES);
                     break;
                  case "DOCE_MESES_SIN_INTERESES":
                     ant.setMetodoPago(MetodoPago.DOCE_MESES_SIN_INTERESES);
                     break;
                  case "CLIP":
                     ant.setMetodoPago(MetodoPago.CLIP);
                     break;
                  case "OTRO":
                     ant.setMetodoPago(MetodoPago.OTRO);
                     break;
                  default:
                     ant.setMetodoPago(MetodoPago.OTRO);
               }
               ant.setFechaPago(correcciónDeHr(new Date(), -7));
               ant.setCobro(getLoggedUser());
               ant.setFechaAPagar(correcciónDeHr(new Date(), -7));
               ant.setFolio(folio);
               ant.setPago(getAnticipo());
               ant.setCantidadPagada(getAnticipo());
               ant.setDiferenciaPago(getTotalPago() - getAnticipo());// Es el pago restante de la venta total solo para el anticipo
               getToursController().addPagosEspacioTour(ant, getLoggedUser(), true);
               logger.info("Anticipo creado correctamente");
            } else {
               logger.error("Ocurrió un error al generar el folio");
               showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al generar el folio");
            }
         }

         // se registran los pagos
         recorrerFechas(fechasPago, detalleEspacios, montoInd);

         // se actualizan  los listados
         clearVentaForm();
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         cargarEspacios();

         logger.info("Espacio vendido");
         showMessage(FacesMessage.SEVERITY_INFO, "Espacio vendido", "El espacio del tour se ha vendido correctamente");

      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al vender el espacio");
         logger.error("addVentaEspacio() " + e);
      }
   }

   public void agregarVentaEspacio() {
      logger.info("agregarVentaEspacio()");
      try {

         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Dia cerrado", "No se puede hacer ninguna acción porque ya existe un cuadre de caja chica del día");
            return;
         }

         if (getNumeroPagos() < 1) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El número de pagos debe de ser mayor a 0");
            return;
         }

         if (getTotalPago() < 0) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El total del pago debe ser mínimo 0");
            return;
         }

         if (getDualEspaciosTourLstEditar().getTarget().size() <= 0) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "Debe seleccionar al menos un espacio");
            return;
         }

         setToursController(new ToursController());

         // se crea el detalle de los campos asociados 
         DetallesPagoEspacios detalleEspacios = getSelectEspaciosTour().getDetallesPagoEspacios();

         // Se deshabilitan los pagos no pagados
         List<PagosEspacioTour> pagos = getToursController().fillPagosEspacioTourByDetalleEspaciosTourAndEstus(detalleEspacios, GenericEnumType.DISABLED);
         for (PagosEspacioTour pa : pagos) {
            pa.setEstatusActivo(GenericEnumType.DISABLED);
            getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
         }

         // se actualizan los espacios seleccionados
         for (EspaciosTour espaciosTour : getDualEspaciosTourLstEditar().getTarget()) {
            espaciosTour.setEstatusEspacio(GenericEnumType.ENABLED);
            espaciosTour.setClientePago(new Person(getSelectPersona()));
            espaciosTour.setDetallesPagoEspacios(detalleEspacios);
            getToursController().addEspaciosTour(espaciosTour, getLoggedUser(), false);
         }

         //Se actualiza el número de espacios vendidos del tour
         getSelectTours().setNumeroReservados(getSelectTours().getNumeroReservados() + getDualEspaciosTourLstEditar().getTarget().size());
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);

         // se genera el listado de fechas para los pagos
         Calendar fechaInicial = Calendar.getInstance();
         fechaInicial.setTime(getFechaAPagar());
         fechaInicial.add(Calendar.DATE, 1);
         Boolean mensual;
         mensual = detalleEspacios.getPeriodoPago() == PeriodoPago.MENSUAL;
         List<Calendar> fechasPago = new ArrayList<>();
         fechasPago = generarFechasDePago(fechaInicial, getNumeroPagos(), mensual);

         // monto a pagar sin el anticipo
         Double montoInd = getTotalPago() / getNumeroPagos();

         // se registran los pagos
         recorrerFechas(fechasPago, detalleEspacios, montoInd);

         //Se actulisa el total del detalle
         Double sumDif = getTotalPago() - getTotalPagoEstatico();
         if (sumDif > 0) {
            detalleEspacios.setPagoTotal(detalleEspacios.getPagoTotal() + sumDif);
         } else {
            detalleEspacios.setPagoTotal(detalleEspacios.getPagoTotal() - sumDif);
         }
         getToursController().addDetallesPagoEspacios(detalleEspacios, getLoggedUser(), false);

         // se actualizan  los listados
         clearVentaForm();
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         cargarEspacios();

         logger.info("Espacio vendido");
         showMessage(FacesMessage.SEVERITY_INFO, "Espacio vendido", "El espacio del tour se ha vendido correctamente");

      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al vender el espacio");
         logger.error("agregarVentaEspacio() " + e);
      }
   }

   public void quitarVentaEspacio() {
      logger.info("quitarVentaEspacio()");
      try {

         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Dia cerrado", "No se puede hacer ninguna acción porque ya existe un cuadre de caja chica del día");
            return;
         }

         if (getNumeroPagos() < 1) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El número de pagos debe de ser mayor a 0");
            return;
         }

         if (getTotalPago() < 0) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El total del pago debe ser mínimo 0");
            return;
         }

         if (getDualEspaciosTourLstEditar().getTarget().size() <= 0) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "Debe seleccionar al menos un espacio");
            return;
         }

         setToursController(new ToursController());

         // se carga el detalle de los campos asociados 
         DetallesPagoEspacios detalleEspacios = getSelectEspaciosTour().getDetallesPagoEspacios();

         // Se deshabilitan los pagos no pagados
         List<PagosEspacioTour> pagos = getToursController().fillPagosEspacioTourByDetalleEspaciosTourAndEstus(detalleEspacios, GenericEnumType.DISABLED);
         for (PagosEspacioTour pa : pagos) {
            pa.setEstatusActivo(GenericEnumType.DISABLED);
            getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
         }

         // se actualizan los espacios eliminados
         for (EspaciosTour espaciosTour : getDualEspaciosTourLstEditar().getTarget()) {
            espaciosTour.setEstatusEspacio(GenericEnumType.DISABLED);
            espaciosTour.setClientePago(null);
            espaciosTour.setCliente(null);
            espaciosTour.setDetallesPagoEspacios(null);
            getToursController().addEspaciosTour(espaciosTour, getLoggedUser(), false);
         }

         // monto a pagar sin el anticipo
         Double montoInd = getTotalPago() / getNumeroPagos();

         // se genera el listado de fechas para los pagos
         Calendar fechaInicial = Calendar.getInstance();
         fechaInicial.setTime(getFechaAPagar());
         fechaInicial.add(Calendar.DATE, 1);
         Boolean mensual;
         mensual = detalleEspacios.getPeriodoPago() == PeriodoPago.MENSUAL;
         List<Calendar> fechasPago = new ArrayList<>();
         fechasPago = generarFechasDePago(fechaInicial, getNumeroPagos(), mensual);

         //Se actualiza el número de espacios vendidos del tour
         getSelectTours().setNumeroReservados(getSelectTours().getNumeroReservados() - getDualEspaciosTourLstEditar().getTarget().size());
         getToursController().addTour(getSelectTours(), getLoggedUser(), false);

         // se registran los pagos
         recorrerFechas(fechasPago, detalleEspacios, montoInd);

         //Se actulisa el total del detalle
         Double sumDif = getTotalPago() - getTotalPagoEstatico();
         if (sumDif > 0) {
            detalleEspacios.setPagoTotal(detalleEspacios.getPagoTotal() + sumDif);
         } else {
            detalleEspacios.setPagoTotal(detalleEspacios.getPagoTotal() - sumDif);
         }
         getToursController().addDetallesPagoEspacios(detalleEspacios, getLoggedUser(), false);

         // se actualizan  los listados
         clearVentaForm();
         setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         cargarEspacios();

         logger.info("Espacio vendido");
         showMessage(FacesMessage.SEVERITY_INFO, "Espacio vendido", "El espacio del tour se ha vendido correctamente");

      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al vender el espacio");
         logger.error("quitarVentaEspacio() " + e);
      }
   }

   public void recorrerFechas(List<Calendar> fechas, DetallesPagoEspacios detalleEspacioTour, Double pagoPorMes) {
      logger.info("Insertando pagos por fecha");

      // Usamos Collections.sort() para ordenar la lista de fechas
      Collections.sort(fechas, new Comparator<Calendar>() {
         @Override
         public int compare(Calendar fecha1, Calendar fecha2) {
            // Comparamos las fechas usando el método compareTo()
            return fecha1.compareTo(fecha2);
         }
      });

      // recorre las fechas ordenadas
      setToursController(new ToursController());
      for (Calendar fecha : fechas) {
         try {
            PagosEspacioTour pago = new PagosEspacioTour();
            pago.setEstatusActivo(GenericEnumType.ENABLED);
            pago.setDetallesPagoEspacios(detalleEspacioTour);
            pago.setTipoPago(TipoAbono.ABONO);
            pago.setPagoEstatus(GenericEnumType.DISABLED);
            pago.setFechaAPagar(fecha.getTime());
            pago.setPago(pagoPorMes);
            getToursController().addPagosEspacioTour(pago, getLoggedUser(), true);
         } catch (Exception e) {
            logger.error("Error al generar el pago correspondiente a la fecha: (" + fecha + ") " + e);
            showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al generar el pago correspondiente a la fecha   (" + fecha + ")");
         }
      }
   }

   public List<Calendar> generarFechasDePago(Calendar fechaInicial, int numeroPagos, Boolean mensual) {
      logger.info("Generación fechas");
      //lista de fechas
      List<Calendar> fechasDePago = new ArrayList<>();
      if (mensual) {
         fechasDePago.add(fechaInicial);
      }
      Calendar fechaActual = (Calendar) fechaInicial.clone();
      //captura el dia de una fecha en caso de que no se encuntre en le mes siguinte
      boolean detenerFecha = false;
      Calendar fechaDetenida = Calendar.getInstance();

      if (mensual) {
         for (int i = 1; i < numeroPagos; i++) {
            Calendar fechaSiguiente = (Calendar) fechaActual.clone();
            fechaSiguiente.add(Calendar.MONTH, 1);
            if (detenerFecha == false) {
               if (fechaActual.get(Calendar.DAY_OF_MONTH) > fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH)) {
                  detenerFecha = true;
                  fechaDetenida = (Calendar) fechaActual.clone();
                  fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
               }
            } else {
               if (fechaSiguiente.get(Calendar.DAY_OF_MONTH) <= fechaDetenida.get(Calendar.DAY_OF_MONTH)) {
                  detenerFecha = false;
                  fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaDetenida.get(Calendar.DAY_OF_MONTH));
               } else {
                  fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
               }
            }
            fechasDePago.add(fechaSiguiente);
            fechaActual = fechaSiguiente;
         }
      } else {
         for (int i = 0; i < numeroPagos;) {
            Calendar fechaSiguiente = (Calendar) fechaActual.clone();
            fechaSiguiente.set(Calendar.DAY_OF_MONTH, 15);
            fechasDePago.add(fechaSiguiente);
            i++;

            if (i < numeroPagos) {
               fechaSiguiente = (Calendar) fechaActual.clone();
               fechaSiguiente.set(Calendar.DAY_OF_MONTH, fechaSiguiente.getActualMaximum(Calendar.DAY_OF_MONTH));
               fechasDePago.add(fechaSiguiente);
               i++;
            }
            fechaActual.add(Calendar.MONTH, 1);
         }
      }

      return fechasDePago;
   }

   private String obtenerFolio(String ab) {
      logger.info("Obteniendo último folio ");
      try {
         setToursController(new ToursController());
         // Se cargan los abonos para validar el folio
         String abreviacion = ab;
         PagosEspaciosTourView ultimoFolio = getToursController().fillFoliosByPagos(abreviacion);
         // Se valida si ya existen folios registrados para el proyecto y se le asigna el último encontrado
         int consecutivo = 0;
         if (ultimoFolio != null) {
            String folior = ultimoFolio.getFolio();
            // Dividir el folio en partes utilizando el guion como separador
            String[] partes = folior.split("-");
            // Obtener la parte del consecutivo
            String consecutivoParte = partes[1];
            // Convertir el consecutivo a un entero
            consecutivo = Integer.parseInt(consecutivoParte);
         }
         // se formatea el consecutivo
         String numeroFormateado = String.format("%06d", consecutivo + 1);
         String folio;
         if (consecutivo == 0) {
            folio = abreviacion + "-" + "000001";
         } else {
            folio = abreviacion + "-" + numeroFormateado;
         }
         return folio;
      } catch (NumberFormatException e) {
         logger.error("Ocurrió un erro al obtener el último folio ");
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al generar el folio");
         return null;
      }
   }

   public void fillPagos() {
      logger.info("fillPagos()");
      try {

         // Se carga la lista de pagos
         setToursController(new ToursController());
         setListPagosEspacioTour(getToursController().fillPagosEspacioTourByDetalleEspaciosTour(getSelectEspaciosTour().getDetallesPagoEspacios()));

         // Encuentra el índice del primer ANTICIPO (si existe)
         int indiceAnticipo = -1;
         for (int i = 0; i < getListPagosEspacioTour().size(); i++) {
            if (TipoAbono.ANTICIPO.equals(getListPagosEspacioTour().get(i).getTipoPago())) {
               indiceAnticipo = i;
               break;
            }
         }

         // Mueve el ANTICIPO al principio de la lista (si existe)
         if (indiceAnticipo != -1) {
            PagosEspacioTour ant = getListPagosEspacioTour().remove(indiceAnticipo);
            getListPagosEspacioTour().add(0, ant);
         }

         // se encuentra el siguiente pago a realizar
         String idSiguinetePago = "";
         for (PagosEspacioTour pa : getListPagosEspacioTour()) {
            if (pa.getPagoEstatus().equals(GenericEnumType.INCOMPLETE) || pa.getPagoEstatus().equals(GenericEnumType.DISABLED)) {
               idSiguinetePago = pa.getId();
               break;
            }
         }
         setPagoSiguiente(idSiguinetePago);

      } catch (Exception e) {
         logger.error("Error a la cargar los pagos " + e);
      }
   }

   public void pagar() {
      logger.info("pagar()");
      try {
         setToursController(new ToursController());

         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Dia cerrado", "No se puede hacer ninguna acción porque ya existe un cuadre de caja chica del día");
            return;
         }

         // Redondeo el pago esperado a 2 decimales para verificar que el pago dado no sea mayor al esperado
         BigDecimal bd1 = new BigDecimal(getSelectPago().getPago());
         bd1 = bd1.setScale(2, RoundingMode.HALF_UP);
         double pagoRedondeado1 = bd1.doubleValue();
         if (getCantidadPagada() > pagoRedondeado1) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "No puedes pagar más de la cantidad a pagar");
            return;
         }

         // se valida el pago realizado sea el siguiente según el consecutivo
         if (getSelectPago().getId().equals(getPagoSiguiente())) {

            // se optiene el folio consecutivo
            String folio = obtenerFolio("T");
            if (folio != null) {
               // Redondeo el pago esperado a 2 decimales para verificar que el pago dado no sea mayor al esperado
               BigDecimal bd = new BigDecimal(getSelectPago().getPago());
               bd = bd.setScale(2, RoundingMode.HALF_UP);
               double pagoRedondeado = bd.doubleValue();

               // se valida la cantidad pagada con respecto a lo que se debe de pagar
               if (getCantidadPagada() < pagoRedondeado) {
                  // se actualiza el pago incompleto
                  getSelectPago().setPagoEstatus(GenericEnumType.INCOMPLETE);
                  getSelectPago().setCantidadPagada(getCantidadPagada());
                  getSelectPago().setDiferenciaPago(getSelectPago().getPago() - getCantidadPagada());
                  getToursController().addPagosEspacioTour(getSelectPago(), getLoggedUser(), false);

                  // se crea el sub abono
                  PagosEspaciosTourDetalle pagoDetalle = new PagosEspaciosTourDetalle();

                  // Se valida el metodo de pago 
                  switch (getMetodoPago()) {
                     case "TARJETA_DEBITO":
                        pagoDetalle.setMetodoPago(MetodoPago.TARJETA_DEBITO);
                        break;
                     case "TARJETA_CREDITO":
                        pagoDetalle.setMetodoPago(MetodoPago.TARJETA_CREDITO);
                        break;
                     case "DEPOSITO_TRANSFERENCIA":
                        pagoDetalle.setMetodoPago(MetodoPago.DEPOSITO_TRANSFERENCIA);
                        break;
                     case "EFECTIVO":
                        pagoDetalle.setMetodoPago(MetodoPago.EFECTIVO);
                        break;
                     case "TRES_MESES_SIN_INTERESES":
                        pagoDetalle.setMetodoPago(MetodoPago.TRES_MESES_SIN_INTERESES);
                        break;
                     case "SEIS_MESES_SIN_INTERESES":
                        pagoDetalle.setMetodoPago(MetodoPago.SEIS_MESES_SIN_INTERESES);
                        break;
                     case "NUEVE_MESES_SIN_INTERESES":
                        pagoDetalle.setMetodoPago(MetodoPago.NUEVE_MESES_SIN_INTERESES);
                        break;
                     case "DOCE_MESES_SIN_INTERESES":
                        pagoDetalle.setMetodoPago(MetodoPago.DOCE_MESES_SIN_INTERESES);
                        break;
                     case "CLIP":
                        pagoDetalle.setMetodoPago(MetodoPago.CLIP);
                        break;
                     case "OTRO":
                        pagoDetalle.setMetodoPago(MetodoPago.OTRO);
                        break;
                     default:
                        pagoDetalle.setMetodoPago(MetodoPago.OTRO);
                  }

                  pagoDetalle.setPagosEspacioTour(getSelectPago());
                  pagoDetalle.setPago(getCantidadPagada());
                  pagoDetalle.setEstatusActivo(GenericEnumType.ENABLED);
                  pagoDetalle.setPagoEstatus(GenericEnumType.ENABLED);
                  pagoDetalle.setFolio(folio);
                  getToursController().addPagosEspaciosTourDetalle(pagoDetalle, getLoggedUser(), true);

                  // se actualiza la cantidad pagada del detalle espacio
                  DetallesPagoEspacios detalle = getSelectEspaciosTour().getDetallesPagoEspacios();
                  detalle.setCantidadPagada(detalle.getCantidadPagada() + getCantidadPagada());
                  getToursController().addDetallesPagoEspacios(detalle, getLoggedUser(), false);

                  // se actualizan los datos del pago principal solo para la impresión del ticket
                  getSelectPago().setFolio(folio);
                  getSelectPago().setCobro(getLoggedUser());

               } else if (getCantidadPagada() == pagoRedondeado) {

                  // Se valida el metodo de pago
                  switch (getMetodoPago()) {
                     case "TARJETA_DEBITO":
                        getSelectPago().setMetodoPago(MetodoPago.TARJETA_DEBITO);
                        break;
                     case "TARJETA_CREDITO":
                        getSelectPago().setMetodoPago(MetodoPago.TARJETA_CREDITO);
                        break;
                     case "DEPOSITO_TRANSFERENCIA":
                        getSelectPago().setMetodoPago(MetodoPago.DEPOSITO_TRANSFERENCIA);
                        break;
                     case "EFECTIVO":
                        getSelectPago().setMetodoPago(MetodoPago.EFECTIVO);
                        break;
                     case "TRES_MESES_SIN_INTERESES":
                        getSelectPago().setMetodoPago(MetodoPago.TRES_MESES_SIN_INTERESES);
                        break;
                     case "SEIS_MESES_SIN_INTERESES":
                        getSelectPago().setMetodoPago(MetodoPago.SEIS_MESES_SIN_INTERESES);
                        break;
                     case "NUEVE_MESES_SIN_INTERESES":
                        getSelectPago().setMetodoPago(MetodoPago.NUEVE_MESES_SIN_INTERESES);
                        break;
                     case "DOCE_MESES_SIN_INTERESES":
                        getSelectPago().setMetodoPago(MetodoPago.DOCE_MESES_SIN_INTERESES);
                        break;
                     case "CLIP":
                        getSelectPago().setMetodoPago(MetodoPago.CLIP);
                        break;
                     case "OTRO":
                        getSelectPago().setMetodoPago(MetodoPago.OTRO);
                        break;
                     default:
                        getSelectPago().setMetodoPago(MetodoPago.OTRO);
                  }
                  // se actualiza el pago
                  getSelectPago().setPagoEstatus(GenericEnumType.ENABLED);
                  getSelectPago().setFolio(folio);
                  getSelectPago().setFechaPago(correcciónDeHr(new Date(), -7));
                  getSelectPago().setCobro(getLoggedUser());
                  getSelectPago().setCantidadPagada(getCantidadPagada());
                  getSelectPago().setDiferenciaPago(0.0);
                  getToursController().addPagosEspacioTour(getSelectPago(), getLoggedUser(), false);

                  // se actualiza la cantidad pagada del detalle espacio
                  DetallesPagoEspacios detalle = getSelectEspaciosTour().getDetallesPagoEspacios();
                  detalle.setCantidadPagada(detalle.getCantidadPagada() + getCantidadPagada());
                  getToursController().addDetallesPagoEspacios(detalle, getLoggedUser(), false);

                  // se valida si ya fue liquidado el espacio
                  double cantidadPa = detalle.getCantidadPagada();
                  double costoTotal = detalle.getPagoTotal();
                  double tolerancia = 0.001;
                  if (Math.abs(cantidadPa - costoTotal) < tolerancia) {
                     detalle.setEstatus(GenericEnumType.ENABLED);
                     setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
                  }
                  getToursController().addDetallesPagoEspacios(detalle, getLoggedUser(), false);
               }

               // se actualizan datos vista
               fillPagos();
               cargarEspacios();
               setPagoExitoso(true);
               reimprimirTicket();
               showMessage(FacesMessage.SEVERITY_INFO, "Pago realizado", "Pago realizado correctamente");
               logger.info("Pago realizado correctamente");
            } else {
               logger.error("Ocurrió un error al generar el folio");
               showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al generar el folio");
            }

         } else {
            setPagoExitoso(false);
            setNoViajes(0);
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "Solo puede pagar el pago consecutivo");
         }

      } catch (Exception e) {
         setPagoExitoso(false);
         setNoViajes(0);
         showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al realizar el pago");
         logger.error("pagar(): " + e);
      }
   }

   public void pagarIncompleto() {
      logger.info("pagarIncompleto()");
      try {

         setToursController(new ToursController());

         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Dia cerrado", "No se puede hacer ninguna acción porque ya existe un cuadre de caja chica del día");
            return;
         }

         // Redondeo el pago esperado a 2 decimales para verificar que el pago dado no sea mayor al esperado
         BigDecimal bd1 = new BigDecimal(getSelectPago().getDiferenciaPago());
         bd1 = bd1.setScale(2, RoundingMode.HALF_UP);
         double pagoRedondeado1 = bd1.doubleValue();
         if (getCantidadPagada() > pagoRedondeado1) {
            setPagoExitoso(false);
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "No puedes pagar más de la cantidad restante del pago");
            return;
         }

         // se valida el pago realizado sea el siguiente según el consecutivo
         if (getSelectPago().getId().equals(getPagoSiguiente())) {

            // Redondeo el pago esperado a 2 decimales para verificar que el pago dado no sea mayor al esperado
            BigDecimal bd = new BigDecimal(getSelectPago().getDiferenciaPago());
            bd = bd.setScale(2, RoundingMode.HALF_UP);
            double pagoRedondeado = bd.doubleValue();
            getSelectPago().setDiferenciaPago(pagoRedondeado - getCantidadPagada());
            getSelectPago().setCantidadPagada(getSelectPago().getCantidadPagada() + getCantidadPagada());
            // se valida si el abono se pago completamente
            PagosEspaciosTourDetalle pagoDetalle = new PagosEspaciosTourDetalle();
            // Se valida el metodo de pago 
            switch (getMetodoPago()) {
               case "TARJETA_DEBITO":
                  pagoDetalle.setMetodoPago(MetodoPago.TARJETA_DEBITO);
                  break;
               case "TARJETA_CREDITO":
                  pagoDetalle.setMetodoPago(MetodoPago.TARJETA_CREDITO);
                  break;
               case "DEPOSITO_TRANSFERENCIA":
                  pagoDetalle.setMetodoPago(MetodoPago.DEPOSITO_TRANSFERENCIA);
                  break;
               case "EFECTIVO":
                  pagoDetalle.setMetodoPago(MetodoPago.EFECTIVO);
                  break;
               case "TRES_MESES_SIN_INTERESES":
                  pagoDetalle.setMetodoPago(MetodoPago.TRES_MESES_SIN_INTERESES);
                  break;
               case "SEIS_MESES_SIN_INTERESES":
                  pagoDetalle.setMetodoPago(MetodoPago.SEIS_MESES_SIN_INTERESES);
                  break;
               case "NUEVE_MESES_SIN_INTERESES":
                  pagoDetalle.setMetodoPago(MetodoPago.NUEVE_MESES_SIN_INTERESES);
                  break;
               case "DOCE_MESES_SIN_INTERESES":
                  pagoDetalle.setMetodoPago(MetodoPago.DOCE_MESES_SIN_INTERESES);
                  break;
               case "CLIP":
                  pagoDetalle.setMetodoPago(MetodoPago.CLIP);
                  break;
               case "OTRO":
                  pagoDetalle.setMetodoPago(MetodoPago.OTRO);
                  break;
               default:
                  pagoDetalle.setMetodoPago(MetodoPago.OTRO);
            }

            if (getSelectPago().getDiferenciaPago() > 0) {
               // se optiene el folio consecutivo
               String folio = obtenerFolio("T");
               if (folio != null) {
                  // se crea el sub abono
                  pagoDetalle.setPagosEspacioTour(getSelectPago());
                  pagoDetalle.setPago(getCantidadPagada());
                  pagoDetalle.setEstatusActivo(GenericEnumType.ENABLED);
                  pagoDetalle.setPagoEstatus(GenericEnumType.ENABLED);
                  pagoDetalle.setFolio(folio);
                  getToursController().addPagosEspaciosTourDetalle(pagoDetalle, getLoggedUser(), true);
                  getToursController().addPagosEspacioTour(getSelectPago(), getLoggedUser(), false);
                  logger.info("Sub abono creado correctamente");
               } else {
                  logger.error("Ocurrió un error al generar el folio");
                  showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al generar el folio para el abono principal");
               }
            } else {
               // se optiene el folio consecutivo
               String folio = obtenerFolio("T");
               if (folio != null) {
                  // se crea el sub abono
                  pagoDetalle.setPagosEspacioTour(getSelectPago());
                  pagoDetalle.setPago(getCantidadPagada());
                  pagoDetalle.setEstatusActivo(GenericEnumType.ENABLED);
                  pagoDetalle.setPagoEstatus(GenericEnumType.ENABLED);
                  pagoDetalle.setFolio(folio);
                  getToursController().addPagosEspaciosTourDetalle(pagoDetalle, getLoggedUser(), true);
                  logger.info("Sub abono creado correctamente");
                  // se crea el sub abono
                  String folio2 = obtenerFolio("T");
                  if (folio2 != null) {
                     getSelectPago().setFolio(folio2);
                     getSelectPago().setCobro(getLoggedUser());
                     getSelectPago().setPagoEstatus(GenericEnumType.ENABLED);
                     getToursController().addPagosEspacioTour(getSelectPago(), getLoggedUser(), false);
                     logger.info("Abono principal actualizado correctamente");
                  } else {
                     logger.error("Ocurrió un error al generar el folio");
                     showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al generar el folio para el abono principal");
                  }
               } else {
                  logger.error("Ocurrió un error al generar el folio");
                  showMessage(FacesMessage.SEVERITY_INFO, "Error", "Ocurrió un error al generar el folio para el abono principal");
               }
            }

            // se actualiza la cantidad pagada del detalle espacio
            DetallesPagoEspacios detalle = getSelectEspaciosTour().getDetallesPagoEspacios();
            detalle.setCantidadPagada(detalle.getCantidadPagada() + getCantidadPagada());
            getToursController().addDetallesPagoEspacios(detalle, getLoggedUser(), false);
            // se valida si ya fue liquidado el espacio
            double cantidadPa = detalle.getCantidadPagada();
            double costoTotal = detalle.getPagoTotal();
            double tolerancia = 0.001;
            if (Math.abs(cantidadPa - costoTotal) < tolerancia) {
               detalle.setEstatus(GenericEnumType.ENABLED);
               setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
            }
            getToursController().addDetallesPagoEspacios(detalle, getLoggedUser(), false);
            logger.error("Cantidad pagada del espacio actualizada");

            // se actualizan los datos del pago principal solo para la impresión del ticket
            getSelectPago().setCantidadPagada(getCantidadPagada());
            getSelectPago().setFolio(pagoDetalle.getFolio());
            getSelectPago().setCobro(getLoggedUser());

            // se actualizan datos vista
            fillPagos();
            cargarEspacios();
            setPagoExitoso(true);
            reimprimirTicket();
            showMessage(FacesMessage.SEVERITY_INFO, "Pago realizado", "Pago realizado correctamente");
            logger.info("Pago realizado correctamente");
         } else {
            setPagoExitoso(false);
            setNoViajes(0);
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "Solo puede pagar el pago consecutivo");
         }

      } catch (Exception e) {
         logger.error("pagarIncompleto(): " + e);
      }
   }

   public void reimprimirTicket() {
      setPagoExitoso(true);
      try {
         List<Tours> no = getToursController().getlistToursByCliente(getSelectPago().getDetallesPagoEspacios().getCliente().getId());
         setNoViajes(no.size());
         if (getSelectPago().getFechaPago() == null) {
            List<PagosEspaciosTourDetalle> lisd = getToursController().fillPagosEspaciosTourDetalleByPagosEspacioTour(getSelectPago());
            getSelectPago().setFechaPago(lisd.get(lisd.size() - 1).getCreatedOn());
            getSelectPago().setMetodoPago(lisd.get(lisd.size() - 1).getMetodoPago());
         }
      } catch (Exception e) {
         setNoViajes(0);
         logger.error("Error al definir el logo para el tiked  " + e);
      }
   }

   public void cargarPago() {
      if (getSelectPago().getPagoEstatus().equals(GenericEnumType.INCOMPLETE)) {
         setCantidadPagada(getSelectPago().getDiferenciaPago());
      } else {
         setCantidadPagada(getSelectPago().getPago());
      }
   }

   public void cargarDetallesPago() {
      setToursController(new ToursController());
      setListDetallesPago(getToursController().fillPagosEspaciosTourDetalleByPagosEspacioTour(getSelectPago()));
   }

   public void cargarClientes() {
      try {
         setAgregarCampos(true);
         setSelectPersonaCliente(getSelectEspaciosTour().getCliente() != null ? getSelectEspaciosTour().getCliente().getId() : null);
         setSelectPersonaAconpanate(getSelectEspaciosTour().getAcompanante() != null ? getSelectEspaciosTour().getAcompanante().getId() : null);
         setObservacionEspacio(getSelectEspaciosTour().getObservacion());
      } catch (Exception e) {
         logger.error("Ocurrió un error al cargar los clientes del espacio");
      }
   }

   public void actualizarEspacio() {
      try {
         if (getSelectEspaciosTour().getEstatusEspacio().equals(GenericEnumType.ENABLED) || getSelectEspaciosTour().getEstatusEspacio().equals(GenericEnumType.INCOMPLETE)) {
            setToursController(new ToursController());

            if (getSelectPersonaCliente().isEmpty()) {
               getSelectEspaciosTour().setCliente(null);
            } else {
               getSelectEspaciosTour().setCliente(new Person(getSelectPersonaCliente()));
            }

            if (getSelectPersonaAconpanate().isEmpty()) {
               getSelectEspaciosTour().setAcompanante(null);
            } else {
               getSelectEspaciosTour().setAcompanante(new Person(getSelectPersonaAconpanate()));
            }

            getSelectEspaciosTour().setObservacion(getObservacionEspacio());

            getToursController().addEspaciosTour(getSelectEspaciosTour(), getLoggedUser(), false);
            cargarEspacios();
            showMessage(FacesMessage.SEVERITY_INFO, "Espacio actualizado", "Los datos del espacio se actuaron correctamente");
            logger.info("Los datos del espacio se actuaron correctamente");
         } else {
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede asignar un espacio que no ha sido vendido");
            logger.info("No se puede asignar un espacio que no ha sido vendido");
         }
      } catch (Exception e) {
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al asignar los clientes al espacio");
         logger.info("Ocurrió un error al asignar los clientes al espacio");
      }
   }

   public void addPagoMultiple() {
      logger.info("Generando abono múltiple");
      try {
         setPagoMultipleExitoso(false);
         // se valida el metodo de pago para cargarlo en los abonos
         MetodoPago metodoP;
         switch (getMetodoPago()) {
            case "TARJETA_DEBITO":
               metodoP = MetodoPago.TARJETA_DEBITO;
               break;
            case "TARJETA_CREDITO":
               metodoP = MetodoPago.TARJETA_CREDITO;
               break;
            case "DEPOSITO_TRANSFERENCIA":
               metodoP = MetodoPago.DEPOSITO_TRANSFERENCIA;
               break;
            case "EFECTIVO":
               metodoP = MetodoPago.EFECTIVO;
               break;
            case "TRES_MESES_SIN_INTERESES":
               metodoP = MetodoPago.TRES_MESES_SIN_INTERESES;
               break;
            case "SEIS_MESES_SIN_INTERESES":
               metodoP = MetodoPago.SEIS_MESES_SIN_INTERESES;
               break;
            case "NUEVE_MESES_SIN_INTERESES":
               metodoP = MetodoPago.NUEVE_MESES_SIN_INTERESES;
               break;
            case "DOCE_MESES_SIN_INTERESES":
               metodoP = MetodoPago.DOCE_MESES_SIN_INTERESES;
               break;
            case "CLIP":
               metodoP = MetodoPago.CLIP;
               break;
            case "OTRO":
               metodoP = MetodoPago.OTRO;
               break;
            default:
               metodoP = MetodoPago.OTRO;
         }
         // Guardará la lista de folios creados por el abono múltiple
         List<String> folios = new ArrayList<>();
         // Se carga el total del abono multiple
         Double totalAbonoMultiple = getPagoMultiple();
         // Guardara los detalles del abono multiple
         String detallesAbonoMultiple = "";
         // Formatos de fecha y numerico
         SimpleDateFormat formatoF = new SimpleDateFormat("dd-MM-yyyy");
         DecimalFormat formatoDecimal = new DecimalFormat("#.##");
         // (1) validación de cortes del día
         if (getGenericController().existClosingDayByCreatedOn(new Date(), getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Error", "No se puede hacer ninguna acción si existe un corte del día");
            return;
         }
         if (getGenericController().existStableGeneralBoxByCurdate(getLoggedUser())) {
            showMessage(FacesMessage.SEVERITY_WARN, "Día cerrado", "No se pueden eliminar más registros porque ya existe un cuadre de caja chica del día");
            return;
         }

         // (2) cargado de lista de abonos no pagados e incompletos de la venta
         DetallesPagoEspacios detalleEspacio = getSelectEspaciosTour().getDetallesPagoEspacios();
         List<PagosEspacioTour> abonos = getToursController().fillPagosEspacioTourNoPagafosByDetalle(getSelectEspaciosTour().getDetallesPagoEspacios());
         if (abonos.isEmpty()) {
            showMessage(FacesMessage.SEVERITY_WARN, "Error ", "No se encontraron pagos disponibles");
            return;
         }
         // (3) Validación de que el abono múltiple no sea mayor al adeudo restante
         Double totalRestante = redondearA2(detalleEspacio.getPagoTotal() - detalleEspacio.getCantidadPagada());
         if (totalAbonoMultiple > totalRestante) {
            showMessage(FacesMessage.SEVERITY_WARN, "Advertencia", "El abono múltiple no puede ser mayor que el adeudo restante de $" + totalRestante);
            return;
         }
         // (4) Se crea un registro del abono múltiple
         HistorialAbonoMultipleTours abonoMultiple = new HistorialAbonoMultipleTours();
         abonoMultiple.setDetallesPagoEspacios(detalleEspacio);
         abonoMultiple.setMetodoPago(metodoP);
         abonoMultiple.setLocation(getLoggedUser().getLocation());
         abonoMultiple.setTotalPago(totalAbonoMultiple);
         abonoMultiple.setEstatus(EstatusPagoMultiple.PAGADO);
         abonoMultiple.setFechaPago(new Date());
         abonoMultiple.setCliente(detalleEspacio.getCliente());
         getToursController().addHitorialAbonoMultipleTours(abonoMultiple, getLoggedUser(), true);
         // (5) Se recorre la lista de bonos no pagados
         for (PagosEspacioTour pa : abonos) {
            // (5.1) Se actualiza el abono principal
            pa.setAbonoMultiple(abonoMultiple);
            // (5.2) Se verifica si el abono es incompleto
            if (pa.getPagoEstatus().equals(GenericEnumType.INCOMPLETE)) {
               // (5.2.1) Se valida si la diferencia del abono es mayor que el restante del abono multiple
               if (pa.getDiferenciaPago() > totalAbonoMultiple) {
                  // (5.2.1.1) Se actualiza el abono con el restante del abono multiple
                  pa.setCantidadPagada(pa.getCantidadPagada() + totalAbonoMultiple);
                  pa.setDiferenciaPago(pa.getPago() - pa.getCantidadPagada());
                  getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
                  // (5.2.1.2) Se crea el detalle del abono 
                  String f = obtenerFolio("T"); // Se genera el folio
                  folios.add(f); // Se carga el folio en la lista de folios para la impresión del tiked
                  PagosEspaciosTourDetalle detalle = new PagosEspaciosTourDetalle();
                  detalle.setMetodoPago(metodoP);
                  detalle.setPagosEspacioTour(pa);
                  detalle.setPago(totalAbonoMultiple);
                  detalle.setEstatusActivo(GenericEnumType.ENABLED);
                  detalle.setPagoEstatus(GenericEnumType.ENABLED);
                  detalle.setFolio(f);
                  detalle.setAbonoMultiple(abonoMultiple);
                  getToursController().addPagosEspaciosTourDetalle(detalle, getLoggedUser(), true);
                  // (5.2.1.3) Se actualiza el detalle del abono múltiple
                  String fechaApagarFormateada = formatoF.format(pa.getFechaAPagar());
                  String catidadPagadaFormateada = formatoDecimal.format(pa.getPago());
                  detallesAbonoMultiple = detallesAbonoMultiple + " | " + f + ", " + fechaApagarFormateada + ", " + catidadPagadaFormateada;
                  // (5.2.1.4) Se actualiza el pago múltiple a 0 y se detiene el ciclo
                  totalAbonoMultiple = 0D;
                  break;
               } else { // (5.2.2) En caso de que el abono multipleser sea mayor que el la diferencia del abono
                  // (5.2.2.1) Se guarda la diferencia del abono para crear el del abono principal
                  Double diferencia = pa.getDiferenciaPago();
                  // (5.2.2.2) Se crea el detalle del abono 
                  String f1 = obtenerFolio("T"); // Se genera el folio 1
                  folios.add(f1); // Se carga el folio en la lista de folios para la impresión del tiked
                  PagosEspaciosTourDetalle detalle = new PagosEspaciosTourDetalle();
                  detalle.setFolio(f1);
                  detalle.setMetodoPago(metodoP);
                  detalle.setPagosEspacioTour(pa);
                  detalle.setPago(diferencia);
                  detalle.setEstatusActivo(GenericEnumType.ENABLED);
                  detalle.setPagoEstatus(GenericEnumType.ENABLED);
                  detalle.setAbonoMultiple(abonoMultiple);
                  getToursController().addPagosEspaciosTourDetalle(detalle, getLoggedUser(), true);
                  // (5.2.2.3)  Se actualiza el detalle del abono múltiple
                  String fechaApagarFormateada = formatoF.format(pa.getFechaAPagar());
                  String catidadPagadaFormateada = formatoDecimal.format(pa.getPago());
                  detallesAbonoMultiple = detallesAbonoMultiple + " | " + f1 + ", " + fechaApagarFormateada + ", " + catidadPagadaFormateada;
                  // (5.2.2.4) Se actualiza el abono principal ha pagado 
                  String f2 = obtenerFolio("T"); // Se genera el folio 2
                  folios.add(f2); // Se carga el folio en la lista de folios para la impresión del tiked
                  pa.setFolio(f2);
                  pa.setDiferenciaPago(0D);
                  pa.setCantidadPagada(pa.getPago());
                  pa.setCobro(getLoggedUser());
                  pa.setPagoEstatus(GenericEnumType.ENABLED);
                  getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
                  // (5.2.2.5) Se actualiza el detalle del abono múltiple
                  String catidadPagadaFormateada2 = formatoDecimal.format(pa.getPago());
                  detallesAbonoMultiple = detallesAbonoMultiple + " | " + f2 + ", " + fechaApagarFormateada + ", " + catidadPagadaFormateada2;
                  // (5.2.2.6) Se actualiza el pago múltiple
                  totalAbonoMultiple = totalAbonoMultiple - diferencia;
               }
            } else if (pa.getPago() < totalAbonoMultiple) { // (5.3) Se verifica si el abono múltiple cubre el abono principal
               // (5.3.1) Se actualiza el abono principal ha pagado 
               String f = obtenerFolio("T"); // Se genera el folio 2
               folios.add(f); // Se carga el folio en la lista de folios para la impresión del tiked
               pa.setFolio(f);
               pa.setDiferenciaPago(0D);
               pa.setCantidadPagada(pa.getPago());
               pa.setMetodoPago(metodoP);
               pa.setCobro(getLoggedUser());
               pa.setFechaPago(correcciónDeHr(new Date(), -7));
               pa.setPagoEstatus(GenericEnumType.ENABLED);
               getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
               // (5.3.2) Se actualiza el pago múltiple
               totalAbonoMultiple = totalAbonoMultiple - pa.getPago();
               // (5.3.3) Se actualiza el detalle del abono múltiple
               String fechaApagarFormateada = formatoF.format(pa.getFechaAPagar());
               String catidadPagadaFormateada = formatoDecimal.format(pa.getPago());
               detallesAbonoMultiple = detallesAbonoMultiple + " | " + f + ", " + fechaApagarFormateada + ", " + catidadPagadaFormateada;
            } else if (totalAbonoMultiple > 0) { // (5.4) Entra si el abono no era incompleto y el abono múltiple no cubrió el abono principal
               // (5.4.1) Se actualiza el abono principal y define el folio
               String f = obtenerFolio("T"); // Se genera el folio
               folios.add(f); // Se carga el folio en la lista de folios para la impresión del tiked
               pa.setCantidadPagada(redondearA2(totalAbonoMultiple));
               pa.setDiferenciaPago(pa.getPago() - pa.getCantidadPagada());
               // (5.4.2) Se valida si el abono múltiple cubrió exactamente el abono principal
               if (pa.getDiferenciaPago() == 0) {
                  pa.setFolio(f);
                  pa.setMetodoPago(metodoP);
                  pa.setPagoEstatus(GenericEnumType.ENABLED);
                  pa.setFechaPago(correcciónDeHr(new Date(), -7));
                  pa.setCobro(getLoggedUser());
               } else { // (5.4.3) Entre si el abono múltiple no cubrió el abono principal
                  // (5.4.3.1) Se define le abono principal como incompleto 
                  pa.setPagoEstatus(GenericEnumType.INCOMPLETE);
                  // (5.4.3.2) Se crea el detalle del abono con el restante del abono múltiple 
                  PagosEspaciosTourDetalle detalle = new PagosEspaciosTourDetalle();
                  detalle.setFolio(f);
                  detalle.setPagosEspacioTour(pa);
                  detalle.setMetodoPago(metodoP);
                  detalle.setPago(totalAbonoMultiple);
                  detalle.setEstatusActivo(GenericEnumType.ENABLED);
                  detalle.setPagoEstatus(GenericEnumType.ENABLED);
                  detalle.setAbonoMultiple(abonoMultiple);
                  getToursController().addPagosEspaciosTourDetalle(detalle, getLoggedUser(), true);
               }
               // (5.4.3) Se actualiza el abono principal 
               getToursController().addPagosEspacioTour(pa, getLoggedUser(), false);
               // Se actualiza el detalle del abono múltiple
               String fechaApagarFormateada = formatoF.format(pa.getFechaAPagar());
               String catidadPagadaFormateada = formatoDecimal.format(pa.getPago());
               detallesAbonoMultiple = detallesAbonoMultiple + " | " + f + ", " + fechaApagarFormateada + ", " + catidadPagadaFormateada;
               // Se actualiza el pago múltiple a 0 y se detiene el ciclo
               totalAbonoMultiple = 0D;
               break;
            }
         }
         // (6) Se convierte la lista de folios a un string
         StringBuilder folioS = new StringBuilder();
         for (String folio : folios) {
            folioS.append(folio).append(", ");
         }
         if (folioS.length() > 0) {
            folioS.setLength(folioS.length() - 2);
         }
         // (7) actulizar abono multiple
         abonoMultiple.setFolios(folioS.toString());
         abonoMultiple.setDetalle(detallesAbonoMultiple);
         getToursController().addHitorialAbonoMultipleTours(abonoMultiple, getLoggedUser(), false);
         // (8) Se actuliza la venta
         detalleEspacio.setCantidadPagada(detalleEspacio.getCantidadPagada() + getPagoMultiple());
         // (9) Se valida si la venta ya fue liquidada
         double cantidadPa = detalleEspacio.getCantidadPagada();
         double costoTotal = detalleEspacio.getPagoTotal();
         double tolerancia = 0.001;
         if (Math.abs(cantidadPa - costoTotal) < tolerancia) {
            detalleEspacio.setEstatus(GenericEnumType.ENABLED);
            setListTours(getToursController().fillToursByLocation(getLoggedUser().getLocation()));
         }
         getToursController().addDetallesPagoEspacios(detalleEspacio, getLoggedUser(), false);
         // (10) Se carga el abono múltiple generado en para el tiket
         setSelectAbonoMultiple(abonoMultiple);
         // (11) Se actualiza la lista de pagos y ventas
         fillPagos();
         cargarEspacios();
         setPagoMultipleExitoso(true);
         //reimprimirTicket();
         logger.info("El pago multiple se realizó correctamente");
         showMessage(FacesMessage.SEVERITY_INFO, "Pago multiple realizado", "El pago multiple se realizó correctamente");
      } catch (Exception e) {
         setPagoMultipleExitoso(false);
         showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al realizar el abono múltiple");
         logger.error("Ocurrió un error al realizar el abono múltiple");
      }
   }

   public void addProvider() {
      logger.info("addProvider");
      try {
         getProvider().setId(UUID.randomUUID().toString());
         getProvider().setActive(true);
         getProvider().setRoot(false);
         getProvider().setCreatedOn(new Date());
         getProvider().setLocation(new Location(getLoggedUser().getLocation().getId()));
         getProvider().setClienteSpa(false);
         boolean success = getToursController().addPerson(getProvider(), getLoggedUser().getUserName(), true);
         if (success) {
            setPersonas(getControladorPreventa().findListOfPersonsByTypeAndTipoCliente(PersonType.CUSTOMER, getLoggedUser().getLocation(), getLoggedUser().getPerson().getClienteSpa()));
            showMessage(FacesMessage.SEVERITY_INFO, "Cliente agregado", "Cliente agregado correctamente");
         } else {
            logger.error("Provider " + getProvider() + " was not saved");
            showMessage(FacesMessage.SEVERITY_ERROR, "Error", "Ocurrió un error al crear el usuario");
         }
      } catch (Exception e) {
         logger.error("addProvider: " + e);
         showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Ocurrió un error al crear el usuario");
      }
      cleanProvider();
   }

   public void cleanProvider() {
      Calendar calendar = Calendar.getInstance();
      calendar.add(Calendar.YEAR, -15);
      setSixteenYearsOld(calendar.getTime());
      setProvider(new Person(PersonType.CUSTOMER));
   }

   public void generarReposteAbonos() throws IOException {
      //Crear reporte PDF

      List<EspaciosTourView> EspaciosTour = getToursController().fillEspaciosTourView(getSelectTours().getId());

      String filePathImagenEmpresa = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/jasper-reports/logo2.jpg");
      java.awt.Image imagenEmpresa = ImageIO.read(new File(filePathImagenEmpresa));

      Map<String, Object> parameters = new HashMap<>();
      JRBeanCollectionDataSource abonosLote = new JRBeanCollectionDataSource(EspaciosTour);
      parameters.put("nombreEmpresa", "Abordo Tours");
      parameters.put("imagenEmpresa", imagenEmpresa);
      parameters.put("espaciosTour", abonosLote);
      parameters.put("fecha", new Date());
      parameters.put("nombreTour", getSelectTours().getDescripcion());
      parameters.put("tipoTour", getSelectTours().getTipo().getTransfer());
      parameters.put("fechaInicio", getSelectTours().getFechaInicio());
      parameters.put("fechaFin", getSelectTours().getFechaFin());
      parameters.put("comentarios", getSelectTours().getComentario());
      parameters.put("espacios", getSelectTours().getNumeroEspacios().toString());
      parameters.put("reservados", getSelectTours().getNumeroReservados().toString());
      parameters.put("costoEspacio", getSelectTours().getCostoIndividual().toString());
      parameters.put("costoTotalTour", getSelectTours().getCostoTotal().toString());

      try {
         String filePath = FacesContext.getCurrentInstance().getExternalContext().getRealPath("/resources/jasper-reports/reporteEspaciosTour.jrxml");
         File file = new File(filePath);
         JasperDesign jasperDesign = JRXmlLoader.load(file);
         JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
         JasperPrint printt = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
         JasperExportManager.exportReportToPdfFile(printt, "espaciosTour.pdf");
         setPrint(printt);
      } catch (JRException e) {
         logger.error("Error al generar el reporte de compra : " + e);
         showMessage(FacesMessage.SEVERITY_FATAL, "Error", "Error al generar el reporte de compra.");
      }
   }

   public void descargarArchivo() {
      try {
         HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
         response.addHeader("content-disposition", "attachment;filename = archivo.pdf");
         ServletOutputStream stream = response.getOutputStream();
         JasperExportManager.exportReportToPdfStream(getPrint(), stream);
         FacesContext.getCurrentInstance().responseComplete();
      } catch (IOException | JRException e) {
         logger.error("Error al descargar el reporte de compra : " + e);
      }
   }

   private double redondearA2(double cantidad) {
      logger.info("Redondeando cantidad a 2 decimales");
      try {
         BigDecimal bd = new BigDecimal(cantidad);
         bd = bd.setScale(2, RoundingMode.HALF_UP);
         double pagoRedondeado = bd.doubleValue();
         return pagoRedondeado;
      } catch (Exception e) {
         logger.error("Ocurrió un error al redondear la cantidad" + e);
         return cantidad;
      }
   }

   private Date correcciónDeHr(Date fecha, int horas) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(fecha);
      calendar.add(Calendar.HOUR_OF_DAY, horas);
      Date nuevaFecha = calendar.getTime();
      return nuevaFecha;
   }

   public Boolean getPagoMultipleExitoso() {
      return PagoMultipleExitoso;
   }

   public void setPagoMultipleExitoso(Boolean PagoMultipleExitoso) {
      this.PagoMultipleExitoso = PagoMultipleExitoso;
   }

   public ToursController getToursController() {
      return toursController;
   }

   public void setToursController(ToursController toursController) {
      this.toursController = toursController;
   }

   public List<Tours> getListTours() {
      return listTours;
   }

   public void setListTours(List<Tours> listTours) {
      this.listTours = listTours;
   }

   public Tours getSelectTours() {
      return selectTours;
   }

   public void setSelectTours(Tours selectTours) {
      this.selectTours = selectTours;
   }

   public String getDescripcion() {
      return descripcion;
   }

   public void setDescripcion(String descripcion) {
      this.descripcion = descripcion;
   }

   public Date getFechaInicio() {
      return fechaInicio;
   }

   public void setFechaInicio(Date fechaInicio) {
      this.fechaInicio = fechaInicio;
   }

   public Date getFechaFin() {
      return fechaFin;
   }

   public void setFechaFin(Date fechaFin) {
      this.fechaFin = fechaFin;
   }

   public int getNumeroEspacios() {
      return numeroEspacios;
   }

   public void setNumeroEspacios(int numeroEspacios) {
      this.numeroEspacios = numeroEspacios;
   }

   public String getComentario() {
      return comentario;
   }

   public void setComentario(String comentario) {
      this.comentario = comentario;
   }

   public Double getCostoIndividual() {
      return costoIndividual;
   }

   public void setCostoIndividual(Double costoIndividual) {
      this.costoIndividual = costoIndividual;
   }

   public List<EspaciosTour> getListEspaciosTour() {
      return listEspaciosTour;
   }

   public void setListEspaciosTour(List<EspaciosTour> listEspaciosTour) {
      this.listEspaciosTour = listEspaciosTour;
   }

   public EspaciosTour getSelectEspaciosTour() {
      return selectEspaciosTour;
   }

   public void setSelectEspaciosTour(EspaciosTour selectEspaciosTour) {
      this.selectEspaciosTour = selectEspaciosTour;
   }

   public PreventaController getControladorPreventa() {
      return controladorPreventa;
   }

   public void setControladorPreventa(PreventaController controladorPreventa) {
      this.controladorPreventa = controladorPreventa;
   }

   public List<PersonListView> getPersonas() {
      return personas;
   }

   public void setPersonas(List<PersonListView> personas) {
      this.personas = personas;
   }

   public String getSelectPersona() {
      return selectPersona;
   }

   public void setSelectPersona(String selectPersona) {
      this.selectPersona = selectPersona;
   }

   public String getPeriodoPago() {
      return periodoPago;
   }

   public void setPeriodoPago(String periodoPago) {
      this.periodoPago = periodoPago;
   }

   public Date getFechaAPagar() {
      return fechaAPagar;
   }

   public void setFechaAPagar(Date fechaAPagar) {
      this.fechaAPagar = fechaAPagar;
   }

   public Integer getNumeroPagos() {
      return numeroPagos;
   }

   public void setNumeroPagos(Integer numeroPagos) {
      this.numeroPagos = numeroPagos;
   }

   public Double getAnticipo() {
      return anticipo;
   }

   public void setAnticipo(Double anticipo) {
      this.anticipo = anticipo;
   }

   public List<PagosEspacioTour> getListPagosEspacioTour() {
      return listPagosEspacioTour;
   }

   public void setListPagosEspacioTour(List<PagosEspacioTour> listPagosEspacioTour) {
      this.listPagosEspacioTour = listPagosEspacioTour;
   }

   public PagosEspacioTour getSelectPago() {
      return selectPago;
   }

   public void setSelectPago(PagosEspacioTour selectPago) {
      this.selectPago = selectPago;
   }

   public String getPagoSiguiente() {
      return pagoSiguiente;
   }

   public void setPagoSiguiente(String pagoSiguiente) {
      this.pagoSiguiente = pagoSiguiente;
   }

   public GenericValidationController getGenericController() {
      return genericController;
   }

   public void setGenericController(GenericValidationController genericController) {
      this.genericController = genericController;
   }

   public Boolean getPagoExitoso() {
      return pagoExitoso;
   }

   public void setPagoExitoso(Boolean pagoExitoso) {
      this.pagoExitoso = pagoExitoso;
   }

   public String getImagen() {
      return imagen;
   }

   public void setImagen(String imagen) {
      this.imagen = imagen;
   }

   public Double getCantidadPagada() {
      return cantidadPagada;
   }

   public void setCantidadPagada(Double cantidadPagada) {
      this.cantidadPagada = cantidadPagada;
   }

   public List<PagosEspaciosTourDetalle> getListDetallesPago() {
      return listDetallesPago;
   }

   public void setListDetallesPago(List<PagosEspaciosTourDetalle> listDetallesPago) {
      this.listDetallesPago = listDetallesPago;
   }

   public PagosEspaciosTourDetalle getSelectDetallePago() {
      return selectDetallePago;
   }

   public void setSelectDetallePago(PagosEspaciosTourDetalle selectDetallePago) {
      this.selectDetallePago = selectDetallePago;
   }

   public List<DetallesPagoEspacios> getListDetallesPagoEspacios() {
      return listDetallesPagoEspacios;
   }

   public void setListDetallesPagoEspacios(List<DetallesPagoEspacios> listDetallesPagoEspacios) {
      this.listDetallesPagoEspacios = listDetallesPagoEspacios;
   }

   public DetallesPagoEspacios getSelectDetallesPagoEspacios() {
      return selectDetallesPagoEspacios;
   }

   public void setSelectDetallesPagoEspacios(DetallesPagoEspacios selectDetallesPagoEspacios) {
      this.selectDetallesPagoEspacios = selectDetallesPagoEspacios;
   }

   public Boolean getNinos() {
      return ninos;
   }

   public void setNinos(Boolean ninos) {
      this.ninos = ninos;
   }

   public int getNumeroNinos() {
      return numeroNinos;
   }

   public void setNumeroNinos(int numeroNinos) {
      this.numeroNinos = numeroNinos;
   }

   public Double getCostoPorNino() {
      return costoPorNino;
   }

   public void setCostoPorNino(Double costoPorNino) {
      this.costoPorNino = costoPorNino;
   }

   public Double getTotalPago() {
      return totalPago;
   }

   public void setTotalPago(Double totalPago) {
      this.totalPago = totalPago;
   }

   public DualListModel<EspaciosTour> getDualEspaciosTourLst() {
      return dualEspaciosTourLst;
   }

   public void setDualEspaciosTourLst(DualListModel<EspaciosTour> dualEspaciosTourLst) {
      this.dualEspaciosTourLst = dualEspaciosTourLst;
   }

   public Double getCostoPorEspacio() {
      return costoPorEspacio;
   }

   public void setCostoPorEspacio(Double costoPorEspacio) {
      this.costoPorEspacio = costoPorEspacio;
   }

   public String getSelectPersonaCliente() {
      return selectPersonaCliente;
   }

   public void setSelectPersonaCliente(String selectPersonaCliente) {
      this.selectPersonaCliente = selectPersonaCliente;
   }

   public String getSelectPersonaAconpanate() {
      return selectPersonaAconpanate;
   }

   public void setSelectPersonaAconpanate(String selectPersonaAconpanate) {
      this.selectPersonaAconpanate = selectPersonaAconpanate;
   }

   public Boolean getAdutosMayores() {
      return adutosMayores;
   }

   public void setAdutosMayores(Boolean adutosMayores) {
      this.adutosMayores = adutosMayores;
   }

   public int getNumeroAdultosMayores() {
      return numeroAdultosMayores;
   }

   public void setNumeroAdultosMayores(int numeroAdultosMayores) {
      this.numeroAdultosMayores = numeroAdultosMayores;
   }

   public Double getCostoPorAdultoMayor() {
      return costoPorAdultoMayor;
   }

   public void setCostoPorAdultoMayor(Double costoPorAdultoMayor) {
      this.costoPorAdultoMayor = costoPorAdultoMayor;
   }

   public Boolean getPagaAlAbordar() {
      return PagaAlAbordar;
   }

   public void setPagaAlAbordar(Boolean PagaAlAbordar) {
      this.PagaAlAbordar = PagaAlAbordar;
   }

   public List<Tours> getListTourCliente() {
      return listTourCliente;
   }

   public void setListTourCliente(List<Tours> listTourCliente) {
      this.listTourCliente = listTourCliente;
   }

   public String getComentariosVenta() {
      return comentariosVenta;
   }

   public void setComentariosVenta(String comentariosVenta) {
      this.comentariosVenta = comentariosVenta;
   }

   public String getObservacionEspacio() {
      return observacionEspacio;
   }

   public void setObservacionEspacio(String observacionEspacio) {
      this.observacionEspacio = observacionEspacio;
   }

   public String getSelectTipotour() {
      return selectTipotour;
   }

   public void setSelectTipotour(String selectTipotour) {
      this.selectTipotour = selectTipotour;
   }

   public String getSelectTipoAbitacion() {
      return selectTipoAbitacion;
   }

   public void setSelectTipoAbitacion(String selectTipoAbitacion) {
      this.selectTipoAbitacion = selectTipoAbitacion;
   }

   public String getMetodoPago() {
      return metodoPago;
   }

   public void setMetodoPago(String metodoPago) {
      this.metodoPago = metodoPago;
   }

   public Integer getNoViajes() {
      return noViajes;
   }

   public void setNoViajes(Integer noViajes) {
      this.noViajes = noViajes;
   }

   public Double getPagoMultiple() {
      return pagoMultiple;
   }

   public void setPagoMultiple(Double pagoMultiple) {
      this.pagoMultiple = pagoMultiple;
   }

   public HistorialAbonoMultipleTours getSelectAbonoMultiple() {
      return selectAbonoMultiple;
   }

   public void setSelectAbonoMultiple(HistorialAbonoMultipleTours selectAbonoMultiple) {
      this.selectAbonoMultiple = selectAbonoMultiple;
   }

   public GenericPersonController getPersonController() {
      return personController;
   }

   public void setPersonController(GenericPersonController personController) {
      this.personController = personController;
   }

   public Person getProvider() {
      return provider;
   }

   public void setProvider(Person provider) {
      this.provider = provider;
   }

   public Date getSixteenYearsOld() {
      return sixteenYearsOld;
   }

   public void setSixteenYearsOld(Date sixteenYearsOld) {
      this.sixteenYearsOld = sixteenYearsOld;
   }

   public JasperPrint getPrint() {
      return print;
   }

   public void setPrint(JasperPrint print) {
      this.print = print;
   }

   public DualListModel<EspaciosTour> getDualEspaciosTourLstEditar() {
      return dualEspaciosTourLstEditar;
   }

   public void setDualEspaciosTourLstEditar(DualListModel<EspaciosTour> dualEspaciosTourLstEditar) {
      this.dualEspaciosTourLstEditar = dualEspaciosTourLstEditar;
   }

   public Boolean getAgregarCampos() {
      return agregarCampos;
   }

   public void setAgregarCampos(Boolean agregarCampos) {
      this.agregarCampos = agregarCampos;
   }

   public Double getTotalPagoEstatico() {
      return totalPagoEstatico;
   }

   public void setTotalPagoEstatico(Double totalPagoEstatico) {
      this.totalPagoEstatico = totalPagoEstatico;
   }

}
