LetterOfCreditGeneralRepositoryImpl.java

package com.tradecloud.repository.impl;

import com.tradecloud.authentication.MultiTenantUtil;
import com.tradecloud.domain.base.utils.DateRange;
import com.tradecloud.domain.base.utils.DateUtils;
import com.tradecloud.domain.infrastructure.persistence.CriteriaBuilder;
import com.tradecloud.domain.letterofcredit.LetterOfCredit;
import com.tradecloud.domain.letterofcredit.LetterOfCreditState;
import com.tradecloud.domain.letterofcredit.LetterOfCreditTemplate;
import com.tradecloud.domain.model.ordermanagement.PurchaseOrder;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.supplier.OrganisationalUnitSupplier;
import com.tradecloud.dto.letterofcredit.LetterOfCreditSearch;
import com.tradecloud.dto.letterofcredit.LetterOfCreditTemplateSearch;
import com.tradecloud.repository.LetterOfCreditGeneralRepository;
import com.tradecloud.repository.SearchMetaParams;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.*;
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.*;

/**
 * @author jon
 */
@Repository(value = "letterOfCreditGeneralRepository")
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class LetterOfCreditGeneralRepositoryImpl extends GeneralRepositoryImpl implements LetterOfCreditGeneralRepository {

    private final static String NUMBER = "number";
    private final static String BANK_REFERENCE = "bankReference";
    private final static String APPLICATION_REFERENCE = "applicationReference";

    private final static String STATE = "state";
    private final static String KEY = "key";
    private final static String NAME = "name";
    private final static String DESCRIPTION = "description";
    private final static String ISSUING_BANK = "issuingBank";
    private final static String BANK = "owningEntity";
    private final static String SUPPLIER = "beneficiary";
    private final static String OUT_OF_SYNC_STATE = "outOfSyncState";

    private final static String CREATED_DATE = "createdDate";
    private final static String LATEST_SHIPMENT_DATE = "shippingInfo.latestShipmentDate";
    private final static String SHIPMENT_DATE = "s.latestShipmentDate";

    private final static String ORG_UNIT = "applicant";
    private final static String ORGANISATIONAL_UNIT_UNIT = "po.organisationalUnit";
    private final static String ORGANISATIONAL_UNIT_PARENT = "po.organisationalUnit.parent";
    private final static String SUPPLIER_ORG_ORG = "po.supplier";
    //private final static String ORGANISATIONAL_UNIT = "po.organisationalUnit";
    //private final static String SUPPLIER_ORG = "po.supplier";
    //private final static String ORGANISATIONAL_UNIT = "beneficiary";
    private final static String SUPPLIER_ORG = "beneficiary.organisationalUnit";

    private void addSearchRestrictions(DetachedCriteria criteria, LetterOfCreditSearch search) {
        // TODO. Add others.
        criteria.createAlias("shippingInfo", "s");
        criteria.createAlias("purchaseOrders", "po");
        criteria.createAlias("po.organisationalUnit", "ou");
        CriteriaBuilder.addiLikeAnyRestriction(criteria, NUMBER, search.getNumber());
        CriteriaBuilder.addiLikeAnyRestriction(criteria, BANK_REFERENCE, search.getBankReference());
        CriteriaBuilder.addiLikeAnyRestriction(criteria, APPLICATION_REFERENCE, search.getApplicationReference());
        CriteriaBuilder.addEqRestriction(criteria, ISSUING_BANK, search.getBank());
        CriteriaBuilder.addEqRestriction(criteria, OUT_OF_SYNC_STATE, search.getOutOfSyncState());
        CriteriaBuilder.addEqActiveStateRestriction(criteria, STATE, search.getState(), LetterOfCreditState.DELETED);

        CriteriaBuilder.addDateRangeCriteria(criteria, CREATED_DATE, search.getCreatedDate());

        //CriteriaBuilder.addEqRestriction(criteria, SUPPLIER_ORG, search.getSupplier());
        CriteriaBuilder.addEqRestriction(criteria, SUPPLIER, search.getSupplier());
        //if (search.getOrganisationalUnit() != null && search.getOrganisationalUnit().getTier().getName() != null) {
        if (search.getOrganisationalUnit() != null) {
            //Ignore org unit specified if the org unit is the holding company.
            //In which case one would search under all org units.
            if (!search.getOrganisationalUnit().getTier().getName().equals("HoldingCompany")) {
                if (search.getOrganisationalUnit().getTier().getName().equals("BUSINESS_UNIT")) {
                    CriteriaBuilder.addEqRestriction(criteria, "ou.parent", search.getOrganisationalUnit());
                } else {
                    CriteriaBuilder.addEqRestriction(criteria, ORGANISATIONAL_UNIT_UNIT, search.getOrganisationalUnit());
                }
            }
        }
        CriteriaBuilder.addDateRangeCriteria(criteria, SHIPMENT_DATE, search.getLatestShipmentDate());
    }

    /*@Override
    public List<LetterOfCredit> search(LetterOfCreditSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCredit.class);
        criteria.addOrder(Order.desc("created"));
        addSearchRestrictions(criteria, search);
        return getExecutableCriteriaList(criteria, LetterOfCredit.class, search.getSearchMetaParams());
    }*/

    private Criteria createLetterOfCreditCriteria(LetterOfCreditSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCredit.class);
        criteria.createAlias("shippingInfo", "s");
        criteria.createAlias("purchaseOrders", "po");
        criteria.createAlias("po.organisationalUnit", "ou");

        Set<OrganisationalUnit> organisationalUnits = Collections.EMPTY_SET;
        if (search.isFilteredByUserOrg()) {
            organisationalUnits = getUserOrganisationalUnits();
            criteria.add(Restrictions.in("po.organisationalUnit", organisationalUnits));
        } else if (search.getOrganisationalUnit() != null) {
            //Ignore org unit specified if the org unit is the holding company.
            //In which case one would search under all org units.
            if (!search.getOrganisationalUnit().getTier().getName().equals("HoldingCompany")) {
                if (search.getOrganisationalUnit().getTier().getName().equals("BUSINESS_UNIT")) {
                    criteria.add(Restrictions.eq("ou.parent", search.getOrganisationalUnit()));
                } else {
                    criteria.add(Restrictions.eq(ORGANISATIONAL_UNIT_UNIT, search.getOrganisationalUnit()));
                }
            }
        }

        if (search.getNumber() != null) {
            criteria.add(Restrictions.ilike(NUMBER, search.getNumber(), MatchMode.ANYWHERE));
        }
        if (search.getBankReference() != null) {
            criteria.add(Restrictions.ilike(BANK_REFERENCE, search.getBankReference(), MatchMode.ANYWHERE));
        }
        if (search.getApplicationReference() != null) {
            criteria.add(Restrictions.ilike(APPLICATION_REFERENCE, search.getApplicationReference(), MatchMode.ANYWHERE));
        }
        if (search.getBank() != null) {
            criteria.add(Restrictions.eq(ISSUING_BANK, search.getBank()));
        }
        if (search.getOutOfSyncState() != null) {
            criteria.add(Restrictions.eq(OUT_OF_SYNC_STATE, search.getOutOfSyncState()));
        }
        if (search.getState() != null) {
            criteria.add(Restrictions.eq(STATE, search.getState()));
        } else {
            criteria.add(Restrictions.not(Restrictions.eq(STATE, LetterOfCreditState.DELETED)));
        }
        if (search.getSupplier() != null) {
            criteria.add(Restrictions.eq(SUPPLIER, search.getSupplier()));
        }

        DateRange createdDateRange = search.getCreatedDate();
        if (createdDateRange != null) {
            if (createdDateRange.getFrom() != null) {
                criteria.add(Restrictions.ge(CREATED_DATE, DateUtils.getStartOfDay(createdDateRange.getFrom())));
            }
            if (createdDateRange.getTo() != null) {
                criteria.add(Restrictions.le(CREATED_DATE, DateUtils.getEndOfDay(createdDateRange.getTo())));
            }
        }

        DateRange lsdDateRange = search.getLatestShipmentDate();
        if (lsdDateRange != null) {
            if (lsdDateRange.getFrom() != null) {
                criteria.add(Restrictions.ge(SHIPMENT_DATE, DateUtils.getStartOfDay(lsdDateRange.getFrom())));
            }
            if (lsdDateRange.getTo() != null) {
                criteria.add(Restrictions.le(SHIPMENT_DATE, DateUtils.getEndOfDay(lsdDateRange.getTo())));
            }
        }

        //search returns multiple results of the same entities due to the purchase order table alias which cannot be removed.
        criteria.setProjection(Projections.distinct(Projections.property("id")));

        Criteria c = getSessionCustom().createCriteria(LetterOfCredit.class);
        c.add(Subqueries.propertyIn("id", criteria));

        return c;
    }

    protected Set<OrganisationalUnit> getUserOrganisationalUnits() {
        return Optional.ofNullable(MultiTenantUtil.getActiveUser().getOrganisationalUnits()).orElseThrow(() ->
                new IllegalStateException("filter by org is true, but organisational unit is not set"));
    }

    @Override
    public List<LetterOfCredit> search(LetterOfCreditSearch search) {
        Criteria criteria = createLetterOfCreditCriteria(search);

        SearchMetaParams searchMetaParams = search.getSearchMetaParams();

        if (searchMetaParams != null) {
            criteria.setMaxResults(searchMetaParams.getRowCount());
            criteria.setFirstResult(searchMetaParams.getRowIndex());
            criteria.addOrder(Order.desc(CREATED_DATE));
        } else {
            criteria.addOrder(Order.desc(CREATED_DATE));
        }

        return criteria.list();

    }

    @Override
    public List<LetterOfCredit> searchByNumber(String lcNumber) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCredit.class);
        getSessionCustom().createCriteria(LetterOfCredit.class).list();
        criteria.addOrder(Order.desc("created"));
        CriteriaBuilder.addEqRestriction(criteria, NUMBER, lcNumber);
        return getExecutableCriteriaList(criteria, LetterOfCredit.class);
    }

    /*@Override
    public long count(LetterOfCreditSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCredit.class);
        addSearchRestrictions(criteria, search);
        return getExecutableCriteriaCount(criteria);
    }*/

    @Override
    public long count(LetterOfCreditSearch search) {
        Criteria criteria = createLetterOfCreditCriteria(search);

        criteria.setProjection(Projections.rowCount());

        long count = ((Long) criteria.uniqueResult()).longValue();

        return count;
    }

    @Override
    public List<LetterOfCreditTemplate> searchTemplates(LetterOfCreditTemplateSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCreditTemplate.class);
        criteria.addOrder(Order.desc("created"));
        if (search.getSupplier() != null) {
            criteria.createAlias("selectedSuppliers", "supplier");
        }

        // TODO. Add others.
        CriteriaBuilder.addiLikeAnyRestriction(criteria, KEY, search.getKey());
        CriteriaBuilder.addiLikeAnyRestriction(criteria, NAME, search.getName());
        CriteriaBuilder.addiLikeAnyRestriction(criteria, DESCRIPTION, search.getDescription());
        CriteriaBuilder.addEqRestriction(criteria, STATE, search.getState());
        CriteriaBuilder.addEqRestriction(criteria, BANK, search.getOwningEntity());
        if (search.getSupplier() != null) {
            CriteriaBuilder.addEqRestriction(criteria, "supplier.id", search.getSupplier().getId());
        }

        return getExecutableCriteriaList(criteria, LetterOfCreditTemplate.class);
    }

    @Override
    public List<LetterOfCreditTemplate> searchTemplatesExact(LetterOfCreditTemplateSearch search) {
        DetachedCriteria criteria = DetachedCriteria.forClass(LetterOfCreditTemplate.class);
        criteria.addOrder(Order.desc("created"));
        if (search.getSupplier() != null) {
            criteria.createAlias("selectedSuppliers", "supplier");
        }

        // TODO. Add others.
        CriteriaBuilder.addEqRestriction(criteria, KEY, search.getKey());
        CriteriaBuilder.addEqRestriction(criteria, NAME, search.getName());
        CriteriaBuilder.addEqRestriction(criteria, DESCRIPTION, search.getDescription());
        CriteriaBuilder.addEqRestriction(criteria, STATE, search.getState());
        CriteriaBuilder.addEqRestriction(criteria, BANK, search.getOwningEntity());
        if (search.getSupplier() != null) {
            CriteriaBuilder.addEqRestriction(criteria, "supplier.id", search.getSupplier().getId());
        }

        return getExecutableCriteriaList(criteria, LetterOfCreditTemplate.class);
    }

    @Override
    public List<OrganisationalUnitSupplier> findLCreditTemplateOrganisationalUnitSuppliers(long lcTemplateId) {

        String queryString =
                "select * from OrganisationalUnitSupplier org join lctemplate_orgsupplier lc on "
                        + "org.id = lc.orgsupplier_id where lc.lctemplate_id = :lcTemplateId";

        return (List<OrganisationalUnitSupplier>) getCurrentSession().createSQLQuery(queryString).addEntity(OrganisationalUnitSupplier.class)
                .setParameter("lcTemplateId", lcTemplateId).list();

    }

    @Override
    public List<String> getItemDescriptions(Collection<PurchaseOrder> purchaseOrders) {
        String queryString = "SELECT description FROM LineItem li WHERE li.order IN (:orders) ORDER BY addedToOrderDate ASC";

        Query query = getSessionCustom().createQuery(queryString);
        query.setMaxResults(5);
        query.setParameterList("orders", purchaseOrders);
        return query.list();
    }

    @Override
    public LetterOfCredit findLetterOfCreditByOrder(Long orderId) {
        Criteria criteria = getSessionCustom().createCriteria(LetterOfCredit.class);
        criteria.createAlias("purchaseOrders", "po");
        criteria.add(Restrictions.eq("po.id", orderId));

        return (LetterOfCredit) criteria.uniqueResult();
    }

    @Override
    public boolean isIntegrated(PurchaseOrder purchaseOrder) {
        String queryString = "SELECT lc.letterOfCreditTemplate.integrated FROM LetterOfCredit lc JOIN lc.purchaseOrders po WHERE po = :purchaseOrder";
        Query query = getSessionCustom().createQuery(queryString);
        query.setParameter("purchaseOrder", purchaseOrder);
        Object result = query.uniqueResult();
        return result != null ? (Boolean)result : false;
    }
}