PaymentRepositoryImpl.java

package com.tradecloud.repository.impl;

import com.tradecloud.domain.document.invoice.CommercialInvoice;
import com.tradecloud.domain.model.ordermanagement.Order;
import com.tradecloud.domain.settlement.Payment;
import com.tradecloud.domain.settlement.PlannedSettlement;
import com.tradecloud.repository.PaymentRepository;
import com.tradecloud.repository.base.impl.RepositoryBaseImpl;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.math.BigDecimal;
import java.util.List;

/**
 * Default implementation of the {@code PaymentRepository} interface.
 */
@Repository
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class PaymentRepositoryImpl extends RepositoryBaseImpl<Payment, Object> implements PaymentRepository {

    private static final long serialVersionUID = 1L;

    @Override
    public List<Payment> findByPlannedSettlement(PlannedSettlement plannedSettlement) {
        @SuppressWarnings("unchecked")
        List<Payment> results =
                (List<Payment>) getNamedQueryAndNamedParam("payment.findByPlannedSettlement",
                        "plannedSettlement", plannedSettlement);
        return results;
    }

    @Override
    public List<Payment> findByOrdersLinkedToInvoice(List<Order> orders) {
        if (orders == null || orders.isEmpty()) {
            throw new IllegalArgumentException("At least one order is required");
        }
        Long[] ids = new Long[orders.size()];
        int i = 0;
        for (Order order : orders) {
            ids[i++] = order.getId();
        }
        /*
         * This sub-query retrieves the IDs of all PlannedSettlements associated to any ID of the Orders supplied. The resulting list will be used in
         * the actual query to find payments, next.
         */
        DetachedCriteria orderSubQuery =
                DetachedCriteria.forClass(Order.class, "order").createAlias("plannedSettlements", "ps").add(Restrictions.in("order.id", ids))
                        .setProjection(Projections.projectionList().add(Projections.property("ps.id")));
        /**
         * This query finds all payments associated with planned settlements whose IDs are in the list retrieved in the previous sub-query. 
         *
         * Note: The setFetchMode("paymentMethod") is to make that association lazy-loaded as we don't need to load it here. The Hibernate docs for
         * FetchMode.SELECT are ambiguous though. They say "Fetch eagerly, using a separate select.". But in the docs for, the now deprecated,
         * FetchMode.LAZY it says "Fetch lazily. Deprecated, use FetchMode.SELECT". See: https://hibernate.atlassian.net/browse/HHH-980
         */
        Criteria paymentCriteria =
                getSessionCustom().createCriteria(Payment.class, "payment").createAlias("plannedSettlement", "pst")
                        .setFetchMode("paymentMethod", FetchMode.SELECT).add(Property.forName("pst.id").in(orderSubQuery));

        @SuppressWarnings("unchecked")
        List<Payment> results = paymentCriteria.list();
        return results;
    }

    @Override
    public BigDecimal getTotalAmountPaidOnPlannedSettlement(PlannedSettlement plannedSettlement) {
        Object uniqueResult =
                getCurrentSession().createCriteria(Payment.class).add(Restrictions.eq("plannedSettlement", plannedSettlement))
                        .setProjection(Projections.projectionList().add(Projections.sum("amount").as("value"))).uniqueResult();

        if (uniqueResult == null) {
            return BigDecimal.ZERO.setScale(4);
        }
        return (BigDecimal) uniqueResult;
    }

    @Override
    public Order getOrderForPayment(Payment payment) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<Order> criteriaQuery = criteriaBuilder.createQuery(Order.class);
        Root<Order> root = criteriaQuery.from(Order.class);

        Join<Order, PlannedSettlement> join = root.join("plannedSettlements");
        Path path = join.get("id");
        Predicate predicate = criteriaBuilder.equal(path, payment.getPlannedSettlement().getId());

        criteriaQuery.where(predicate);
        org.hibernate.query.Query<Order> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public CommercialInvoice getCIForPayment(Payment payment) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<CommercialInvoice> criteriaQuery = criteriaBuilder.createQuery(CommercialInvoice.class);
        Root<CommercialInvoice> root = criteriaQuery.from(CommercialInvoice.class);

        Join<CommercialInvoice, PlannedSettlement> join = root.join("plannedSettlements");
        Path path = join.get("id");
        Predicate predicate = criteriaBuilder.equal(path, payment.getPlannedSettlement().getId());

        criteriaQuery.where(predicate);
        org.hibernate.query.Query<CommercialInvoice> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public Payment findByERPPaymentId(String erpPaymentId) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<Payment> criteriaQuery = criteriaBuilder.createQuery(Payment.class);
        Root<Payment> c = criteriaQuery.from(Payment.class);
        criteriaQuery = criteriaQuery.select(c).where(criteriaBuilder.equal(c.get("erpPaymentId"), erpPaymentId));
        org.hibernate.query.Query<Payment> q = getSession().createQuery(criteriaQuery);
        return q.uniqueResult();
    }

    @Override
    public boolean alreadyReversed(Payment payment) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<Payment> criteriaQuery = criteriaBuilder.createQuery(Payment.class);
        Root<Payment> c = criteriaQuery.from(Payment.class);
        Predicate reallocationDestinationId = criteriaBuilder.equal(c.get("reallocationDestinationId"), payment.getId());
        Predicate andType = criteriaBuilder.equal(c.get("type"), Payment.Type.REALLOCATION_REVERSAL);
        Predicate finalQuery = criteriaBuilder.and(reallocationDestinationId, andType);

        criteriaQuery = criteriaQuery.select(c).where(finalQuery);
        org.hibernate.query.Query<Payment> q = getSession().createQuery(criteriaQuery);
        List<Payment> list = q.list();
        if (list == null || list.isEmpty())
            return false;
        return true;
    }
}