/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.arrebol.taxiservicios.controller.admin;

import com.arrebol.taxiservicios.controller.connection.ConnectionManager;
import com.arrebol.taxiservicios.controller.util.DateWrapper;
import com.arrebol.taxiservicios.controller.util.HibernateUtil;
import com.arrebol.taxiservicios.model.admin.Expense;
import com.arrebol.taxiservicios.model.admin.Sale;
import com.arrebol.taxiservicios.model.admin.Sanction;
import com.arrebol.taxiservicios.model.admin.Taxi;
import com.arrebol.taxiservicios.model.catalog.BankCard;
import com.arrebol.taxiservicios.model.catalog.Color;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.catalog.Vehicle;
import com.arrebol.taxiservicios.model.core.Person;
import com.arrebol.taxiservicios.model.core.User;
import com.arrebol.taxiservicios.model.view.dashboard.SaleDashboardView;
import com.arrebol.taxiservicios.model.dashboard.ToCollect;
import com.arrebol.taxiservicios.model.enums.ExpenseStatusType;
import com.arrebol.taxiservicios.model.enums.ExpenseType;
import com.arrebol.taxiservicios.model.enums.PersonType;
import com.arrebol.taxiservicios.model.enums.SanctionType;
import com.arrebol.taxiservicios.model.enums.ServiceOrderType;
import com.arrebol.taxiservicios.model.enums.TransferType;
import com.arrebol.taxiservicios.model.stocktaking.ServiceOrder;
import com.sun.istack.Nullable;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;

/**
 *
 * @author oscarvargas
 */
public class SaleController extends ConnectionManager implements Serializable {

    public String getTaxiByDriver(String driverId) {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching taxi by Driver..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Taxi> criteria = builder.createQuery(Taxi.class);
            Root<Taxi> root = criteria.from(Taxi.class);

            Path<String> idPath = root.get("id");

            criteria.select(
                    builder.construct(Taxi.class, idPath
                    ));

            criteria.where(builder.equal(root.get("driver"), new Person(driverId)));

            result = session.createQuery(criteria).uniqueResult().getId();
            transaction.commit();
            logger.info("Taxi found");
        } catch (HibernateException e) {
            logger.error("Can not load the taxi by driver ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTaxiByDriver of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public Taxi getTaxiObject(String driverId) {
        logger.info("getTaxiObject");
        String idTaxi = getTaxiByDriver(driverId);
        Taxi taxi = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            taxi = session.find(Taxi.class, idTaxi);
            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Sale can not be added ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method addSale() ", e);
            rollback(transaction);
        }
        return taxi;
    }

