BaseClearingInstructionRepositoryImpl.java

package com.tradecloud.repository.impl;

import com.tradecloud.domain.base.utils.ObjectUtil;
import com.tradecloud.domain.document.invoice.ActualLineItem;
import com.tradecloud.domain.shipment.Shipment;
import com.tradecloud.domain.shipment.clearing.BaseClearingInstruction;
import com.tradecloud.domain.shipment.clearing.ClearingInstruction;
import com.tradecloud.domain.shipment.clearing.CustomsDeclaration;
import com.tradecloud.dto.clearing.BaseClearingInstructionSearch;
import com.tradecloud.repository.BaseClearingInstructionRepository;
import com.tradecloud.repository.base.impl.RepositoryBaseImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;
import org.hibernate.transform.DistinctRootEntityResultTransformer;
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.BigInteger;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

@Repository
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public class BaseClearingInstructionRepositoryImpl extends RepositoryBaseImpl<BaseClearingInstruction, BaseClearingInstructionSearch>
        implements BaseClearingInstructionRepository {

    @Override
    public List<BaseClearingInstruction> search(BaseClearingInstructionSearch search) {
        CriteriaBuilder criteriaBuilder = getSession().getCriteriaBuilder();
        CriteriaQuery<BaseClearingInstruction> criteriaQuery = criteriaBuilder.createQuery(BaseClearingInstruction.class);
        Root<BaseClearingInstruction> c = criteriaQuery.from(BaseClearingInstruction.class);
        List<Predicate> predicates = new ArrayList<>();
        //Date Range Search Restrictions
        if (ObjectUtil.allNotNull(search.getFromDate(), search.getToDate())) {
            Join<BaseClearingInstruction, Shipment> shipment = c.join("shipment", JoinType.INNER);
            predicates.add(criteriaBuilder.between(
                    shipment.get("estimatedArrivalDateAtPlaceOfDischarge"), search.getFromDate(), search.getToDate()));
        }
        //Status Search Criteria
        if (search.getSelectedStatuses() != null && search.getSelectedStatuses().length > 0) {
            predicates.add(c.get("status").in(Arrays.asList(search.getSelectedStatuses())));
        }
        return CollectionUtils.isNotEmpty(predicates)
                ? getSession().createQuery(criteriaQuery.select(c).where(predicates.toArray(new Predicate[]{}))).getResultList()
                : getSession().createQuery(criteriaQuery.select(c)).getResultList();
    }

    @Override
    public CustomsDeclaration findDeclarationBy(Long number, LocalDate date, String placeOfCustomEntryCode) {
/*        String sql = """
                select cd.*, ce.*, se.*, pc.* from customsdeclaration cd
                left join CustomsDeclaration_clearingevent cd_ce on cd_ce.customsdeclaration_id = cd.id
                left join clearingevent ce on cd_ce.events_id = ce.id
                left join customsdeclaration_sarsevent cd_se on cd.id = cd_se.customsdeclaration_id
                left join SarsEvent se on se.id = cd_se.events_id
                left join placeofcustom pc on cd.placeOfCustomsEntry_code = pc.code
                where
                cd.number = :number and
                DATE(cd.declarationDate) = :date
                """;*/


        String sql = """
                select cd.*,
                (SELECT MAX(CustomsDeclaration_clearingevent.events_id)
                                FROM CustomsDeclaration_clearingevent left
                                join ClearingEvent on CustomsDeclaration_clearingevent.events_id = ClearingEvent.id
                                WHERE CustomsDeclaration_clearingevent.customsdeclaration_id = id) as lastEvent,
                (SELECT MAX(customsdeclaration_sarsevent.events_id)
                                FROM customsdeclaration_sarsevent left
                                join SarsEvent on customsdeclaration_sarsevent.events_id = SarsEvent.id
                                WHERE customsdeclaration_sarsevent.customsdeclaration_id = id) as lastSarsEvent
                from customsdeclaration cd
                left join placeofcustom pc on cd.placeOfCustomsEntry_code = pc.code
                where
                cd.number = :number and
                DATE(cd.declarationDate) = :date
                """;

        if (placeOfCustomEntryCode != null)
            sql = sql + " and pc.code = :pc";

        Date declarationDate = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
        NativeQuery<CustomsDeclaration> nativeQuery = getSession().createNativeQuery(sql, CustomsDeclaration.class);
        nativeQuery
                .setParameter("number", number)
                .setParameter("date", date);
        if (placeOfCustomEntryCode != null)
            nativeQuery.setParameter("pc", placeOfCustomEntryCode);
        nativeQuery.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
        nativeQuery.addEntity(CustomsDeclaration.class);
        return nativeQuery.uniqueResult();
    }

    @Override
    public int getItemCount(CustomsDeclaration customsDeclaration) {
        String queryString = "select count(ali.id) " +
                "FROM actuallineitem ali " +
                "left join actualorder on " + "actualorder.id = ali.actualorder_id " +
                "left join actualconsignment on " + "actualconsignment.id = actualorder.actualconsignment_id " +
                "left join costsinvoice on " + "costsinvoice.id = actualconsignment.costsinvoice_id " +
                "left join declarationinvoice di on costsinvoice.id = di.id" +
                "WHERE " +
                "costsinvoice.state != 'DELETED' " +
                "AND costsinvoice.id = :invoiceid " +
                "AND di.customsdeclaration_id=:id";

        return getCurrentSession().createNativeQuery(queryString)
                .setParameter("id", customsDeclaration.getId()).getFirstResult();
    }

    @Override
    public List<ActualLineItem> getItems(CustomsDeclaration customsDeclaration) {
        String queryString = "select ali.* " +
                "FROM actuallineitem ali " +
                "left join actualorder on " + "actualorder.id = ali.actualorder_id " +
                "left join actualconsignment on " + "actualconsignment.id = actualorder.actualconsignment_id " +
                "left join costsinvoice on " + "costsinvoice.id = actualconsignment.costsinvoice_id " +
                "left join declarationinvoice di on costsinvoice.id = di.id" +
                "WHERE " +
                "costsinvoice.state != 'DELETED' " +
                "AND costsinvoice.id = :invoiceid " +
                "AND di.customsdeclaration_id=:id";

        return getCurrentSession().createNativeQuery(queryString, ActualLineItem.class)
                .setParameter("id", customsDeclaration.getId()).getResultList();
    }

    @Override
    public boolean isShipmentDeclarationsInReopenedState(Long shipmentId) {
        String queryString = "select count(cd.id) " +
                "FROM customsdeclaration cd " +
                "left join shipment s on s.id = cd.shipment_id " +
                "WHERE " +
                "cd.status in ('NEW','DRAFT','REOPENED','REJECTED') " +
                "AND cd.shipment_id = :shipmentId ";

        List<BigInteger> list = getCurrentSession().createNativeQuery(queryString)
                .setParameter("shipmentId", shipmentId).getResultList();
        if (list.size() > 0) {
            return (list.get(0).intValue()) > 0;
        }
        return false;
    }

    @Override
    public List<Object[]> findDeclarationShipment(String lrnNumber, long customsDeclarationId) {
        String queryString = "select s.id, s.reference " +
                "FROM customsdeclaration cd " +
                "left join shipment s on s.id = cd.shipment_id " +
                "WHERE " +
                "cd.id <> :id " +
                "and (cd.lrnnumber ilike :lrn or cd.mrnnumber ilike :lrn)";

        List list = getCurrentSession().createNativeQuery(queryString)
                .setParameter("lrn", lrnNumber).setParameter("id", customsDeclarationId).getResultList();

        return list;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set<ClearingInstruction> findClearingInstructions(Shipment shipment) {
        CriteriaBuilder builder = getCurrentSession().getCriteriaBuilder();
        CriteriaQuery<ClearingInstruction> criteria = builder.createQuery(ClearingInstruction.class);
        Root<ClearingInstruction> root = criteria.from(ClearingInstruction.class);

        criteria.select(root);
        criteria.where(builder.equal(root.get("shipment"), shipment));
        criteria.orderBy(builder.asc(root.get("created")));
        Query query = getCurrentSession().createQuery(criteria);
        return new HashSet<>(query.getResultList());
    }

    @Override
    public boolean hasClearingInstructions(Shipment shipment) {
        Number count = (Number) getCurrentSession()
                .createNativeQuery("select count(*) from clearinginstruction where shipment_id = :shipment_id")
                .setParameter("shipment_id", shipment.getId())
                .getSingleResult();

        return count.longValue() > 0;
    }

    @Override
    public CustomsDeclaration getCustomsDeclarationByLRN(String lrn) {
        CustomsDeclaration declaration = getCurrentSession()
                .createNativeQuery("select * from customsdeclaration where lrnnumber = :lrn ", CustomsDeclaration.class)
                .setParameter("lrn", lrn).uniqueResult();
        return declaration;
    }

    @Override
    public Map<Long, Date> shipmentClearingInstructionLastSignOff(Set<Long> shipIds) {
        if (shipIds == null || shipIds.isEmpty()) {
            return Collections.emptyMap();
        }
        String query = """ 
            SELECT c.shipment_id, MAX(e.created)
            FROM clearinginstruction c 
            JOIN clearinginstruction_clearingevent cie ON (cie.clearinginstruction_id = c.id) 
            JOIN clearingevent e ON (e.id = cie.events_id) 
            WHERE e.eventtype = 'SIGNED_OFF' 
            AND c.shipment_id IN (:shipIds) 
            GROUP BY c.shipment_id
            """;

        List<Object[]> results = getCurrentSession()
                .createNativeQuery(query)
                .setParameter("shipIds", shipIds)
                .list();

        Map<Long, Date> resultMap = new HashMap<>();

        for (Object[] row : results) {
            // Native queries often return BigInteger for IDs, so Number is a safe cast
            Long shipmentId = ((Number) row[0]).longValue();
            Date date = (Date) row[1];
            resultMap.put(shipmentId, date);
        }

        return resultMap;
    }
}