OrganisationalUnitSupplierRepositoryImpl.java

package com.tradecloud.repository.company;

import com.tradecloud.authentication.MultiTenantUtil;
import com.tradecloud.common.externalreference.ExternalReference;
import com.tradecloud.domain.base.utils.ObjectUtil;
import com.tradecloud.domain.infrastructure.persistence.CriteriaBuilder;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.supplier.*;
import com.tradecloud.dto.company.OrganisationalUnitSupplierSearch;
import com.tradecloud.dto.freetext.FreeTextSearch;
import com.tradecloud.repository.base.impl.RepositoryBaseImpl;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.*;
import org.hibernate.transform.BasicTransformerAdapter;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Repository(value = "organisationalUnitSupplierRepository")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class OrganisationalUnitSupplierRepositoryImpl extends RepositoryBaseImpl<OrganisationalUnitSupplier, OrganisationalUnitSupplierSearch>
        implements OrganisationalUnitSupplierRepository {

    private static final long serialVersionUID = 1L;

    private final static String NAME = "s.name";

    private final static String EXTERNAL_REFERENCE = "s.externalReference";

    @Override
    public OrganisationalUnitSupplier findByName(String name) {
        List<OrganisationalUnitSupplier> all = super.findAll();
        for (OrganisationalUnitSupplier organisationalUnitSupplier : all) {
            if (organisationalUnitSupplier.getSupplier().getName().equals(name)) {
                return organisationalUnitSupplier;
            }
        }
        return null;
    }

    @Override
    public List<OrganisationalUnitSupplier> search(OrganisationalUnitSupplierSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(getPersistentClass());
        addSearchRestrictions(criteria, search);
        return getExecutableCriteriaList(criteria, search.getSearchMetaParams());
    }

    @Override
    public long count(OrganisationalUnitSupplierSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(getPersistentClass());
        addSearchRestrictions(criteria, search);
        return getExecutableCriteriaCount(criteria);
    }

    private void addSearchRestrictions(DetachedCriteria criteria, OrganisationalUnitSupplierSearch search) {
        DetachedCriteria supplier = criteria.createCriteria("supplier");
        if (search.getState() != null) {
            supplier.add(Restrictions.eq("state", search.getState()));
        }
        if (search.getComplete() != null) {
            // Active will be true by default in the search.
            if (CompleteType.COMPLETE.equals(search.getComplete())) {
                supplier.add(Restrictions.eq("complete", true));
            } else {
                supplier.add(Restrictions.eq("complete", false));
            }
        }
        if (search.getSupplierState() != null) {
            supplier.add(Restrictions.eq("state", search.getSupplierState()));
        }
        if (search.getIntegrated() != null) {
            // Integrated will be true by default in the search.
            if (IntegratedType.INTEGRATED.equals(search.getIntegrated())) {
                supplier.add(Restrictions.eq("integrated", true));
            } else {
                supplier.add(Restrictions.eq("integrated", false));
            }
        }
        if (search.getTypes() != null && !search.getTypes().isEmpty()) {
            supplier.add(Restrictions.in("type", search.getTypes()));
        } else {
            supplier.add(Restrictions.eq("type", AbstractSupplier.Type.FULL));
        }
        if (search.getName() != null) {
            supplier.add(Restrictions.ilike("name", LIKE + search.getName() + LIKE));
        }

        if (search.getReference() != null) {
            if (!search.isUnique()) {
                supplier.add(Restrictions.ilike("externalReference", LIKE + search.getReference() + LIKE));
            } else {
                supplier.add(Expression.eq("externalReference", search.getReference()).ignoreCase());
            }
        }

        if (search.getCreatedFrom() != null && search.getCreatedTo() != null) {
            supplier.add(Restrictions.between("created", search.getCreatedFrom(), search.getCreatedTo()));
        }

        if (search.getCountry() != null) {
            DetachedCriteria eventCrit = supplier.createCriteria("physicalAddress").add(Restrictions.eq("country", search.getCountry()));
            eventCrit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        }

        if (search.getCurrency() != null) {
            supplier.add(Restrictions.eq("currency", search.getCurrency()));
        }


        if (search.getSupplier() != null) {
            criteria.add(Restrictions.eq("supplier", search.getSupplier()));
        }
        if (CollectionUtils.isNotEmpty(search.getOrganisationalUnitList())) {
            criteria.add(Restrictions.in("organisationalUnit", search.getOrganisationalUnitList()));
        } else if (search.getOrganisationalUnit() != null) {
            if (search.isLiteSearch()) {
                Junction conditionGroup = Restrictions.disjunction();
                for (OrganisationalUnit organisationalUnit : search.getOrganisationalUnit().getChildren()) {
                    conditionGroup.add(Restrictions.eq("organisationalUnit", organisationalUnit));
                }
                conditionGroup.add(Restrictions.eq("organisationalUnit", search.getOrganisationalUnit()));
                if (search.getOrganisationalUnit().getParent() != null) {
                    conditionGroup.add(Restrictions.eq("organisationalUnit", search.getOrganisationalUnit().getParent()));
                }
                criteria.add(conditionGroup);
            } else {
                criteria.add(Restrictions.eq("organisationalUnit", search.getOrganisationalUnit()));
            }
        }
    }

    @Override
    public OrganisationalUnitSupplier findByIdOrgUnitAndSupplier(OrganisationalUnit orgUnit, Supplier supplier) {
        List<OrganisationalUnitSupplier> organisationalUnitSuppliers = (List<OrganisationalUnitSupplier>)
                findByNamedQueryAndNamedParam("findByIdOrgUnitAndSupplier",
                        new String[]{"organisationalUnit", "supplier"}, new Object[]{orgUnit, supplier});
        return ObjectUtil.first(organisationalUnitSuppliers);
    }

    @Override
    public OrganisationalUnitSupplier findByIdOrgUnitAndSupplierAllowNull(OrganisationalUnit organisationalUnit, Supplier supplier) {
        List<OrganisationalUnitSupplier> organisationalUnitSuppliers = (List<OrganisationalUnitSupplier>)
                findByNamedQueryAndNamedParam("findByIdOrgUnitAndSupplierWhereNullAllowed",
                        new String[]{"organisationalUnit", "supplier"}, new Object[]{organisationalUnit, supplier});
        return ObjectUtil.first(organisationalUnitSuppliers);
    }

    private Criterion createFreeTextCriterion(FreeTextSearch search) {
        return Restrictions.or(Restrictions.like(NAME, search.getText()), Restrictions.like(EXTERNAL_REFERENCE, search.getText()));
    }

    @Override
    public List<OrganisationalUnitSupplier> findBySupplier(Supplier supplier) {
        return findBySupplier(supplier, false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findBySupplier(Supplier supplier, boolean includeInactive) {
        return findBySupplier(supplier, includeInactive, false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findBySupplier(Supplier supplier, boolean includeInactive, boolean includeSuspended) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        DetachedCriteria criteriaOrgUnit = criteriaOrgUnitSupplier.createCriteria("organisationalUnit");
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        CriteriaBuilder.addExactMatch(criteriaOrgUnitSupplier, "supplier", supplier);

        addSupplierStateRestriction(criteriaSupplier, includeInactive, includeSuspended);

        List<OrganisationalUnitSupplier> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        return results;
    }

    @Override
    public List<OrganisationalUnitSupplier> searchFreeText(FreeTextSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        criteria.createAlias("supplier", "s");
        criteria.add(createFreeTextCriterion(search));
        return getExecutableCriteriaList(criteria, search.getSearchMetaParams());
    }

    @Override
    public long countFreeText(FreeTextSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        criteria.add(createFreeTextCriterion(search));
        return getExecutableCriteriaCount(criteria);
    }

    @Override
    public List<OrganisationalUnitSupplier> getAllUsedOrgUnitSuppliersByCommercialInvoice() {
        String queryString = "from OrganisationalUnitSupplier ous where ous.id in (select ci.supplier.id from CommercialInvoice ci)";
        Query query = getCurrentSession().createQuery(queryString);
        return (List<OrganisationalUnitSupplier>) query.list();
    }

    @Override
    public List<OrganisationalUnitSupplier> getAllUsedOrgUnitSuppliersByCreditNote() {
        String queryString = "from OrganisationalUnitSupplier ous where ous.id in (select ccn.supplier.id from CommercialCreditNote ccn)";
        Query query = getCurrentSession().createQuery(queryString);
        return (List<OrganisationalUnitSupplier>) query.list();
    }

    @Override
    public List<OrganisationalUnitSupplier> findByOrganisationalUnit(OrganisationalUnit organisationalUnit) {
        return findByOrganisationalUnit(organisationalUnit, false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findByOrganisationalUnit(OrganisationalUnit organisationalUnit, boolean includeInactive) {
        return findByOrganisationalUnit(organisationalUnit, includeInactive, false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findByOrganisationalUnit(OrganisationalUnit organisationalUnit,
                                                                     boolean includeInactive,
                                                                     boolean includeSuspended) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        DetachedCriteria criteriaOrgUnit = criteriaOrgUnitSupplier.createCriteria("organisationalUnit");
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        CriteriaBuilder.addEqRestriction(criteriaOrgUnit, "code", organisationalUnit.getCode());

        addSupplierStateRestriction(criteriaSupplier, includeInactive, includeSuspended);

        List<OrganisationalUnitSupplier> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        return results;
    }

    @Override
    public List<OrganisationalUnitSupplier> findAll() {
        return findAll(false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findAll(boolean includeInactive) {
        return findAll(includeInactive, false);
    }

    @Override
    public List<OrganisationalUnitSupplier> findAll(boolean includeInactive, boolean includeSuspended) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        addSupplierStateRestriction(criteriaSupplier, includeInactive, includeSuspended);

        List<OrganisationalUnitSupplier> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        return results;
    }

    @Override
    public OrganisationalUnitSupplier findByOrgUnitCodeAndSupplierName(String orgUnitCode, String supplierName, String externalRef) {
        return findByOrgUnitCodeAndSupplierName(orgUnitCode, supplierName, externalRef, false, false);
    }

    @Override
    public OrganisationalUnitSupplier findByOrgUnitCodeAndSupplierName(String orgUnitCode, String supplierName,
                                                                       String externalRef, boolean includeInactive, boolean includeSuspended) {
        return findByOrgUnitCodeAndSupplierNameOrReference(orgUnitCode, supplierName, externalRef, includeInactive, includeSuspended);
    }

    @Override
    public OrganisationalUnitSupplier findByOrgUnitCodeAndSupplierReference(String orgUnitCode, String supplierReference, boolean includeInactive) {
        return findByOrgUnitCodeAndSupplierNameOrReference(orgUnitCode, null, supplierReference, includeInactive, false);
    }

    @Override
    public OrganisationalUnitSupplier findByOrgUnitCodeAndSupplierNameOrReference(String orgUnitCode, String supplierName, String supplierReference,
                                                                                  boolean includeInactive, boolean includeSuspended) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        DetachedCriteria criteriaOrgUnit = criteriaOrgUnitSupplier.createCriteria("organisationalUnit");
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        if (MultiTenantUtil.getActiveUser().getIntegratedSystem() != null) {
            DetachedCriteria eventCrit = criteriaOrgUnit.createCriteria("externalReferences")
                    .add(Restrictions.eq("integratedSystem", MultiTenantUtil.getActiveUser().getIntegratedSystem()))
                    .add(Restrictions.eq("referenceValue", orgUnitCode));
        } else {
            CriteriaBuilder.addEqRestriction(criteriaOrgUnit, "code", orgUnitCode);
            CriteriaBuilder.addDisjunction(criteriaOrgUnit, "code", orgUnitCode);
        }

        if (supplierName != null) {
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "name", supplierName);
        }
        if (supplierReference != null) {
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "externalReference", supplierReference);
        }

        addSupplierStateRestriction(criteriaSupplier, includeInactive, includeSuspended);

        List<OrganisationalUnitSupplier> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        if (results != null && results.size() > 1)
            throw new RuntimeException("only one org with code and supplier name should exist.");
        return (results != null && !results.isEmpty()) ? results.get(0) : null;
    }

    private void addSupplierStateRestriction(DetachedCriteria criteriaSupplier, boolean includeInactive, boolean includeSuspended) {
        List<SupplierState> supplierStates = new ArrayList<SupplierState>();
        supplierStates.addAll(Arrays.asList(SupplierState.values()));

        if (!includeInactive) {
            supplierStates.remove(SupplierState.INACTIVE);
            supplierStates.remove(SupplierState.LIQUIDATED);
            supplierStates.remove(SupplierState.SUSPENDED);
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "complete", true);
        }

        if (!includeSuspended) {
            if (supplierStates.contains(SupplierState.SUSPENDED)) {
                supplierStates.remove(SupplierState.SUSPENDED);
            }
        } else {
            if (!supplierStates.contains(SupplierState.SUSPENDED)) {
                supplierStates.add(SupplierState.SUSPENDED);
            }
        }

        if (!supplierStates.isEmpty()) {
            CriteriaBuilder.addDisjunctionIn(criteriaSupplier, "state", supplierStates);
        }
    }

    @Override
    public List<OrganisationalUnitSupplier> findByOrgUnitCodeAndSupplierNameOrReferenceList(String orgUnitCode,
                                                                                            String supplierName, String supplierReference,
                                                                                            boolean includeInactive) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        DetachedCriteria criteriaOrgUnit = criteriaOrgUnitSupplier.createCriteria("organisationalUnit");
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        if (MultiTenantUtil.getActiveUser().getIntegratedSystem() != null) {
            DetachedCriteria eventCrit = criteriaOrgUnit.createCriteria("externalReferences")
                    .add(Restrictions.eq("integratedSystem", MultiTenantUtil.getActiveUser().getIntegratedSystem()))
                    .add(Restrictions.eq("referenceValue", orgUnitCode));
        } else {
            CriteriaBuilder.addEqRestriction(criteriaOrgUnit, "code", orgUnitCode);
            CriteriaBuilder.addDisjunction(criteriaOrgUnit, "code", orgUnitCode);
        }

        if (supplierName != null) {
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "name", supplierName);
        }
        if (supplierReference != null) {
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "externalReference", supplierReference);
        }

        if (!includeInactive) {
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "state", SupplierState.ACTIVE);
            CriteriaBuilder.addEqRestriction(criteriaSupplier, "complete", true);
        }

        List<OrganisationalUnitSupplier> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        return results;
    }

    // This should be the same as findBySupplier(,,), but returns just the names. Used on the UI and report
    @Override
    public List<String> findOrgUnitNamesBySupplier(Supplier supplier, boolean includeInactive, boolean includeSuspended) {
        DetachedCriteria criteriaOrgUnitSupplier = DetachedCriteria.forClass(OrganisationalUnitSupplier.class);
        //DetachedCriteria criteriaOrgUnit = criteriaOrgUnitSupplier.createCriteria("organisationalUnit");
        DetachedCriteria criteriaSupplier = criteriaOrgUnitSupplier.createCriteria("supplier");

        CriteriaBuilder.addEqRestriction(criteriaOrgUnitSupplier, "supplier", supplier);

        addSupplierStateRestriction(criteriaSupplier, includeInactive, includeSuspended);

        criteriaOrgUnitSupplier.createAlias("organisationalUnit", "ou", CriteriaSpecification.LEFT_JOIN);
//        criteriaOrgUnitSupplier.add(Restrictions.sqlRestriction("ou"));
        Projection projection = Projections.projectionList().
                add(Projections.property("ou.name"), "1");
        criteriaOrgUnitSupplier.setProjection(projection);

        criteriaOrgUnitSupplier.setResultTransformer(new BasicTransformerAdapter() {
            @Override
            public Object transformTuple(Object[] tuple, String[] aliases) {
                return (String) tuple[0];
            }
        });

        List<String> results = criteriaOrgUnitSupplier.getExecutableCriteria(getSession()).list();
        return results;
    }

    @Override
    protected OrganisationalUnitSupplier filterResult(ExternalReference externalReference, List<OrganisationalUnitSupplier> results) {
        if (results.size() > 1) {
            Set<OrganisationalUnitSupplier> suppliers = results.stream().filter(ous -> ous.getActive() == Boolean.TRUE).collect(Collectors.toSet());
            if (suppliers.size() > 1) {
                return failDueToDuplicateResults(externalReference, results);
            } else {
                return suppliers.iterator().next();
            }
        } else if (results.size() == 1) {
            return results.iterator().next();
        } else {
            return null;

        }
    }
}