    /**
     * Save a sale in the system.
     *
     * @param sale Sale to be saved
     * @param userId User to load and use to set createBy user in the record.
     * @param driverId Id for driver.
     * @param currentKM
     *
     * @return True if sale was saved otherwise false.
     */
    public boolean addSale(Sale sale, String userId, String driverId, Float currentKM) {
        logger.info("addSale");
        String idTaxi = getTaxiByDriver(driverId);
        boolean success = false;

        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            Taxi taxi = session.find(Taxi.class, idTaxi);

            if (null != taxi) {
                sale.setPayDayAsSpected(taxi.getPayDay());
                sale.setPaySavingAsSpected(taxi.getPaySaving());
                sale.setAccidentAsSpected(taxi.getPayAccident());
                sale.setTotalToPay(taxi.getPayDay() + taxi.getPaySaving() + taxi.getPayAccident());
            } else {
                if (null != transaction) {
                    transaction.rollback();
                }
                return false;
            }

            sale.setCreatedBy(new User(userId, ""));
            sale.setPerson(new Person(driverId));
            sale.setTaxi(new Taxi(idTaxi));
            session.save(sale);

            if (null != currentKM && 0 < currentKM.intValue()) {
                if (null != taxi) {
                    taxi.setKm(currentKM);
                    taxi.setLastUpdatedOn(new Date());
                    session.update(taxi);
                }
            }

            transaction.commit();
            logger.info("Sale saved " + sale.getId());
            success = true;
        } catch (HibernateException e) {
            logger.error("Sale can not be added ", e);
            if (null != transaction) {
                transaction.rollback();
            }
        } catch (Exception e) {
            logger.error("Method addSale() ", e);
            if (null != transaction) {
                transaction.rollback();
            }
        }
        return success;
    }
    
    public boolean updateTicketStatus(String idSale, String userId) {
        logger.info("updating payment ticket");
        boolean success = false;
        Transaction transaction = null;
         try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<Sale> update = builder.createCriteriaUpdate(Sale.class);
            Root<Sale> rootUpdate = update.from(Sale.class);

            update.set("active", true);
            update.set("lastUpdatedBy", new User(userId, true));
            update.set("lastUpdatedOn", new Date());
            update.where(builder.equal(rootUpdate.get("id"), idSale));

            int total = session.createQuery(update).executeUpdate();
            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + idSale + " the status was updated by ticket printing");
            } else {
                transaction.rollback();
                logger.error("Error Sale updated total: " + total + " by ticket printing");
            }
            
        } catch (HibernateException e) {
            logger.error("Sale can not be updated ", e +" by ticket printing");
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method updateTicketStatus() ", e);
            rollback(transaction);
        }
        return success;
    }

    public List<Person> fillDriversComboBox() {

        Transaction transaction = null;
        List<Person> result = null;

        try {
            logger.info("Searching all drivers to select..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
            Root<Person> root = criteria.from(Person.class);

            Path<String> idPath = root.get("id");
            Path<String> firstNamePath = root.get("firstName");
            Path<String> secondNamePath = root.get("secondName");
            Path<String> lastNamePath = root.get("lastName");
            Path<String> middleNamePath = root.get("middleName");

            criteria.select(
                    builder.construct(Person.class, idPath, firstNamePath, secondNamePath, lastNamePath, middleNamePath
                    ));

            criteria.where(builder.equal(root.get("personType"), PersonType.DRIVER),
                    builder.and(
                            builder.equal(root.get("active"), Boolean.TRUE)
                    ));

            criteria.orderBy(
                    builder.asc(root.get("firstName")),
                    builder.asc(root.get("secondName")),
                    builder.asc(root.get("lastName")),
                    builder.asc(root.get("middleName"))
            );

            result = session.createQuery(criteria).getResultList();
            transaction.commit();
            logger.info("Total of drivers found for ComboBox: " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of drivers for ComboBox ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method fillDriversComboBox of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @return
     */
    public List<Person> findActiveDriversThatHaveTaxi() {

        Transaction transaction = null;
        List<Person> result = null;

        try {
            logger.info("Searching all active drivers that have Taxi assigned to select..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String hql = "SELECT person FROM Person person "
                    + "INNER JOIN Taxi taxi ON person.id = taxi.driver "
                    + "WHERE person.active = :active AND person.personType = :personType ";

            Query query = session.createQuery(hql, Person.class);

            query.setParameter("personType", PersonType.DRIVER);
            query.setParameter("active", true);

            result = query.getResultList();

            transaction.commit();
            logger.info("Total of active drivers that have Taxi assigned found for ComboBox: " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of active drivers that have Taxi assigned for ComboBox ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findActiveDriversThatHaveTaxi()", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Person> findAllDriversThatHaveTaxi() {

        Transaction transaction = null;
        List<Person> result = null;

        try {
            logger.info("Searching all active drivers that have Taxi assigned to select..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String hql = "SELECT person FROM Person person "
                    + "LEFT JOIN Taxi taxi ON person.id = taxi.driver "
                    + "WHERE person.personType = :personType ";

            Query query = session.createQuery(hql, Person.class);

            query.setParameter("personType", PersonType.DRIVER);

            result = query.getResultList();

            transaction.commit();
            logger.info("Total of active drivers that have Taxi assigned found for ComboBox: " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of active drivers that have Taxi assigned for ComboBox ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findActiveDriversThatHaveTaxi()", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Taxi> findActiveDriversThatHaveTaxiAndTaxisDescription() {

        Transaction transaction = null;
        List<Taxi> result = null;

        try {
            logger.info("Searching all active drivers that have Taxi assigned to select..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String hql = "SELECT taxi FROM Taxi taxi "
                    + "INNER JOIN Person person ON person.id = taxi.driver "
                    + "WHERE person.active = :active AND person.personType = :personType ";

            Query query = session.createQuery(hql, Taxi.class);

            query.setParameter("personType", PersonType.DRIVER);
            query.setParameter("active", true);

            result = query.getResultList();

            transaction.commit();
            logger.info("Total of active drivers that have Taxi assigned found for ComboBox: " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of active drivers that have Taxi assigned for ComboBox ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findActiveDriversThatHaveTaxi()", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Taxi> fillTaxisComboBox() {

        Transaction transaction = null;
        List<Taxi> result = null;

        try {
            logger.info("Searching all taxis to select..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Taxi> criteria = builder.createQuery(Taxi.class);
            Root<Taxi> root = criteria.from(Taxi.class);

            Path<String> id = root.get("id");
            Path<String> placa = root.get("placa");
            Path<String> year = root.get("year");
            Path<Vehicle> vehicle = root.get("vehicle");
            Path<Color> color = root.get("color");

            criteria.select(
                    builder.construct(Taxi.class, id,
                            placa, year, vehicle, color
                    ));

            criteria.where(builder.equal(root.get("active"), Boolean.TRUE));

            criteria.orderBy(
                    builder.asc(root.get("placa"))
            );
            criteria.orderBy(
                    builder.asc(root.get("year"))
            );

            result = session.createQuery(criteria).getResultList();
            transaction.commit();
            logger.info("Total of taxis found for ComboBox: " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of taxis for ComboBox ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method fillTaxisComboBox of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @return
     */
    public String getTotalSaleForMonth() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total sales for this month..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> payDayPath = root.get("payDay");

            criteria.select(
                    builder.construct(Sale.class, payDayPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).firstDateForMonth().midnigthTime().asDate());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).lastDateForMonth().midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate,
                            lastDate
                    )
            );

            Double totalPay = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalPay = totalPay + total.getPayDay();
            }
            result = totalPay.toString();

            transaction.commit();
            logger.info("Total of sales for this month: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales for this month ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalSaleForMonth of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public String getTotalSavingForMonth() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total saving for this month..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> payDayPath = root.get("paySaving");

            criteria.select(
                    builder.construct(Sale.class, payDayPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).firstDateForMonth().midnigthTime().asDate());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).lastDateForMonth().midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate,
                            lastDate
                    )
            );

            Double totalPay = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalPay = totalPay + total.getPaySaving();
            }
            result = totalPay.toString();

            transaction.commit();
            logger.info("Total of saving for this month: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total saving for this month ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalSavingForMonth of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public String getTotalAccidentForMonth() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total accident for this month..");
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> accidentPath = root.get("accident");

            criteria.select(
                    builder.construct(Sale.class, accidentPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).firstDateForMonth().midnigthTime().asDate());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("payDate"), DateWrapper.today().subtractHours(6).lastDateForMonth().midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate,
                            lastDate
                    )
            );

            Double totalAccident = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalAccident = totalAccident + total.getAccident();
            }
            result = totalAccident.toString();

            transaction.commit();
            logger.info("Total of accident for this month: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total accident for this month ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalAccidentForMonth of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @param driverId
     * @param initialDate
     * @param endDate
     * @return
     */
    public List<Sale> getSalesbyDriver(String driverId, Date initialDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales by Driver..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> taxiPath = root.get("taxi");
            Path<String> descriptionPath = root.get("description");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<Boolean> garagePath = root.get("garage");

            criteria.select(
                    builder.construct(Sale.class,
                            idPath, payDayPath, savingDayPath, payDatePath,
                            accidentPath, taxiPath, descriptionPath,
                            createdOnPath, createdByPath, garagePath)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThan(root.get("payDate"), endDate);

            criteria.where(builder.equal(root.get("person"), new Person(driverId)),
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales by driver: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales by driver ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesbyDriver of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Sale> getSalesbyUser(String userId, Date initialDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales by Driver..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> taxiPath = root.get("taxi");
            Path<String> descriptionPath = root.get("description");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<Boolean> garagePath = root.get("garage");

            criteria.select(
                    builder.construct(Sale.class,
                            idPath, payDayPath, savingDayPath, payDatePath,
                            accidentPath, taxiPath, descriptionPath,
                            createdOnPath, createdByPath, garagePath)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThan(root.get("payDate"), endDate);

            criteria.where(builder.equal(root.get("createdBy"), new User(userId, true)),
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales by driver: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales by driver ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesbyDriver of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @param driverId
     * @param initialDate
     * @param endDate
     * @return
     */
    public List<SaleDashboardView> findSaleDashboardView(String driverId, Date initialDate, Date endDate) {
        List<SaleDashboardView> result = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            StringBuilder hql = new StringBuilder("FROM SaleDashboardView WHERE driverId = :driverId ");

            hql.append("AND DATE(payDate) BETWEEN DATE(:initialDate) AND DATE(:endDate) ");
            hql.append("ORDER BY payDate");

            result = session.createQuery(hql.toString(), SaleDashboardView.class)
                    .setParameter("driverId", driverId)
                    .setParameter("initialDate", initialDate)
                    .setParameter("endDate", endDate)
                    .getResultList();

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not load sale from dashboard view ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findSaleDashboardView of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @param driverId
     * @param date
     * @return
     */
    @Nullable
    public SaleDashboardView findSaleByDriverIdEqualstoDate(String driverId, Date date) {
        SaleDashboardView result = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            
            
            String hqlC = "SELECT COUNT(sd) FROM SaleDashboardView sd WHERE driverId = :driverId AND DATE(payDate) = DATE(:date) ORDER BY totalToPay DESC";
            
            long countSalesDay = (long) session.createQuery(hqlC)
                    .setParameter("driverId", driverId)
                    .setParameter("date", date)
                    .uniqueResult();
            
            if(countSalesDay > 0){
                String hql = "FROM SaleDashboardView WHERE driverId = :driverId AND DATE(payDate) = DATE(:date) ORDER BY totalToPay DESC";

                    result = session.createQuery(hql, SaleDashboardView.class)
                    .setParameter("driverId", driverId)
                    .setParameter("date", date)
                    .setMaxResults(1)
                    .getSingleResult();
            }
            

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not load sale from dashboard view ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findSaleDashboardView of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @param driverId
     * @param startDate
     * @param endDate
     * @return
     */
    public List<Sale> getSalesGroupByDriverAndPayDate(String driverId, Date startDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = new ArrayList<>();

        try {
            logger.info("Searching all sales by Driver..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String hql = "SELECT SUM(s.payDay), SUM(s.paySaving), SUM(s.accident), "
                    + "DATE(s.payDate), s.person, s.id, s.taxi, s.description "
                    + "FROM Sale s "
                    + "WHERE DATE(s.payDate) BETWEEN :startDate AND :endDate "
                    + "AND s.person = :person "
                    + "GROUP BY DATE(s.payDate) ORDER BY DATE(s.payDate) ";
            Query query = session.createQuery(hql, Object[].class);

            query.setParameter("startDate", startDate);
            query.setParameter("endDate", endDate);
            query.setParameter("person", new Person(driverId));

            List<Object[]> results = query.getResultList();

            result = buildSaleList(results);

            transaction.commit();
            logger.info("Total of sales by driver: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales by driver ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesbyDriver of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Sale> getSalesbyTaxi(String taxiId, Date initialDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales by taxi..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> descriptionPath = root.get("description");
            Path<Person> person = root.get("person");
            Path<String> createdOnPath = root.get("createdOn");
            Path<Taxi> TaxiPath = root.get("taxi");
            Path<User> createdByPath = root.get("createdBy");

            criteria.select(
                    builder.construct(Sale.class, idPath, payDayPath,
                            savingDayPath, payDatePath, accidentPath,
                            descriptionPath, person, createdOnPath, TaxiPath, createdByPath)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThanOrEqualTo(root.get("payDate"), endDate);

            criteria.where(builder.equal(root.get("taxi"), new Taxi(taxiId)),
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales by taxi: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales by taxi ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesbyTaxi of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Sale> getSalesAll(Date initialDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales in General..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> descriptionPath = root.get("description");
            Path<String> personPath = root.get("person");
            Path<String> taxiPath = root.get("taxi");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<ExpenseStatusType> status = root.get("status");

            criteria.select(
                    builder.construct(Sale.class, idPath, payDayPath, savingDayPath, payDatePath, accidentPath,
                            descriptionPath, personPath, taxiPath, createdOnPath, createdByPath, status)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThanOrEqualTo(root.get("payDate"), endDate);

            criteria.where(
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales in general: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales in general ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesAll of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public List<Sale> getSalesAllByLocation(Date initialDate, Date endDate, String locationId) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales in General..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> descriptionPath = root.get("description");
            Path<String> personPath = root.get("person");
            Path<String> taxiPath = root.get("taxi");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");

            criteria.select(
                    builder.construct(Sale.class, idPath, payDayPath, savingDayPath, payDatePath, accidentPath,
                            descriptionPath, personPath, taxiPath, createdOnPath, createdByPath)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThanOrEqualTo(root.get("payDate"), endDate);
            Predicate locationPredicate = builder.equal(root.get("createdBy").get("location").get("id"), locationId);

            criteria.where(
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate,
                            locationPredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales in general: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales in general ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesAll of Sale", e);
            rollback(transaction);
        }

        return result;
    }
    
    public List<Sale> getSalesAllByLocationPending() {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales in General..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> descriptionPath = root.get("description");
            Path<String> personPath = root.get("person");
            Path<String> taxiPath = root.get("taxi");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<ExpenseStatusType> status = root.get("status");
            Path<BankCard> bankCard = root.get("bankCard");

            criteria.select(
                    builder.construct(Sale.class, idPath, payDayPath, savingDayPath, payDatePath, accidentPath,
                            descriptionPath, personPath, taxiPath, createdOnPath, createdByPath, status, bankCard)
            );

           
            Predicate estatusPredicate = builder.equal(root.get("status"), ExpenseStatusType.PENDING);

            criteria.where(
                    builder.and(
                          
                            estatusPredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales in general: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales in general ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesAll of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     * Delete a sale in the system
     *
     * @param saleId
     * @return True if sale was deleted otherwise false.
     */
    public boolean deleteSaleById(String saleId) {
        logger.info("deleteSaleById");

        boolean success = false;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaDelete<Sale> delete = builder.createCriteriaDelete(Sale.class);
            Root<Sale> root = delete.from(Sale.class);

            delete.where(builder.equal(root.get("id"), saleId));

            int total = session.createQuery(delete).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + saleId + " deleted");
            } else {
                transaction.rollback();
                logger.error("Error Sale deleted total: " + total);
            }
        } catch (HibernateException e) {
            logger.error("Sale can not be deleted ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method deleteSaleById() ", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     * Do to reprint from a sale in the system
     *
     * @param saleId
     * @return True if Sale was updated correctly otherwise false.
     */
    public boolean printSale(String saleId) {
        logger.info("printSale");

        boolean success = false;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<Sale> update = builder.createCriteriaUpdate(Sale.class);
            Root<Sale> rootUpdate = update.from(Sale.class);

            update.set("active", false);
            update.where(builder.equal(rootUpdate.get("id"), saleId));

            int total = session.createQuery(update).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + saleId + " updated");
            } else {
                transaction.rollback();
                logger.error("Error Sale updated total: " + total);
            }
        } catch (HibernateException e) {
            logger.error("Sale can not be updated ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method printSale() ", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     * Don´t print from a sale in the system
     *
     * @param saleId
     * @return True if Sale was updated correctly otherwise false.
     */
    public boolean noPrintSale(String saleId) {
        logger.info("noPrintSale");

        boolean success = false;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<Sale> update = builder.createCriteriaUpdate(Sale.class);
            Root<Sale> rootUpdate = update.from(Sale.class);

            update.set("active", true);
            update.where(builder.equal(rootUpdate.get("id"), saleId));

            int total = session.createQuery(update).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + saleId + " updated");
            } else {
                transaction.rollback();
                logger.error("Error Sale updated total: " + total);
            }
        } catch (HibernateException e) {
            logger.error("Sale can not be updated ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method noPrintSale() ", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     *
     * @return
     */
    public String getTotalSaleForToday() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total sales for today..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> payDayPath = root.get("payDay");

            criteria.select(
                    builder.construct(Sale.class, payDayPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("createdOn"), DateWrapper.midnigthMXTime());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("createdOn"), DateWrapper.lastMXTime());
            //Predicate initialDate = builder.equal(root.get("payDate"), DateWrapper.today().subtractHours(6).midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate, lastDate
                    )
            );

            Double totalPay = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalPay = totalPay + total.getPayDay();
            }
            result = totalPay.toString();

            transaction.commit();
            logger.info("Total of sales for today: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales for today ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalSaleForToday of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @return
     */
    public String getTotalSavingForToday() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total saving for today..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> savingDayPath = root.get("paySaving");

            criteria.select(
                    builder.construct(Sale.class, savingDayPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("createdOn"), DateWrapper.midnigthMXTime());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("createdOn"), DateWrapper.lastMXTime());
            //Predicate initialDate = builder.equal(root.get("payDate"), DateWrapper.today().subtractHours(6).midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate, lastDate
                    )
            );

            Double totalSaving = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalSaving = totalSaving + total.getPaySaving();
            }
            result = totalSaving.toString();

            transaction.commit();
            logger.info("Total of saving for today: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total saving for today ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalSavingForToday of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @return
     */
    public String getTotalAccidentForToday() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total accident for today..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<Double> savingDayPath = root.get("accident");

            criteria.select(
                    builder.construct(Sale.class, savingDayPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("createdOn"), DateWrapper.midnigthMXTime());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("createdOn"), DateWrapper.lastMXTime());
            //Predicate initialDate = builder.equal(root.get("payDate"), DateWrapper.today().subtractHours(6).midnigthTime().asDate());

            criteria.where(
                    builder.and(
                            initialDate, lastDate
                    )
            );

            Double totalAccident = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sale total : session.createQuery(criteria).getResultList()) {
                totalAccident = totalAccident + total.getAccident();
            }
            result = totalAccident.toString();

            transaction.commit();
            logger.info("Total of accidents for today: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total accidents for today ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalAccidentForToday of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @return
     */
    public String getTotalExpenseForToday() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total expense for today..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Expense> criteria = builder.createQuery(Expense.class);
            Root<Expense> root = criteria.from(Expense.class);

            Path<Double> paymentPath = root.get("payment");

            criteria.select(
                    builder.construct(Expense.class, paymentPath)
            );

            Predicate initialDate = builder.equal(root.get("paymentDate"), DateWrapper.today().subtractHours(6).midnigthTime().asDate());

            criteria.where(builder.equal(root.get("status"), ExpenseStatusType.AUTHORIZED),
                    builder.and(
                            initialDate
                    )
            );

            Double totalToday = 0.0;
            session.createQuery(criteria).getResultList();
            for (Expense total : session.createQuery(criteria).getResultList()) {
                totalToday = totalToday + total.getPayment();
            }
            result = totalToday.toString();

            transaction.commit();
            logger.info("Total of expense for today: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total expense for today ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalExpenseForToday of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public String getTotalExpenseForMonth() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total expense for month..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Expense> criteria = builder.createQuery(Expense.class);
            Root<Expense> root = criteria.from(Expense.class);

            Path<Double> paymentPath = root.get("payment");

            criteria.select(
                    builder.construct(Expense.class, paymentPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("paymentDate"), DateWrapper.today().subtractHours(6).firstDateForMonth().midnigthTime().asDate());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("paymentDate"), DateWrapper.today().subtractHours(6).lastDateForMonth().midnigthTime().asDate());
            Predicate corte = builder.notEqual(root.get("type"), ExpenseType.CORTE);

            criteria.where(builder.equal(root.get("status"), ExpenseStatusType.AUTHORIZED),
                    builder.and(
                            initialDate,
                            lastDate,
                            corte
                    )
            );

            Double totalMonth = 0.0;
            session.createQuery(criteria).getResultList();
            for (Expense total : session.createQuery(criteria).getResultList()) {
                totalMonth = totalMonth + total.getPayment();
            }
            result = totalMonth.toString();

            transaction.commit();
            logger.info("Total of expense for month: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total expense for month ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalExpenseForMonth of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     *
     * @param sale
     * @return
     */
    public boolean updateSale(Sale sale) {
        boolean success = false;
        Transaction transaction = null;

        try {
            logger.info("updateSale");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<Sale> update = builder.createCriteriaUpdate(Sale.class);
            Root<Sale> rootUpdate = update.from(Sale.class);

            if (null != sale.getPayDay()) {
                update.set("payDay", sale.getPayDay());
            }

            if (null != sale.getPaySaving()) {
                update.set("paySaving", sale.getPaySaving());
            }

            if (null != sale.getAccident()) {
                update.set("accident", sale.getAccident());
            }

            update.set("lastUpdatedBy", sale.getLastUpdatedBy());
            update.set("lastUpdatedOn", sale.getLastUpdatedOn());

            update.where(builder.equal(rootUpdate.get("id"), sale.getId()));

            int total = session.createQuery(update).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + sale + " updated");
            } else {
                rollback(transaction);
                logger.error("Error Sale updated total: " + total);
            }

        } catch (HibernateException e) {
            logger.error("Can not update the sale " + sale, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method updateSale(" + sale + ")", e);
            rollback(transaction);
        }

        return success;
    }

    /**
     *
     * @param id
     * @param day
     * @return
     */
    public ServiceOrder findServiceOrderByTaxiId(String id, Date day) {
        logger.info("findServiceOrderByTaxiId");

        ServiceOrder order = null;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<ServiceOrder> query = builder.createQuery(ServiceOrder.class);

            Root<ServiceOrder> root = query.from(ServiceOrder.class);

            Path<String> name = root.get("name");
            Path<Date> progressDate = root.get("progressDate");
            Path<Date> finishDate = root.get("finishDate");

            Predicate startDate = builder.greaterThanOrEqualTo(root.get("progressDate"), DateWrapper.startDate(day));
            Predicate endDate = builder.lessThanOrEqualTo(root.get("progressDate"), DateWrapper.endDate(day));

            query.select(builder.construct(ServiceOrder.class, name, progressDate, finishDate));
            query.where(
                    builder.equal(root.get("taxi"), new Taxi(id)),
                    builder.and(
                            builder.equal(root.get("serviceOrderType"), ServiceOrderType.FINISHED),
                            startDate,
                            endDate)
            );

            order = session.createQuery(query).uniqueResult();

            transaction.commit();
            logger.info("Service order time details " + order);
        } catch (HibernateException e) {
            logger.error("Can not ufaind service order for taxi id: " + id, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findServiceOrderByTaxiId(" + id + ")", e);
            rollback(transaction);
        }
        return order;
    }

    /**
     *
     * @param driverId
     * @param date
     * @throws Exception
     */
    public void deleteToCollectByDriverAndDate(String driverId, Date date) throws Exception {
        logger.info("deleteToCollectByDriverAndDate");

        //boolean success = false;
        Transaction transaction = null;

        try {

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaDelete<ToCollect> delete = builder.createCriteriaDelete(ToCollect.class);
            Root<ToCollect> root = delete.from(ToCollect.class);

            Calendar calendar = Calendar.getInstance();

            calendar.setTime(date);

            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);

            delete.where(
                    builder.and(
                            builder.equal(root.get("collect"), calendar.getTime()),
                            builder.equal(root.get("driver"), new Person(driverId))
                    )
            );

            session.createQuery(delete).executeUpdate();
            transaction.commit();
            logger.info("To collect date deleted " + date);
        } catch (HibernateException e) {
            logger.error("Can not delete the date from To_Collect", e);
            rollback(transaction);
            throw e;
        } catch (Exception e) {
            logger.error("Method deleteToCollectByDriverAndDate of Sale", e);
            rollback(transaction);
            throw e;
        }
    }

    /**
     *
     * @param driverId
     * @param paymentDate
     * @param isGarage
     */
    public void updateToCollectByTaxyDriverAndPaymentDate(String driverId, Date paymentDate, boolean isGarage, boolean isNuevaAsignacion, boolean isBanca) {
        logger.info("updateToCollectByTaxyDriverAndPaymentDate");

        Transaction transaction = null;

        try {
            int result = 0;
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String sql = "SELECT id FROM TAXI WHERE driver_id = :driverId";

            String taxiId = (String) session.createNativeQuery(sql)
                    .setParameter("driverId", driverId)
                    .uniqueResult();

            sql = "SELECT COUNT(id) FROM SALE WHERE taxi_id = :taxiId and DATE(pay_date) = DATE(:paymentDate)";
            // total debe ser mayor o igual a 1
            BigInteger total = (BigInteger) session.createNativeQuery(sql)
                    .setParameter("paymentDate", paymentDate)
                    .setParameter("taxiId", taxiId)
                    .uniqueResult();

            if (null != total && total.compareTo(BigInteger.ZERO) == 1) {
                sql = "SELECT SUM(pay_day + pay_saving + accident)"
                        + " FROM SALE WHERE taxi_id = :taxiId and DATE(pay_date) = DATE(:paymentDate)"
                        + " ORDER BY pay_date";

                Double totalPaid = (Double) session.createNativeQuery(sql)
                        .setParameter("paymentDate", paymentDate)
                        .setParameter("taxiId", taxiId)
                        .uniqueResult();

                sql = "SELECT SUM(pay_day + pay_saving + pay_accident) FROM TAXI WHERE id = :taxiId";
                Double totalToPay = (Double) session.createNativeQuery(sql)
                        .setParameter("taxiId", taxiId)
                        .uniqueResult();

                if (isNuevaAsignacion || isGarage || isBanca || totalPaid.compareTo(totalToPay) == 0) {

                    sql = "DELETE FROM TO_COLLECT WHERE driver_id = :driverId AND DATE(to_collect) = DATE(:paymentDate)";

                    result = session.createNativeQuery(sql)
                            .setParameter("driverId", driverId)
                            .setParameter("paymentDate", paymentDate)
                            .executeUpdate();
                } else {
                    Date lastUpdatedOn = DateWrapper.getTodayMXTime();
                    Double newTotal = totalToPay - totalPaid;
                    sql = "UPDATE TO_COLLECT "
                            + " SET total = :newTotal, complete = 0, last_updated_on = :lastUpdatedOn"
                            + " WHERE driver_id = :driverId AND DATE(to_collect) = DATE(:paymentDate)";

                    result = session.createNativeQuery(sql)
                            .setParameter("newTotal", newTotal.intValue())
                            .setParameter("driverId", driverId)
                            .setParameter("paymentDate", paymentDate)
                            .setParameter("lastUpdatedOn", lastUpdatedOn)
                            .executeUpdate();
                }
            }

            transaction.commit();
            logger.info("To collect date deleted " + paymentDate);
        } catch (HibernateException e) {
            logger.error("Can not delete the date from To_Collect", e);
            rollback(transaction);
            throw e;
        } catch (Exception e) {
            logger.error("Method deleteToCollectByDriverAndDate of Sale", e);
            rollback(transaction);
            throw e;
        }
    }

    public String getTotalAddForToday() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total add for today..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sanction> criteria = builder.createQuery(Sanction.class);
            Root<Sanction> root = criteria.from(Sanction.class);

            Path<Double> paymentPath = root.get("payment");

            criteria.select(
                    builder.construct(Sanction.class, paymentPath)
            );

            Predicate paymentDate = builder.equal(root.get("createdOn"), DateWrapper.today().subtractHours(6).midnigthTime().asDate());

            criteria.where(builder.equal(root.get("type"), SanctionType.ADD),
                    builder.and(
                            paymentDate
                    )
            );

            Double totalToday = 0.0;
            session.createQuery(criteria).getResultList();
            for (Sanction total : session.createQuery(criteria).getResultList()) {
                totalToday = totalToday + total.getPayment();
            }
            result = totalToday.toString();

            transaction.commit();
            logger.info("Total of add for today: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total add for today ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalAddForToday of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public String getTotalAddForMonth() {

        Transaction transaction = null;
        String result = null;

        try {
            logger.info("Searching total add for month..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sanction> criteria = builder.createQuery(Sanction.class);
            Root<Sanction> root = criteria.from(Sanction.class);

            Path<Double> paymentPath = root.get("payment");

            criteria.select(
                    builder.construct(Sanction.class, paymentPath)
            );

            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("paymentDate"), DateWrapper.today().subtractHours(6).firstDateForMonth().midnigthTime().asDate());
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("paymentDate"), DateWrapper.today().subtractHours(6).lastDateForMonth().midnigthTime().asDate());

            criteria.where(builder.equal(root.get("type"), SanctionType.ADD),
                    builder.and(
                            initialDate,
                            lastDate
                    )
            );

            Double totalMonth = 0.0;
            //session.createQuery(criteria).getResultList();
            for (Sanction total : session.createQuery(criteria).getResultList()) {
                totalMonth = totalMonth + total.getPayment();
            }
            result = totalMonth.toString();

            transaction.commit();
            logger.info("Total of add for month: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total add for month ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalAddForMonth of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    /**
     * Save to collect new sale from date and driver id
     *
     * @param toCollect
     * @return
     */
    public boolean saveToCollectByDriverIdAndDate(ToCollect toCollect) {
        logger.info("Save to collect by driver id and date");

        boolean success = false;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            session.save(toCollect);

            transaction.commit();
            logger.info("To Collect sale added it");

            success = true;
        } catch (HibernateException e) {
            logger.error("Can not save to collect sale", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method saveToCollectByDriverIdAndDate", e);
            rollback(transaction);
        }

        return success;
    }

    /**
     *
     * @param idDriver
     * @return
     */
    public Taxi findDayPaymentsByIdDriver(String idDriver) {
        logger.info("Find Day Payments by id driver");

        Transaction transaction = null;
        Taxi taxi = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Taxi> query = builder.createQuery(Taxi.class);
            Root<Taxi> root = query.from(Taxi.class);

            Path<Double> payDayPath = root.get("payDay");
            Path<Double> paySavingPath = root.get("paySaving");
            Path<Double> payAccidentPath = root.get("payAccident");

            query.select(builder.construct(Taxi.class, payDayPath, paySavingPath, payAccidentPath));

            query.where(builder.equal(root.get("driver"), new Person(idDriver)));

            taxi = session.createQuery(query).uniqueResult();

            transaction.commit();
            logger.info("Taxi found");
        } catch (HibernateException e) {
            logger.error("Can not find  payments day", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findDayPaymentsByIdDriver", e);
            rollback(transaction);
        }

        return taxi;
    }

    /**
     *
     * @param driverId
     * @param startDate
     * @param endDate
     * @return
     */
    public List<Object[]> findStyleClassSaleByDriverIdBetweenDates(String driverId, Date startDate, Date endDate) {
        logger.info("Find Style Class Sale By Driver Id Between Dates");
        List<Object[]> results = new ArrayList<>();

        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String sqlQuery = "SELECT \n"
                    + "IF((SUM(pay_day) + SUM(pay_saving) + SUM(accident)) >= (SELECT (t.pay_day + t.pay_saving + t.pay_accident)  FROM TAXI t WHERE t.driver_id = :driverId),'schedule-payed','schedule-no-payed') AS style_class, \n"
                    + "pay_date AS pay_date \n"
                    + "FROM SALE \n"
                    + "WHERE person_id = :driverId AND \n"
                    + "DATE(pay_date) BETWEEN :startDate AND :endDate \n"
                    + "GROUP BY DATE(pay_date) \n"
                    + "ORDER BY DATE(pay_date) ";

            NativeQuery nativeQuery = session.createNativeQuery(sqlQuery);

            nativeQuery.setParameter("driverId", driverId);
            nativeQuery.setParameter("startDate", startDate);
            nativeQuery.setParameter("endDate", endDate);

            Object rows = nativeQuery.getResultList();

            if (null != rows) {
                List<?> list = new ArrayList<>();

                if (rows.getClass().isArray()) {
                    list = Arrays.asList((Object[]) rows);
                } else if (rows instanceof Collection) {
                    list = new ArrayList<>((Collection<?>) rows);
                }

                for (Object object : list) {
                    Object[] array = (Object[]) object;
                    results.add(array);
                }

                logger.info("execute sql native query's list " + results.size());
            }

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not Find Style Class Sale By Driver Id Between Dates", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findStyleClassSaleByDriverIdBetweenDates", e);
            rollback(transaction);
        }
        return results;
    }

    private List<Sale> buildSaleList(Object results) {
        List<Sale> sales = new ArrayList<>();
        try {
            List<?> list = new ArrayList<>();
            if (null != results) {
                if (results.getClass().isArray()) {
                    list = Arrays.asList((Object[]) results);
                } else if (results instanceof Collection) {
                    list = new ArrayList<>((Collection<?>) results);
                }

                for (Object object : list) {
                    Object[] array = (Object[]) object;
                    Sale sale = new Sale(array);

                    sales.add(sale);
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
        return sales;
    }

    public Sale getById(String saleId) {
        logger.info("getById");

        Sale sale = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            sale = session.get(Sale.class, saleId);

            transaction.commit();
            logger.info("Sale " + sale);
        } catch (HibernateException e) {
            logger.error("Sale cannot be loaded " + saleId, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getById(" + saleId + ") ", e);
            rollback(transaction);
        }
        return sale;
    }

    public List<Sale> getSalesbyDeposit(Date initialDate, Date endDate) {

        Transaction transaction = null;
        List<Sale> result = null;

        try {
            logger.info("Searching all sales by deposit..");

            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();

            CriteriaQuery<Sale> criteria = builder.createQuery(Sale.class);
            Root<Sale> root = criteria.from(Sale.class);

            Path<String> payDayPath = root.get("payDay");
            Path<String> idPath = root.get("id");
            Path<String> savingDayPath = root.get("paySaving");
            Path<String> payDatePath = root.get("payDate");
            Path<String> accidentPath = root.get("accident");
            Path<String> taxiPath = root.get("taxi");
            Path<String> descriptionPath = root.get("description");
            Path<String> createdOnPath = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<Boolean> garagePath = root.get("garage");
            Path<Person> personPath = root.get("person");

            criteria.select(
                    builder.construct(Sale.class,
                            idPath, payDayPath, savingDayPath, payDatePath,
                            accidentPath, taxiPath, descriptionPath,
                            createdOnPath, createdByPath, garagePath,personPath)
            );

            Predicate initialDatePredicate = builder.greaterThanOrEqualTo(root.get("payDate"), initialDate);
            Predicate lastDatePredicate = builder.lessThan(root.get("payDate"), endDate);

            criteria.where(builder.equal(root.get("transfer"), TransferType.ENEBLED),
                    builder.and(
                            initialDatePredicate,
                            lastDatePredicate
                    )
            );

            criteria.orderBy(
                    builder.asc(root.get("payDate"))
            );

            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of sales by driver: " + result);
        } catch (HibernateException e) {
            logger.error("Can not load the information for total sales by driver ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getSalesbyDriver of Sale", e);
            rollback(transaction);
        }

        return result;
    }

    public boolean autorizar(String saleId) {
        logger.info("autorizar");

        boolean success = false;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<Sale> update = builder.createCriteriaUpdate(Sale.class);
            Root<Sale> rootUpdate = update.from(Sale.class);

            update.set("status", ExpenseStatusType.AUTHORIZED);
            update.where(builder.equal(rootUpdate.get("id"), saleId));

            int total = session.createQuery(update).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + saleId + " updated");
            } else {
                transaction.rollback();
                logger.error("Error Sale updated total: " + total);
            }
        } catch (HibernateException e) {
            logger.error("Sale can not be updated ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method autorizar() ", e);
            rollback(transaction);
        }
        return success;
    }
    private static final long serialVersionUID = -6427080681547370834L;

    final Logger logger = LogManager.getLogger(SaleController.class);

    public SaleController() {
    }

}
