package com.arrebol.taxiservicios.controller.stocktaking;

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.GeneralPublic;
import com.arrebol.taxiservicios.model.admin.Taxi;
import com.arrebol.taxiservicios.model.catalog.Brand;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.catalog.Product;
import com.arrebol.taxiservicios.model.core.Person;
import com.arrebol.taxiservicios.model.core.User;
import com.arrebol.taxiservicios.model.enums.BasicType;
import com.arrebol.taxiservicios.model.enums.ExpenseStatusType;
import com.arrebol.taxiservicios.model.enums.PersonType;
import com.arrebol.taxiservicios.model.enums.ServiceOrderType;
import com.arrebol.taxiservicios.model.stocktaking.ServiceOrder;
import com.arrebol.taxiservicios.model.stocktaking.ServiceOrderHasProduct;
import com.arrebol.taxiservicios.model.stocktaking.ServiceOrderHasProductId;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
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;

/**
 *
 * @author Carlos Janitzio Zavala Lopez
 */
public class ServiceOrderController extends ConnectionManager implements Serializable {

    /**
     * Search all economic number like coincidence.
     *
     * @param like Coincident to find in economic number.
     *
     * @return All coindence with patter found.
     */
    public List<String> getEconomicNumbersLike(String like) {
        logger.info("getEconomicNumberLike");

        List<String> resultList = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

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

            query.select(root.get("economicNumber"));
            query.where(
                    builder.like(root.get("economicNumber"), "%" + like + "%"),
                    builder.and(builder.equal(root.get("active"), true))
            );

            resultList = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Coincidences " + resultList);
        } catch (HibernateException e) {
            logger.error("Economic numbers cannot be loaded " + like, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getEconomicNumberLike(" + like + ") ", e);
            rollback(transaction);
        }
        return resultList;
    }

    public List<String> getPlacaLike(String like) {
        logger.info("getPlacaLike");

        List<String> resultList = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<String> query = builder.createQuery(String.class);
            Root<GeneralPublic> root = query.from(GeneralPublic.class);

            query.select(root.get("placas"));
            query.where(
                    builder.like(root.get("placas"), "%" + like + "%"),
                    builder.and(builder.equal(root.get("activeStatus"), BasicType.ENEBLED))
            );

            resultList = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Coincidences " + resultList);
        } catch (HibernateException e) {
            logger.error("Placa general public cannot be loaded " + like, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getPlacaLike(" + like + ") ", e);
            rollback(transaction);
        }
        return resultList;
    }

    /**
     * Get all taxi details by economic number.
     *
     * @param economicNumber Economic number.
     *
     * @return Taxi details.
     */
    public Taxi getTaxiByEconomicNumber(String economicNumber) {
        logger.info("getTaxiByEconomicNumber");

        Taxi result = null;
        Transaction transaction = 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);

            query.select(root);
            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true),
                            builder.equal(root.get("economicNumber"), economicNumber)
                    )
            );

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

            transaction.commit();
            logger.info("Taxi loaded");
        } catch (HibernateException e) {
            logger.error("Taxi cannot be loaded " + economicNumber, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTaxiByEconomicNumber(" + economicNumber + ") ", e);
            rollback(transaction);
        }
        return result;
    }

    public GeneralPublic getGeneralPublicByPlaca(String placa) {
        logger.info("getGeneralPublicByPlaca");

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

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<GeneralPublic> query = builder.createQuery(GeneralPublic.class);
            Root<GeneralPublic> root = query.from(GeneralPublic.class);

            query.select(root);
            query.where(
                    builder.and(
                            builder.equal(root.get("activeStatus"), BasicType.ENEBLED),
                            builder.equal(root.get("placas"), placa)
                    )
            );

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

            transaction.commit();
            logger.info("GeneralPublic loaded");
        } catch (HibernateException e) {
            logger.error("GeneralPublic cannot be loaded " + placa, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getGeneralPublicByPlaca(" + placa + ") ", e);
            rollback(transaction);
        }
        return result;
    }

    /**
     * Get all service order in active status order by nameNumber.
     *
     * @return A list of all service order availables.
     */
    public List<ServiceOrder> getAllServiceOrderActive() {
        logger.info("getAllServiceOrderActive");

        List<ServiceOrder> serviceOrders = 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> id = root.get("id");
            Path<String> name = root.get("name");
            Path<Integer> nameNumber = root.get("nameNumber");
            Path<Float> km = root.get("km");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, nameNumber, km, serviceOrderType, taxi
                    )
            );
            query.where(builder.equal(root.get("active"), true));

            query.orderBy(
                    builder.asc(root.get("nameNumber"))
            );

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderActive() ", e);
            rollback(transaction);
        }
        return serviceOrders;
    }

    /**
     * Get all service order in active status and serviceOrderType NOT IN
     * (EMPTY_STOCK, FINISHED) order by nameNumber.
     *
     * @return A list of all service order availables.
     */
    public List<ServiceOrder> getAllServiceOrderNotIn(Location location) {
        logger.info("getAllServiceOrderToAuthorize");

        List<ServiceOrder> serviceOrders = 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);
            Join<ServiceOrder, GeneralPublic> b = root.join("generalPublic", JoinType.LEFT);
            query.multiselect(root, b);

            Path<String> id = root.get("id");
            Path<String> name = root.get("name");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");
            Path<String> createdOn = root.get("createdOn");
            Path<String> pendingDate = root.get("pendingDate");
            Path<String> approvateDate = root.get("approvateDate");
            Path<String> progressDate = root.get("progressDate");
            Path<String> finishDate = root.get("finishDate");
            Path<String> description = root.get("description");
            Path<Float> km = root.get("km");
            Path<String> paseTallerDate = root.get("paseTallerDate");
            Path<String> paseTaller = root.get("tallerExterno");
            Path<User> createdBy = root.get("createdBy");
            Path<GeneralPublic> general = root.get("generalPublic");

            List<ServiceOrderType> status = new ArrayList<ServiceOrderType>();

            status.add(ServiceOrderType.PASE_TALLER);
            status.add(ServiceOrderType.PENDING);
            status.add(ServiceOrderType.APPROVED);
            status.add(ServiceOrderType.PROGRESS);

            Expression<ServiceOrderType> expression = root.get("serviceOrderType");
            Predicate inClause = expression.in(status);
             Predicate locationPrm = builder.equal(root.get("taxi").get("location"), location);

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, serviceOrderType, taxi, createdOn,
                            pendingDate, approvateDate, progressDate,
                            finishDate, description, km, createdBy, paseTallerDate, paseTaller, general
                    )
            );

            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true), inClause,locationPrm
                    )
            );
            query.orderBy(
                    builder.asc(root.get("nameNumber"))
            );

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders to authorize loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders to authorize cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderToAuthorize() ", e);
            rollback(transaction);
        }

        return serviceOrders;
    }

    /**
     *
     * Searching all Products.
     *
     * @return
     */
    public List<Product> getAllProducts() {
        logger.info("Searching all products..");

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

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

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<Product> criteria = builder.createQuery(Product.class);

            Root<Product> root = criteria.from(Product.class);

            Path<String> idPath = root.get("id");
            Path<String> namePath = root.get("name");
            Path<String> codePath = root.get("code");
            Path<Double> amountPath = root.get("amount");
            Path<Integer> utilityPath = root.get("utility");
            Path<Integer> quantityPath = root.get("quantity");
            Path<Brand> brandPath = root.get("brand");
            Path<Double> amount2Path = root.get("amount2");
            Path<Integer> utility2Path = root.get("utility2");

            criteria.select(
                    builder.construct(
                            Product.class,
                            idPath, namePath, codePath, amountPath,
                            utilityPath, quantityPath, brandPath, amount2Path, utility2Path
                    )
            );
            criteria.where(builder.equal(root.get("activeStatus"), BasicType.ENEBLED));
            result = session.createQuery(criteria).getResultList();

            transaction.commit();
            logger.info("Total of products found : " + (null == result ? -1 : result.size()));
        } catch (HibernateException e) {
            logger.error("Can not load list of products ", e);
            rollback(transaction);
            throw e;
        } catch (Exception e) {
            logger.error("Method getAllProducts of Product", e);
            rollback(transaction);
            throw e;
        }

        return result;
    }

    public boolean payServiceOrderHasProduct(String idServiceOrder,String userId){
        logger.debug("updateStatus");

        boolean success = false;
       
        Transaction transaction = null;

        try {
           

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

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<ServiceOrderHasProduct> update = builder.createCriteriaUpdate(ServiceOrderHasProduct.class);

            Root<ServiceOrderHasProduct> root = update.from(ServiceOrderHasProduct.class);

            
            update.set(root.get("cobrado"), BasicType.ENEBLED);
            update.set(root.get("lastUpdatedBy"), new User(userId, ""));
            update.set(root.get("lastUpdatedOn"), DateWrapper.getTodayMXTime());
            update.set(root.get("payDate"), DateWrapper.getTodayMXTime());
            

            update.where(builder.equal(root.get("serviceOrder").get("id"), idServiceOrder));

            session.createQuery(update).executeUpdate();

            transaction.commit();
            logger.debug("service order has product " + idServiceOrder + " was updated.");

            success = true;
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be authorized ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method authorizeServiceOrder() ", e);
            rollback(transaction);
        }
        return success;
    
    }
    /**
     * Create the service order.
     *
     * @param serviceOrder Service Order to be created.
     * @param sohps List of products that Service Order has.
     *
     * @return True if the Service order and its details were successfully saved
     * otherwiser false.
     */
    public boolean createServiceOrder(ServiceOrder serviceOrder, List<ServiceOrderHasProduct> sohps) {
        logger.info("saveServiceOrder");

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

            session.save(serviceOrder);

            sohps.forEach((ServiceOrderHasProduct sohp) -> {
                session.save(sohp);
            });

            transaction.commit();
            logger.info("Service order saved");

            success = true;
        } catch (HibernateException e) {
            logger.error("Service order cannot be saved " + serviceOrder, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method saveServiceOrder(" + serviceOrder + sohps + ")", e);
            rollback(transaction);
        }
        return success;
    }

    @Deprecated
    public Object getTaxiDetailsInnerJoin(String economicNumber) {
        logger.info("getTaxiDetailsInnerJoin");

        Object result = null;
        Transaction transaction = null;
        try {

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

            StringBuilder sb = new StringBuilder("select ");

            sb.append("t.economicNumber, t.placa, v.name ,b.name, c.name, ");
            sb.append("driver.firstName, driver.secondName, driver.lastName, driver.middleName, ");
            sb.append("customer.firstName, customer.secondName, customer.lastName, customer.middleName ");
            sb.append("from Taxi t ");
            sb.append("inner join Person driver on t.driver = driver.id ");
            sb.append("inner join Person customer on t.customer = customer.id ");
            sb.append("inner join Vehicle v on t.vehicle = v.id ");
            sb.append("inner join Brand b on v.brand = b.id ");
            sb.append("inner join Color c on t.color = c.id ");
            sb.append("where t.economicNumber =");
            sb.append("'");
            sb.append(economicNumber);
            sb.append("'");

            result = session.createQuery(sb.toString()).uniqueResult();

            transaction.commit();
            logger.info("Taxi details loaded");
        } catch (HibernateException e) {
            logger.error("Taxi details cannot be loaded " + economicNumber, e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTaxiDetailsInnerJoin(" + economicNumber + ") ", e);
            rollback(transaction);
        }
        return result;
    }

    /**
     * Get next folio number.
     *
     * @return Next folio number if query was success other wise null.
     */
    public synchronized Integer getNextFolio() {
        logger.info("getNextFolio");

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

            String sb = "select max(nameNumber) as nameNumber from ServiceOrder";

            Object val = session.createQuery(sb).uniqueResult();

            transaction.commit();

            nextFolio = null == val ? 0 : (Integer) val;
            nextFolio = nextFolio + 1;

            logger.info("next folio " + nextFolio);
        } catch (HibernateException e) {
            logger.error("HibernateException getNextFolio()", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getNextFolio() ", e);
            rollback(transaction);
        }
        return nextFolio;
    }

    /**
     *
     * @param id
     * @return
     */
    public List<ServiceOrderHasProduct> findAllProductByOrderId(String id) {
        logger.info("getServiceOrderHasProduct");

        List<ServiceOrderHasProduct> list = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

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

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

            Path<ServiceOrderHasProductId> sohpId = root.get("id");
            Path<Product> product = root.get("product");
            Path<Integer> quantity = root.get("quantity");
            Path<Float> price = root.get("price");
            Path<Float> total = root.get("total");

            query.select(builder.construct(ServiceOrderHasProduct.class,
                    sohpId, product, quantity, price, total));

            query.where(builder.equal(root.get("serviceOrder"), new ServiceOrder(id)));

            list = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Load all products by Service Order");
        } catch (HibernateException e) {
            logger.error("HibernateException getServiceOrderHasProduct()", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getServiceOrderHasProduct() ", e);
            rollback(transaction);
        }
        return list;
    }

    /**
     * Gell all Service Order in PENDING status and active.
     *
     * @return List of all services order in pending status or empety list if
     * doesn not exist.
     */
    public List<ServiceOrder> getAllServiceOrderActiveAndPending() {
        logger.info("getAllServiceOrderActive");

        List<ServiceOrder> serviceOrders = 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);
            Join<ServiceOrder, User> c = root.join("paseTallerUser", JoinType.LEFT);
            query.multiselect(root, c);
            Join<ServiceOrder, User> d = root.join("pendienteUser", JoinType.LEFT);
            query.multiselect(root, d);
            Join<ServiceOrder, User> e = root.join("aprobadoUser", JoinType.LEFT);
            query.multiselect(root, e);
            Join<ServiceOrder, User> f = root.join("progresoUser", JoinType.LEFT);
            query.multiselect(root, f);
            Join<ServiceOrder, User> j = root.join("terminadoUser", JoinType.LEFT);
            query.multiselect(root, j);

            Path<String> id = root.get("id");
            Path<String> name = root.get("name");
            Path<Integer> nameNumber = root.get("nameNumber");
            Path<Float> km = root.get("km");
            Path<Float> kmMaintenance = root.get("kmMaintenance");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");
            Path<String> createdOn = root.get("createdOn");
            Path<String> pendingDate = root.get("pendingDate");
            Path<String> approvateDate = root.get("approvateDate");
            Path<String> progressDate = root.get("progressDate");
            Path<String> finishDate = root.get("finishDate");
            Path<String> description = root.get("description");
            Path<String> paseTallerDate = root.get("paseTallerDate");
            Path<User> createdBy = root.get("createdBy");
            
            Path<User> paseTallerUser = root.get("paseTallerUser");
            Path<User> pendienteUSer = root.get("pendienteUser");
            Path<User> aprobadoUser = root.get("aprobadoUser");
            Path<User> progresoUser = root.get("progresoUser");
            Path<User> terminadoUser = root.get("terminadoUser");

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, nameNumber, km, serviceOrderType, taxi, createdOn,
                            pendingDate, approvateDate, progressDate, finishDate, description, kmMaintenance, paseTallerDate, createdBy, 
                            paseTallerUser, pendienteUSer, aprobadoUser, progresoUser, terminadoUser
                    )
            );
            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true),
                            builder.or(
                                    builder.equal(root.get("serviceOrderType"), ServiceOrderType.PENDING),
                                    builder.equal(root.get("serviceOrderType"), ServiceOrderType.PASE_TALLER)
                            )
                    )
            );
            query.orderBy(builder.asc(root.get("nameNumber")));

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderActive() ", e);
            rollback(transaction);
        }
        return serviceOrders;
    }
     public List<ServiceOrder> getAllServiceOrderActiveAndPendingByLocation(Location location) {
        logger.info("getAllServiceOrderActive");

        List<ServiceOrder> serviceOrders = 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);
            Join<ServiceOrder, User> c = root.join("paseTallerUser", JoinType.LEFT);
            query.multiselect(root, c);
            Join<ServiceOrder, User> d = root.join("pendienteUser", JoinType.LEFT);
            query.multiselect(root, d);
            Join<ServiceOrder, User> e = root.join("aprobadoUser", JoinType.LEFT);
            query.multiselect(root, e);
            Join<ServiceOrder, User> f = root.join("progresoUser", JoinType.LEFT);
            query.multiselect(root, f);
            Join<ServiceOrder, User> j = root.join("terminadoUser", JoinType.LEFT);
            query.multiselect(root, j);
            Join<ServiceOrder, User> k = root.join("payUser", JoinType.LEFT);
            query.multiselect(root, k);

            Path<String> id = root.get("id");
            Path<String> name = root.get("name");
            Path<Integer> nameNumber = root.get("nameNumber");
            Path<Float> km = root.get("km");
            Path<Float> kmMaintenance = root.get("kmMaintenance");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");
            Path<String> createdOn = root.get("createdOn");
            Path<String> pendingDate = root.get("pendingDate");
            Path<String> approvateDate = root.get("approvateDate");
            Path<String> progressDate = root.get("progressDate");
            Path<String> finishDate = root.get("finishDate");
            Path<String> description = root.get("description");
            Path<String> paseTallerDate = root.get("paseTallerDate");
            Path<User> createdBy = root.get("createdBy");
            
            Path<User> paseTallerUser = root.get("paseTallerUser");
            Path<User> pendienteUSer = root.get("pendienteUser");
            Path<User> aprobadoUser = root.get("aprobadoUser");
            Path<User> progresoUser = root.get("progresoUser");
            Path<User> terminadoUser = root.get("terminadoUser");
            
            Path<Float> subtotal = root.get("subtotal");

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, nameNumber, km, serviceOrderType, taxi, createdOn,
                            pendingDate, approvateDate, progressDate, finishDate, description, kmMaintenance, paseTallerDate, createdBy, 
                            paseTallerUser, pendienteUSer, aprobadoUser, progresoUser, terminadoUser,subtotal
                    )
            );
            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true),
                            builder.and(builder.equal(root.get("taxi").get("location"), location)),
                            builder.or(
                                    builder.equal(root.get("serviceOrderType"), ServiceOrderType.PENDING),
                                    builder.equal(root.get("serviceOrderType"), ServiceOrderType.PASE_TALLER)
                            )
                    )
            );
            
            /*query.where(builder.equal(root.get("taxi").get("location"), location));*/
            query.orderBy(builder.asc(root.get("nameNumber")));

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderActive() ", e);
            rollback(transaction);
        }
        return serviceOrders;
    }

    /**
     *
     * @param types
     * @param startDate
     * @param endDate
     * @param location
     * @return
     */
    public List<ServiceOrder> getAllServiceOrderActiveAndStatusIn(List<ServiceOrderType> types,
            Date startDate, Date endDate, Location location) {
        logger.info("getAllServiceOrderActive");

        List<ServiceOrder> serviceOrders = 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);
            Join<ServiceOrder, GeneralPublic> b = root.join("generalPublic", JoinType.LEFT);
            query.multiselect(root, b);
            
            Join<ServiceOrder, User> c = root.join("paseTallerUser", JoinType.LEFT);
            query.multiselect(root, c);
            Join<ServiceOrder, User> d = root.join("pendienteUser", JoinType.LEFT);
            query.multiselect(root, d);
            Join<ServiceOrder, User> e = root.join("aprobadoUser", JoinType.LEFT);
            query.multiselect(root, e);
            Join<ServiceOrder, User> f = root.join("progresoUser", JoinType.LEFT);
            query.multiselect(root, f);
            Join<ServiceOrder, User> j = root.join("terminadoUser", JoinType.LEFT);
            query.multiselect(root, j);
            Join<ServiceOrder, User> k = root.join("payUser", JoinType.LEFT);
            query.multiselect(root, k);

            Path<String> id = root.get("id");
            Path<String> name = root.get("name");
            Path<Integer> nameNumber = root.get("nameNumber");
            Path<Float> km = root.get("km");
            Path<Float> kmMaintenance = root.get("kmMaintenance");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");
            Path<String> createdOn = root.get("createdOn");
            Path<String> pendingDate = root.get("pendingDate");
            Path<String> approvateDate = root.get("approvateDate");
            Path<String> progressDate = root.get("progressDate");
            Path<String> finishDate = root.get("finishDate");
            Path<String> payDate = root.get("payDate");
            Path<String> description = root.get("description");
            Path<User> createdByPath = root.get("createdBy");
            Path<String> paseTallerDate = root.get("paseTallerDate");
            Path<String> paseTaller = root.get("tallerExterno");
            Path<GeneralPublic> general = root.get("generalPublic");
            
            Path<User> paseTallerUser = root.get("paseTallerUser");
            Path<User> pendienteUSer = root.get("pendienteUser");
            Path<User> aprobadoUser = root.get("aprobadoUser");
            Path<User> progresoUser = root.get("progresoUser");
            Path<User> terminadoUser = root.get("terminadoUser");
            Path<User> payUser = root.get("payUser");
            Path<Float> subtotal = root.get("subtotal");

            Expression<ServiceOrderType> expression = root.get("serviceOrderType");

            Predicate inClause = expression.in(types);
            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("createdOn"), startDate);
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("createdOn"), endDate);
            Predicate locationPrm = builder.equal(root.get("taxi").get("location"), location);

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, nameNumber, km, serviceOrderType, taxi, createdOn,
                            pendingDate, approvateDate, progressDate, finishDate, description, kmMaintenance, createdByPath, paseTallerDate, paseTaller, general, 
                            paseTallerUser, pendienteUSer, aprobadoUser, progresoUser, terminadoUser, payDate, payUser,subtotal
                    )
            );
            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true),
                            inClause, initialDate, lastDate,locationPrm
                    )
            );
            query.orderBy(builder.asc(root.get("nameNumber")));

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderActive() ", e);
            rollback(transaction);
        }
        return serviceOrders;
    }
    
    
    public List<ServiceOrder> findServiceOrderPayedByDates(Date startDate, Date endDate, String location) {
        logger.info("getAllServiceOrderActive");

        List<ServiceOrder> serviceOrders = 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);
            Join<ServiceOrder, GeneralPublic> b = root.join("generalPublic", JoinType.LEFT);
            query.multiselect(root, b);
            
            Join<ServiceOrder, User> c = root.join("paseTallerUser", JoinType.LEFT);
            query.multiselect(root, c);
            Join<ServiceOrder, User> d = root.join("pendienteUser", JoinType.LEFT);
            query.multiselect(root, d);
            Join<ServiceOrder, User> e = root.join("aprobadoUser", JoinType.LEFT);
            query.multiselect(root, e);
            Join<ServiceOrder, User> f = root.join("progresoUser", JoinType.LEFT);
            query.multiselect(root, f);
            Join<ServiceOrder, User> j = root.join("terminadoUser", JoinType.LEFT);
            query.multiselect(root, j);
            Join<ServiceOrder, User> k = root.join("payUser", JoinType.LEFT);
            query.multiselect(root, k);

            Path<String> id = root.get("id");
            Path<String> name = root.get("name");
            Path<Integer> nameNumber = root.get("nameNumber");
            Path<Float> km = root.get("km");
            Path<Float> kmMaintenance = root.get("kmMaintenance");
            Path<ServiceOrderType> serviceOrderType = root.get("serviceOrderType");
            Path<Taxi> taxi = root.get("taxi");
            Path<String> createdOn = root.get("createdOn");
            Path<String> pendingDate = root.get("pendingDate");
            Path<String> approvateDate = root.get("approvateDate");
            Path<String> progressDate = root.get("progressDate");
            Path<String> finishDate = root.get("finishDate");
            Path<String> payDate = root.get("payDate");
            Path<String> description = root.get("description");
            Path<User> createdByPath = root.get("createdBy");
            Path<String> paseTallerDate = root.get("paseTallerDate");
            Path<String> paseTaller = root.get("tallerExterno");
            Path<GeneralPublic> general = root.get("generalPublic");
            
            Path<User> paseTallerUser = root.get("paseTallerUser");
            Path<User> pendienteUSer = root.get("pendienteUser");
            Path<User> aprobadoUser = root.get("aprobadoUser");
            Path<User> progresoUser = root.get("progresoUser");
            Path<User> terminadoUser = root.get("terminadoUser");
            Path<User> payUser = root.get("payUser");
            Path<Float> subtotal = root.get("subtotal");
            Path<Float> total = root.get("total");

            Expression<ServiceOrderType> expression = root.get("serviceOrderType");

            Predicate inClause = expression.in(ServiceOrderType.PAYED);
            Predicate initialDate = builder.greaterThanOrEqualTo(root.get("payDate"), startDate);
            Predicate lastDate = builder.lessThanOrEqualTo(root.get("payDate"), endDate);
            Predicate locationPrm = builder.equal(root.get("taxi").get("location").get("id"), location);

            query.select(
                    builder.construct(ServiceOrder.class,
                            id, name, nameNumber, km, serviceOrderType, taxi, createdOn,
                            pendingDate, approvateDate, progressDate, finishDate, description, kmMaintenance, createdByPath, paseTallerDate, paseTaller, general, 
                            paseTallerUser, pendienteUSer, aprobadoUser, progresoUser, terminadoUser, payDate, payUser,subtotal,total
                    )
            );
            query.where(
                    builder.and(
                            builder.equal(root.get("active"), true),
                            inClause, initialDate, lastDate,locationPrm
                    )
            );
            query.orderBy(builder.asc(root.get("nameNumber")));

            serviceOrders = session.createQuery(query).getResultList();

            transaction.commit();
            logger.info("Total of Service Orders loaded " + serviceOrders.size());
        } catch (HibernateException e) {
            logger.error("Service Orders cannot be loaded ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getAllServiceOrderActive() ", e);
            rollback(transaction);
        }
        return serviceOrders;
    }

    /**
     *
     * @return
     */
    public List<Person> fillDriversComboBox() {
        logger.info("Searching all drivers to select..");

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

        try {
            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.and(
                            builder.equal(root.get("personType"), PersonType.DRIVER),
                            builder.equal(root.get("active"), Boolean.TRUE)
                    )
            );

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

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

        return result;
    }
    
    public List<ServiceOrder> fillDataTableDepositPending(String idLocation) {
        logger.info("fillDataTableDepositPending");

        List<ServiceOrder> results = 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);

           
            Predicate pending = builder.equal(root.get("depositStatus"), ExpenseStatusType.PENDING);
            Predicate bankCardNotNull = builder.isNotNull(root.get("bankCard"));

            Predicate locationPredicate = builder.equal(root.get("taxi").get("location"), new Location(idLocation));

            //query.select(builder.construct(Taxes.class, id, amount, description, createdOn, createdByPath, locationPath, activeStatusPath, taxesTypePath));
            query.where(builder.and(
                    pending, bankCardNotNull, locationPredicate
            ));
            query.orderBy(builder.desc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();

            transaction.commit();

            logger.info("Total of paymentClient founds " + results.size());
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Can not load PaymentCllient list", e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("fillDataTableDepositPending", e);
        }

        return results;
    }
    
    public boolean autorizarDeposito(String expenseId, User user) {
        logger.info("autorizar");

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

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

            update.set("depositStatus", ExpenseStatusType.AUTHORIZED);
            update.set("lastUpdatedBy", user);
            update.set("lastUpdatedOn", new Date());
            update.where(builder.equal(rootUpdate.get("id"), expenseId));

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

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Sale " + expenseId + " 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;
    }

    /**
     * Get Service order by Identification.
     *
     * @param id
     * @return
     */
    public ServiceOrder loadServiceOrderById(String id) {
        logger.info("loadServiceOrderById");

        ServiceOrder serviceOrder = null;
        Transaction transaction = null;

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

            transaction = session.beginTransaction();

            serviceOrder = session.get(ServiceOrder.class, id);

            transaction.commit();
            logger.info("Service order loaded");
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Cannot find SO Id = " + id, e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("Method loadServiceOrderById(" + id + ")", e);
        }

        return serviceOrder;
    }

    private static final long serialVersionUID = 1525444638959433303L;

    private final Logger logger = LogManager.getLogger(ServiceOrderController.class);

}
