---------- Start of ./integration/yms/task/TasksSequenceNextTaskProvider.java ----------
package eu.reply.lea.services.yms.task.tracking.integration.yms.task;

import java.util.Optional;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.locationaware.LocationAwareTask;
import eu.reply.lea.services.yms.task.sequence.LocationAwareTasksSequence;
import eu.reply.lea.services.yms.task.sequence.repository.AbstractTasksSequenceRepository;
import eu.reply.lea.services.yms.task.tracking.NextTaskProvider;
import eu.reply.sce.backend.core.entity.EntityReference;

/**
 * Provides the next location in a task sequence for a given logistic unit.
 */
@ApplicationScoped
public class TasksSequenceNextTaskProvider implements NextTaskProvider {

    @Inject
    private AbstractTasksSequenceRepository abstractTasksSequenceRepository;

    /**
     * Retrieves the location associated with the next active {@link LocationAwareTask} for the specified logistic unit.
     *
     * @param logisticUnitReference the reference of the logistic unit
     * @return an {@code Optional} containing the next location, or empty if no active tasks are available
     */
    @Override
    public Optional<LocationReference> getNextLocation(EntityReference logisticUnitReference) {
        LocationAwareTasksSequence<LocationAwareTask<?, ?, ?>> tasksSequence =
            abstractTasksSequenceRepository.findByLogisticUnitReference(logisticUnitReference);

        if (tasksSequence != null && !tasksSequence.getActiveTasks().isEmpty()) {
            LocationAwareTask<?, ?, ?> nextTask = tasksSequence.getActiveTasks().get(0);
            return Optional.of(nextTask.getLocation());
        }

        return Optional.empty();
    }
}
---------- End of ./integration/yms/task/TasksSequenceNextTaskProvider.java ----------

---------- Start of ./QueueingStatus.java ----------
package eu.reply.lea.services.yms.task.tracking;


import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;

import java.time.Instant;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.StandardEntity;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@NoArgsConstructor
@Entity
public class QueueingStatus extends StandardEntity {

    @Setter
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "executionLocation_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "executionLocation_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "executionLocation_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "executionLocation_businessKey"))})
    private LocationReference queueExecutionLocationRef;
    @Setter
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "currentLocation_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "currentLocation_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "currentLocation_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "currentLocation_businessKey"))})
    private LocationReference currentLocationRef;
    @Setter
    private AreaType currentLocationAreaType;

    @Setter
    private Instant eventInstant;

    @ManyToOne
    private LogisticsUnitLocationTracker locationTracker;

    public QueueingStatus(final LocationReference queueExecutionLocationRef,
                          final LocationReference currentLocation,
                          final AreaType currentLocationAreaType, final Instant eventInstant) {
        this.queueExecutionLocationRef = queueExecutionLocationRef;
        this.currentLocationRef = currentLocation;
        this.eventInstant = eventInstant;
        this.currentLocationAreaType = currentLocationAreaType;
    }

    void setLocationTracker(final LogisticsUnitLocationTracker locationTracker) {
        this.locationTracker = locationTracker;
    }

    public void updateFrom(QueueingStatus source) {
        this.setCurrentLocationRef(source.getCurrentLocationRef());
        this.setCurrentLocationAreaType(source.getCurrentLocationAreaType());
        this.setEventInstant(source.getEventInstant());
    }

}

---------- End of ./QueueingStatus.java ----------

---------- Start of ./LogisticUnitLocationTrackerService.java ----------
package eu.reply.lea.services.yms.task.tracking;

import java.util.Optional;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.tracking.event.LogisticUnitLocationUpdateCommand;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.event.Producer;

/**
 * Service for tracking logistic unit locations and updating their positions based on tasks.
 */
public class LogisticUnitLocationTrackerService {

    @Inject
    private LogisticUnitLocationTrackerRepository logisticUnitLocationTrackerRepository;

    @Inject
    private Producer<LogisticUnitLocationUpdateCommand> logisticUnitLocationUpdateCommandProducer;

    @Inject
    private NextTaskProvider nextTaskProvider;

    /**
     * Updates the location of a logistic unit with the provided queueing status.
     *
     * @param logisticUnitRef    the reference of the logistic unit
     * @param queueingStatusDTO  the data transfer object containing queueing status information
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void updateLocation(final EntityReference logisticUnitRef,
                               final QueueingStatusDTO queueingStatusDTO) {
        LogisticsUnitLocationTracker
            logisticsUnitLocationTracker = logisticUnitLocationTrackerRepository.getOrCreateLogisticUnitLocationTracker(logisticUnitRef);
        Optional<LocationReference> optionalNextLocation = nextTaskProvider.getNextLocation(logisticUnitRef);

        if (optionalNextLocation.isEmpty()) {
            return;
        }
        LocationReference nextLocation = optionalNextLocation.get();

        QueueingStatus queueingStatus = new QueueingStatus(
            queueingStatusDTO.getExecutionLocationRef(),
            queueingStatusDTO.getCurrentLocationRef(),
            queueingStatusDTO.getCurrentLocationAreaType(),
            queueingStatusDTO.getEventInstant()
        );

        logisticsUnitLocationTracker.updateQueueingStatus(nextLocation, queueingStatus);
        Optional<LogisticUnitLocationUpdateCommand> updateCommand = logisticsUnitLocationTracker.generateCommandToUpdateLocation();
        updateCommand.ifPresent(logisticUnitLocationUpdateCommandProducer::fire);

    }


    /**
     * Commands a transit operation to update the loading unit's position to its next execution location.
     *
     * @param logisticUnitRef the reference of the logistic unit
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void commandTransitToUpdateLoadingUnitPosition(final EntityReference logisticUnitRef) {
        Optional<LocationReference> optionalNextLocation = nextTaskProvider.getNextLocation(logisticUnitRef);

        if (optionalNextLocation.isEmpty()) {
            return;
        }
        LocationReference nextLocation = optionalNextLocation.get();

        // Get the TransitLocationTracker
        LogisticsUnitLocationTracker
            logisticsUnitLocationTracker = logisticUnitLocationTrackerRepository.findByLogisticUnitReference(logisticUnitRef);

        // Update the next execution location
        logisticsUnitLocationTracker.updateNextExecutionLocation(nextLocation);

        // Command transit to update location
        Optional<LogisticUnitLocationUpdateCommand> updateCommand = logisticsUnitLocationTracker.generateCommandToUpdateLocation();
        updateCommand.ifPresent(logisticUnitLocationUpdateCommandProducer::fire);
    }

}

---------- End of ./LogisticUnitLocationTrackerService.java ----------

---------- Start of ./observer/LogisticUnitLocationTrackerObserver.java ----------
package eu.reply.lea.services.yms.task.tracking.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import java.time.Instant;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.tracking.AreaType;
import eu.reply.lea.services.yms.task.tracking.QueueingStatusDTO;
import eu.reply.lea.services.yms.task.tracking.LogisticUnitLocationTrackerService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;


@Observer
@ApplicationScoped
@Logged
public class LogisticUnitLocationTrackerObserver {


    @Inject
    private LogisticUnitLocationTrackerService logisticUnitLocationTrackerService;


    public void handleUpdateLocationTrackingInfo(
        @Observes @ModuleEvent("elementChangedAreaEvent") final JsonNode event)
        throws JsonProcessingException {
        EntityReference
            logisticUnitRef = ObjectMapperUtility.getMapper().treeToValue(event.path("externalEntityReference"), EntityReference.class);
        Instant
            eventInstant = Instant.parse(ObjectMapperUtility.getMapper().treeToValue(event.path("eventInstant"), String.class));
        String
            areaType = ObjectMapperUtility.getMapper().treeToValue(event.path("areaType"), String.class);
        LocationReference
            currentLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("currentLocationReference"), LocationReference.class);
        LocationReference
            executionLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionLocationReference"), LocationReference.class);
        QueueingStatusDTO queueingStatus =
            new QueueingStatusDTO(executionLocationReference, currentLocationReference, areaTypeConverter(areaType), eventInstant);
        logisticUnitLocationTrackerService.updateLocation(logisticUnitRef, queueingStatus);

    }

    public void updatePositionAfterDequeuedEvent(@Observes @ModuleEvent("updatePositionAfterDequeuedEvent") final JsonNode event)
        throws JsonProcessingException {
        EntityReference
            logisticUnitRef = ObjectMapperUtility.getMapper().treeToValue(event.path("queuedExternalEntityRef"), EntityReference.class);
        logisticUnitLocationTrackerService.commandTransitToUpdateLoadingUnitPosition(logisticUnitRef);
    }

    private AreaType areaTypeConverter(String areaType){
        return AreaType.valueOf(areaType);
    }


}

---------- End of ./observer/LogisticUnitLocationTrackerObserver.java ----------

---------- Start of ./NextTaskProvider.java ----------
package eu.reply.lea.services.yms.task.tracking;


import java.util.Optional;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.EntityReference;

/*
 * Interface for providing the next location for a logistic unit.
 */
public interface NextTaskProvider {

    /**
     * Retrieves the location associated with the next active task for the specified logistic unit.
     *
     * @param logisticUnitReference the reference of the logistic unit
     * @return an {@code Optional} containing the next location, or empty if no active tasks are available
     */
    Optional<LocationReference> getNextLocation(EntityReference logisticUnitReference);
}
---------- End of ./NextTaskProvider.java ----------

---------- Start of ./QueueingStatusDTO.java ----------
package eu.reply.lea.services.yms.task.tracking;

import java.time.Instant;

import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;

@Getter
public class QueueingStatusDTO {

    private final LocationReference currentLocationRef;
    private final AreaType currentLocationAreaType;
    private final LocationReference executionLocationRef;
    private final Instant eventInstant;

    public QueueingStatusDTO(final LocationReference executionLocationRef, final LocationReference currentLocationRef,
                             final AreaType currentLocationAreaType,
                             final Instant eventInstant) {
        this.currentLocationRef = currentLocationRef;
        this.executionLocationRef = executionLocationRef;
        this.currentLocationAreaType = currentLocationAreaType;
        this.eventInstant = eventInstant;
    }
}

---------- End of ./QueueingStatusDTO.java ----------

---------- Start of ./AreaType.java ----------
package eu.reply.lea.services.yms.task.tracking;

public enum AreaType {

    EXECUTION_AREA {
        public boolean isExecutionArea() {
            return true;
        }
    },
    WAITING_AREA {
        public boolean isWaitingArea() {
            return true;
        }
    },
    BOOKING_AREA {
        public boolean isBookingArea() {
            return true;
        }
    };


    public boolean isExecutionArea() {
        return false;
    }

    public boolean isWaitingArea() {
        return false;
    }

    public boolean isBookingArea() {
        return false;
    }

}
---------- End of ./AreaType.java ----------

---------- Start of ./LogisticUnitLocationTrackerRepository.java ----------
package eu.reply.lea.services.yms.task.tracking;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class LogisticUnitLocationTrackerRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;


    public LogisticsUnitLocationTracker findByLogisticUnitReference(final EntityReference logisticUnitRef) {
        try{
            return em.createNamedQuery(LogisticsUnitLocationTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE, LogisticsUnitLocationTracker.class)
                .setParameter("logisticUnitReferenceId", logisticUnitRef.getEntityId())
                .setParameter("logisticUnitReferenceEntityName", logisticUnitRef.getEntityName())
                .setParameter("logisticUnitReferenceModule", logisticUnitRef.getModule())
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }



    public LogisticsUnitLocationTracker getOrCreateLogisticUnitLocationTracker(final EntityReference logisticUnitRef) {
        LogisticsUnitLocationTracker tracker = findByLogisticUnitReference(logisticUnitRef);
        if (tracker == null) {
            tracker = new LogisticsUnitLocationTracker(logisticUnitRef);
            em.persist(tracker);
        }
        return tracker;
    }

}

---------- End of ./LogisticUnitLocationTrackerRepository.java ----------

---------- Start of ./LogisticsUnitLocationTracker.java ----------
package eu.reply.lea.services.yms.task.tracking;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import lombok.Getter;
import lombok.NoArgsConstructor;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.tracking.event.LogisticUnitLocationUpdateCommand;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;


/**
 * Manages the tracking of Logistic unit through various locations.
 * This entity maintains a history of all the locations a Logistic unit has passed through
 * along with the current execution location.
 */
@NoArgsConstructor
@Entity
@NamedQueries(value = {
    @NamedQuery(name = LogisticsUnitLocationTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE, query =
        "select tq from LogisticsUnitLocationTracker tq where tq.logisticUnitRef.entityId = :logisticUnitReferenceId "
        + "and tq.logisticUnitRef.entityName = :logisticUnitReferenceEntityName "
        + "and tq.logisticUnitRef.module = :logisticUnitReferenceModule")
})
@Table(indexes = {
    @Index(columnList = "logisticUnitRef_entityId,logisticUnitRef_entityName,logisticUnitRef_module")
})
public class LogisticsUnitLocationTracker extends StandardEntity {
    public static final String Q_FIND_BY_LOGISTIC_UNIT_REFERENCE = "LogisticsUnitLocationTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE";

    @NotNull
    @Getter
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "logisticUnitRef_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "logisticUnitRef_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "logisticUnitRef_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "logisticUnitRef_businessKey")),
        @AttributeOverride(name = "usageType", column = @Column(name = "logisticUnitRef_usageType"))
    })
    private EntityReference logisticUnitRef;


    @Embedded
    @Getter
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "currentExecutionLocation_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "currentExecutionLocation_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "currentExecutionLocation_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "currentExecutionLocation_businessKey"))})
    private LocationReference nextExecutionLocation;


    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "locationTracker")
    private final List<QueueingStatus> queueingStatuses = new ArrayList<>();

    /**
     * Provides an immutable list of all tracking information associated with this Logistic unit.
     * */
    public List<QueueingStatus> getQueueingStatuses(){
        return Collections.unmodifiableList(queueingStatuses);
    }

    public LogisticsUnitLocationTracker(final EntityReference logisticUnitRef) {
        this.logisticUnitRef = logisticUnitRef;
    }


    /**
     * Updates the current execution location of the Logistic unit.
     * Does nothing if the new location is the same as the current one.
     * @param locationReference The new location to update to.
     */
    public void updateNextExecutionLocation(final LocationReference  locationReference){
        if(nextExecutionLocation != null && nextExecutionLocation.equals(locationReference)){
            return;
        }
        this.nextExecutionLocation = locationReference;
    }


    /**
     * Adds or updates queueing information for the Logistic unit.
     * If the execution location data matches, it updates the area type;
     * otherwise, it adds a new entry.
     *
     * @param newQueueingStatus newQueueingStatus information
     * @return The updated or newly added {@link QueueingStatus}.
     */
    public QueueingStatus updateQueueingStatus(final LocationReference nextExecutionLocation,
                                               final QueueingStatus newQueueingStatus) {
        updateNextExecutionLocation(nextExecutionLocation);

        Optional<QueueingStatus> optionalExistingStatus = this.queueingStatuses.stream()
            .filter(qs -> qs.getQueueExecutionLocationRef().equals(newQueueingStatus.getQueueExecutionLocationRef()))
            .findFirst();

        if (optionalExistingStatus.isPresent()) {
            QueueingStatus existingStatus = optionalExistingStatus.get();
            if (existingStatus.getEventInstant().isBefore(newQueueingStatus.getEventInstant())) {
                existingStatus.updateFrom(newQueueingStatus);
            }
            return existingStatus;
        } else {
            this.addQueueingStatus(newQueueingStatus);
            return newQueueingStatus;
        }
    }

    private void addQueueingStatus(final QueueingStatus queueingStatus) {
        this.queueingStatuses.add(queueingStatus);
        queueingStatus.setLocationTracker(this);
    }

    public Optional<LogisticUnitLocationUpdateCommand> generateCommandToUpdateLocation() {
        QueueingStatus nextLocationQueueingStatus = this.getQueueingStatuses().stream().filter(locationTrackingInfo ->
                                                                                          locationTrackingInfo.getQueueExecutionLocationRef()
                                                                                              .equals(this.getNextExecutionLocation())).findFirst().orElse(null);
        if(nextLocationQueueingStatus != null){
            LogisticUnitLocationUpdateCommand command = new LogisticUnitLocationUpdateCommand(
                getLogisticUnitRef(),
                nextLocationQueueingStatus.getCurrentLocationRef(),
                nextLocationQueueingStatus.getCurrentLocationAreaType()
            );
            return Optional.of(command);
        }

        return Optional.empty();
    }

}

---------- End of ./LogisticsUnitLocationTracker.java ----------

---------- Start of ./event/LogisticUnitLocationUpdateCommand.java ----------
package eu.reply.lea.services.yms.task.tracking.event;

import java.io.Serializable;
import java.time.Instant;

import lombok.Getter;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.tracking.AreaType;
import eu.reply.sce.backend.core.entity.EntityReference;

@Getter
@Setter
public class LogisticUnitLocationUpdateCommand implements Serializable {

    private EntityReference logisticUnitRef;

    private LocationReference locationReference;

    private Instant instant;

    private AreaType areaType;

    public LogisticUnitLocationUpdateCommand(final EntityReference logisticUnitRef, final LocationReference locationReference,
                                             final AreaType areaType) {
        this.logisticUnitRef = logisticUnitRef;
        this.locationReference = locationReference;
        this.instant = Instant.now();
        this.areaType = areaType;
    }
}

---------- End of ./event/LogisticUnitLocationUpdateCommand.java ----------

---------- Start of ./ZoneQueue.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.*;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.masterdata.reference.SiteReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import lombok.Getter;

@Getter
@Entity
@DiscriminatorValue(value="ZONE")
@NamedQueries(value = {
    @NamedQuery(name = ZoneQueue.Q_FIND_BY_LOCATION_REFERENCE, query =
        "select q from ZoneQueue q where q.executionLocationReference.entityId = :locationReferenceId "
        + "and q.executionLocationReference.entityName = :locationReferenceEntityName "
        + "and q.executionLocationReference.module = :locationReferenceModule"),
    @NamedQuery(name = ZoneQueue.Q_FIND_BY_WAITING_AREA, query =
        "select q from ZoneQueue q where q.waitingArea.entityId = :waitingAreaReferenceId "
        + "and q.waitingArea.entityName = :waitingAreaReferenceEntityName "
        + "and q.waitingArea.module = :waitingAreaReferenceModule"),
})
public class ZoneQueue extends Queue {

    /**
     * 
     */
    private static final long serialVersionUID = -5486225231013215337L;
    public static final String Q_FIND_BY_WAITING_AREA = "ZoneQueue.Q_FIND_BY_WAITING_AREA";

    public static final String Q_FIND_BY_LOCATION_REFERENCE = "ZoneQueue.Q_FIND_BY_LOCATION_REFERENCE";

    
    protected ZoneQueue() {
       super();
    }
    
    public ZoneQueue(final EntityReference executionArea, final EntityReference waitingArea, final LocationReference locationRef,
                     final SiteReference siteReference) {
        super(executionArea,waitingArea, locationRef, siteReference);
    }




}

---------- End of ./ZoneQueue.java ----------

---------- Start of ./QueueAreaDomainService.java ----------
package eu.reply.lea.services.yms.task.queue;

import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.event.QueuedElementInExecutionAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInWaitingAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutExecutionAreaEvent;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
public class QueueAreaDomainService {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;


    @Inject
    private Producer<QueuedElementInWaitingAreaEvent> queuedElementInWaitingAreaProducer;

    @Inject
    private Producer<QueuedElementInExecutionAreaEvent> queuedElementInExecutionAreaProducer;

    @Inject
    private Producer<QueuedElementOutAreaEvent> queuedElementOutAreaEventProducer;

    @Inject
    private Producer<QueuedElementOutExecutionAreaEvent> queuedElementOutExecutionAreaEventProducer;

    //After enqueued event
    public void addToArea(final ExecutionArea executionArea, final WaitingArea waitingArea, final QueuedElement queuedElement){
        if(executionArea.contains(queuedElement)){
            return;
        }
        if(executionArea.add(queuedElement, queuedElementInExecutionAreaProducer)){
            return;
        }
        waitingArea.add(queuedElement, queuedElementInWaitingAreaProducer);
    }


    public void removeFromArea(final ExecutionArea executionArea, final WaitingArea waitingArea, final QueuedElement queuedElement){
        executionArea.remove(queuedElement,queuedElementOutExecutionAreaEventProducer);
        waitingArea.remove(queuedElement,queuedElementOutAreaEventProducer);
    }


    public boolean addNextOnExecutionArea(final ExecutionArea executionArea, final Queue queue) {
        return executionArea.add(queue.getQueuedElements(),
                                 queuedElementInExecutionAreaProducer);
    }



    public <T extends Queue> boolean addNextOnWaitingArea(final List<T> queues) {
        boolean atLeastOneAdded = false;

        for (Queue queue: queues){
            WaitingArea waitingArea = entityReferenceHandler.getEntity(queue.getWaitingArea(), em);
            if (!waitingArea.canAdd()) {
                return false;
            }
            ExecutionArea executionArea = entityReferenceHandler.getEntity(queue.getExecutionArea(),em);
            for (QueuedElement queuedElement : queue.getQueuedElements()) {
                if(executionArea.contains(queuedElement)){
                    continue;
                }
                if (waitingArea.add(queuedElement, queuedElementInWaitingAreaProducer)) {
                    atLeastOneAdded = true;
                }
            }
        }
        return atLeastOneAdded;
    }





}

---------- End of ./QueueAreaDomainService.java ----------

---------- Start of ./WaitingArea.java ----------
package eu.reply.lea.services.yms.task.queue;


import javax.persistence.Entity;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInWaitingAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutWaitingAreaEvent;
import eu.reply.sce.backend.core.entity.configuration.BusinessKey;
import eu.reply.sce.backend.core.jee.event.Producer;

@Entity
@BusinessKey(attributes = {"locationReference.businessKey"})
public class WaitingArea extends AbstractArea {


    public WaitingArea(final LocationReference locationReference, final Integer nominalCapacity) {
        super(locationReference, nominalCapacity);
    }

    public WaitingArea() {
        super();
    }

    public Boolean canAdd() {
        if (this.isFull()) {
            return false;
        }
        return true;
    }


    @Override
    public boolean add(QueuedElement element, Producer<? extends QueuedElementInAreaEvent> inAreaEventProducer) {
        if(super.add(element, inAreaEventProducer)){
            element.setInWaitingArea(this);
            return true;
        }
        return false;
    }

    public void fireInAreaEvent(Producer<? extends QueuedElementInAreaEvent> InWaitingAreaProducer, QueuedElement element) {
        ((Producer<QueuedElementInWaitingAreaEvent>) InWaitingAreaProducer).fire(
            new QueuedElementInWaitingAreaEvent(element.toEntityReference(),
                                                element.getExternalEntityRef(),
                                                this.toEntityReference(),
                                                this.getLocationReference(),
                                                element.getExecutionLocationReference()));
    }

    @Override
    public void fireOutAreaEvent(final Producer<? extends QueuedElementOutAreaEvent> outAreaEventProducer, QueuedElement element) {
        ((Producer<QueuedElementOutWaitingAreaEvent>) outAreaEventProducer).fire(
            new QueuedElementOutWaitingAreaEvent(element.toEntityReference(),
                                                 element.getExternalEntityRef(),
                                                 this.toEntityReference(),
                                                 this.getLocationReference(),
                                                 element.getExecutionLocationReference()));

    }




}

---------- End of ./WaitingArea.java ----------

---------- Start of ./integration/yms/task/TranslatingQueueRequestService.java ----------
package eu.reply.lea.services.yms.task.queue.integration.yms.task;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.queue.QueueRequest;
import eu.reply.lea.services.yms.task.queue.application.QueueRequestService;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.log.Logged;


/**
 * Service for obtaining queue requests by translating location-aware task sequences.
 */
@ApplicationScoped
@Logged
public class TranslatingQueueRequestService implements QueueRequestService {

    @Inject
    private TaskInQueueRequestAdapter taskInQueueRequestAdapter;

    /**
     * Retrieves a queue request based on the provided logistic unit reference.
     *
     * @param logisticUnitReference the reference of the logistic unit
     * @return the queue request corresponding to the given logistic unit reference
     */
    @Override
    public QueueRequest getQueueRequest(final EntityReference logisticUnitReference) {
        return taskInQueueRequestAdapter.getQueueRequest(logisticUnitReference);
    }

}

---------- End of ./integration/yms/task/TranslatingQueueRequestService.java ----------

---------- Start of ./integration/yms/task/QueueRequestTranslator.java ----------
package eu.reply.lea.services.yms.task.queue.integration.yms.task;

import javax.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;

import eu.reply.lea.services.yms.task.TaskReference;
import eu.reply.lea.services.yms.task.locationaware.LocationAwareTask;
import eu.reply.lea.services.yms.task.sequence.LocationAwareTasksSequence;
import eu.reply.lea.services.yms.task.queue.QueueRequest;
import eu.reply.lea.services.yms.task.queue.QueueRequestInfo;
import eu.reply.sce.backend.core.jee.log.Logged;

/**
 * Translates location-aware task sequences into queue requests.
 */
@ApplicationScoped
@Logged
public class QueueRequestTranslator {

    /**
     * Converts a location-aware tasks sequence into a queue request.
     *
     * @param taskTasksSequence the sequence of location-aware tasks to be converted
     * @return the queue request containing task information
     */
    public QueueRequest toQueueRequest(final LocationAwareTasksSequence<LocationAwareTask<?, ?, ?>> taskTasksSequence) {
        List<LocationAwareTask<?, ?, ?>> tasks = taskTasksSequence.getTasksToBeEnqueuedNext();
        List<QueueRequestInfo> queueRequestInfos = new ArrayList<>();
        for (LocationAwareTask<?, ?, ?> task : tasks) {
            queueRequestInfos.add(new QueueRequestInfo(new TaskReference(task), task.getLocation(), task.getScheduled()));
        }
        return new QueueRequest(queueRequestInfos);
    }
}

---------- End of ./integration/yms/task/QueueRequestTranslator.java ----------

---------- Start of ./integration/yms/task/TaskInQueueRequestAdapter.java ----------
package eu.reply.lea.services.yms.task.queue.integration.yms.task;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.locationaware.LocationAwareTask;
import eu.reply.lea.services.yms.task.sequence.LocationAwareTasksSequence;
import eu.reply.lea.services.yms.task.sequence.repository.AbstractTasksSequenceRepository;
import eu.reply.lea.services.yms.task.queue.QueueRequest;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.log.Logged;


/**
 * Adapter class responsible for converting location-aware task sequences
 * into queue requests.
 */
@ApplicationScoped
@Logged
public class TaskInQueueRequestAdapter {


    @Inject
    private AbstractTasksSequenceRepository abstractTasksSequenceRepository;

    @Inject
    private QueueRequestTranslator queueRequestTranslator;

    /**
     * Retrieves a queue request based on the provided logistic unit reference.
     *
     * @param logisticUnitReference the reference of the logistic unit
     * @return the queue request corresponding to the given logistic unit reference
     */
    public QueueRequest getQueueRequest(final EntityReference logisticUnitReference) {
        LocationAwareTasksSequence<LocationAwareTask<?, ?, ?>> taskTasksSequence =
            abstractTasksSequenceRepository.findByLogisticUnitReference(logisticUnitReference);
        return queueRequestTranslator.toQueueRequest(taskTasksSequence);
    }
}

---------- End of ./integration/yms/task/TaskInQueueRequestAdapter.java ----------

---------- Start of ./QueuedElement.java ----------
package eu.reply.lea.services.yms.task.queue;



import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import java.util.Calendar;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@NoArgsConstructor
@NamedQuery(name = QueuedElement.Q_FIND_BY_EXTERNAL_ENTITY_AND_EXECUTION_LOCATION_AND_IS_INSIDE_THE_QUEUE,
    query = "select queuedElement from QueuedElement queuedElement where "
            + "queuedElement.externalEntityRef.entityId = :externalEntityId "
            + "and queuedElement.externalEntityRef.entityName = :externalEntityName "
            + "and queuedElement.externalEntityRef.module = :externalEntityModule "
            + "and queuedElement.executionLocationReference.entityId = :locationReferenceId "
            + "and queuedElement.executionLocationReference.entityName = :locationReferenceEntityName "
            + "and queuedElement.executionLocationReference.module = :locationReferenceModule "
            + "and queuedElement.isInsideTheQueue = :isInsideTheQueue")
@Table(indexes = {
    @Index(name = "idx_queued_element_external_entity", columnList =
        "externalEntityRef_entity_id, externalEntityRef_entity_name, externalEntityRef_module"),
    @Index(name = "idx_queued_element_execution_location", columnList =
        "locationReference_entity_id, locationReference_entity_name, locationReference_module"),
    @Index(name = "idx_queued_element_is_inside_queue", columnList =
        "isInsideTheQueue")

})
public class QueuedElement extends StandardEntity implements Comparable<QueuedElement> {

    public static final String Q_FIND_BY_EXTERNAL_ENTITY_AND_EXECUTION_LOCATION_AND_IS_INSIDE_THE_QUEUE = "QueuedElement.Q_FIND_BY_EXTERNAL_ENTITY_AND_EXECUTION_LOCATION_AND_IS_INSIDE_THE_QUEUE";

    @Getter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "externalEntityRef_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "externalEntityRef_entity_name")),
        @AttributeOverride(name = "entityId", column = @Column(name = "externalEntityRef_entity_id")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "externalEntityRef_business_key")),
        @AttributeOverride(name = "usageType", column = @Column(name = "externalEntityRef_usage_type"))
    })
    private EntityReference externalEntityRef;

    @Setter(AccessLevel.PRIVATE)
    @Getter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "locationReference_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "locationReference_entity_name")),
        @AttributeOverride(name = "entityId", column = @Column(name = "locationReference_entity_id")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "locationReference_business_key")),
    })
    private LocationReference lastLocationReference;

    @Setter(AccessLevel.PRIVATE)
    @Getter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "executionLocationReference_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "executionLocationReference_entity_name")),
        @AttributeOverride(name = "entityId", column = @Column(name = "executionLocationReference_entity_id")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "executionLocationReference_business_key")),
    })
    private LocationReference executionLocationReference;
    @Getter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "queue_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "queue_entity_name")),
        @AttributeOverride(name = "entityId", column = @Column(name = "queue_entity_id")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "queue_business_key")),
        @AttributeOverride(name = "usageType", column = @Column(name = "queue_usage_type"))
    })
    private EntityReference queue;

    @Getter
    private AreaType areaType=AreaType.BOOKING_AREA;
    @Setter
    @Getter
    private int priority=1;
    @Getter
    private boolean scheduled;
    @Setter
    @Getter
    @Temporal(TemporalType.TIMESTAMP)
    @NotNull
    private Calendar enqueuedDate;

    @Getter
    @Setter(AccessLevel.PRIVATE)
    private Boolean isInsideTheQueue;

    public QueuedElement(final EntityReference externalEntityRef, final boolean scheduled, final EntityReference queue, final LocationReference executionLocationReference){
        this.externalEntityRef = externalEntityRef;
        this.scheduled = scheduled;
        this.setQueue(queue, executionLocationReference);
        this.isInsideTheQueue = true;
    }

    private void setQueue(final EntityReference queue, final LocationReference executionLocationReference) {
        if (queue == null){
            throw new IllegalArgumentException("Queue can't be null");
        }
        this.queue = queue;
        this.executionLocationReference = executionLocationReference;
        setEnqueuedDate(Calendar.getInstance());
    }


    public void setInExecutionArea(final ExecutionArea executionArea) {
        LocationReference locationReference = executionArea.getLocationReference();
        if(locationReference.equals(this.getLastLocationReference())){
            return;
        }
        this.setAreaType(AreaType.EXECUTION_AREA);
        this.setLastLocationReference(locationReference);
    }

    public void setInWaitingArea(final WaitingArea waitingArea) {
        LocationReference locationReference = waitingArea.getLocationReference();
        if(locationReference.equals(this.getLastLocationReference())){
            return;
        }
        this.setAreaType(AreaType.WAITING_AREA);
        this.setLastLocationReference(locationReference);
    }

    private void setAreaType(final AreaType areaType) {
        this.areaType = areaType;
    }


    @JsonIgnore
    public boolean isInExecutionArea() {
        return this.getAreaType().isExecutionArea();
    }

    @JsonIgnore
    public boolean isInWaitingArea() {
        return this.getAreaType().isWaitingArea();
    }

    @Override
    public int compareTo(QueuedElement other) {
        // Ensure "other" is not null to prevent NullPointerException
        if (other == null) throw new NullPointerException("Cannot compare to null");
        if (this.equals(other)){
            return 0;
        }

        // Compare priorities; here, lower numbers are considered "higher priority"
        int priorityComparison = Integer.compare(this.priority, other.priority);
        if (priorityComparison != 0) {
            return priorityComparison; // Return the opposite of the natural order to treat lower numbers as higher priority
        }
        if (this.scheduled != other.scheduled) {
            return this.scheduled ? -1 : 1; // scheduled (true) comes before non-scheduled (false)
        }

        int dateComparison = this.enqueuedDate.compareTo(other.enqueuedDate);
        if (dateComparison!=0){
            return dateComparison;
        }
        return Integer.compare(this.hashCode(), other.hashCode());
    }


    public QueuedElementReference toEntityReference(){
        return new QueuedElementReference(this);
    }

    @Override
    public boolean equals(Object anObject) {
        boolean equalObjects = false;

        if (anObject != null && this.getClass() == anObject.getClass()) {
            QueuedElement typedObject = (QueuedElement) anObject;
            equalObjects =
                this.getExternalEntityRef().equals(typedObject.getExternalEntityRef()) &&
                this.getQueue().equals(typedObject.getQueue()) &&
                this.getIsInsideTheQueue().equals(typedObject.getIsInsideTheQueue());
        }

        return equalObjects;
    }

    @Override
    public int hashCode() {
        int hashCodeValue =
            + (63815 * 59)
            + this.getExternalEntityRef().hashCode()
            + this.getQueue().hashCode()
            + this.getIsInsideTheQueue().hashCode();

        return hashCodeValue;
    }

    public void removeFromQueue(){
        // This method changes the 'active' field, which is used in both the 'equals' and 'hashCode' methods.
        // If this object is part of a Set or used as a key in a Map, ensure to update the collection accordingly
        // to avoid inconsistencies due to the change in hash code or equality status.
        this.isInsideTheQueue = false;
    }


}

---------- End of ./QueuedElement.java ----------

---------- Start of ./repository/ElementDequeueProcessRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;

import eu.reply.lea.services.yms.task.queue.ElementDequeueProcess;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.util.Process;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class ElementDequeueProcessRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;



    public ElementDequeueProcess findByQueuedElementReference(final QueuedElementReference queuedElementReference,
                                                              final Process.ProcessCompletionType processCompletionType,
                                                              final LockModeType lockModeType) {
        try{
            return em.createNamedQuery(ElementDequeueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS, ElementDequeueProcess.class)
                .setParameter("elementQueueId", Long.parseLong(queuedElementReference.getEntityId()))
                .setParameter("processCompletionType", processCompletionType)
                .setLockMode(lockModeType)
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }

    public ElementDequeueProcess findByQueuedElementReference(final QueuedElementReference queuedElementReference,
                                                              final Process.ProcessCompletionType processCompletionType) {
        return this.findByQueuedElementReference(queuedElementReference, processCompletionType, LockModeType.OPTIMISTIC);
    }
}

---------- End of ./repository/ElementDequeueProcessRepository.java ----------

---------- Start of ./repository/QueuedElementRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElement;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class QueuedElementRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    public QueuedElement findByExternalEntityAndExecutionLocationAndIsInsideTheQueue(final EntityReference externalEntityReference, final
    LocationReference locationReference, final boolean isInsideTheQueue) {
        try {
            return em.createNamedQuery(QueuedElement.Q_FIND_BY_EXTERNAL_ENTITY_AND_EXECUTION_LOCATION_AND_IS_INSIDE_THE_QUEUE, QueuedElement.class)
                .setParameter("externalEntityId", externalEntityReference.getEntityId())
                .setParameter("externalEntityName", externalEntityReference.getEntityName())
                .setParameter("externalEntityModule", externalEntityReference.getModule())
                .setParameter("locationReferenceId", locationReference.getEntityId())
                .setParameter("locationReferenceEntityName", locationReference.getEntityName())
                .setParameter("locationReferenceModule", locationReference.getModule())
                .setParameter("isInsideTheQueue", isInsideTheQueue)
                .getSingleResult();
        } catch (NoResultException noResultException) {
            return null;
        }
    }
}



---------- End of ./repository/QueuedElementRepository.java ----------

---------- Start of ./repository/WaitingAreaRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.NoResultException;

import eu.reply.lea.services.yms.task.queue.WaitingArea;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
public class WaitingAreaRepository {

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    public WaitingArea findByEntityReference(final EntityReference entityReference) {
        try {
            return entityReferenceHandler.getEntity(entityReference, WaitingArea.class);
        } catch (NoResultException noResultException) {
            return null;
        }
    }
}

---------- End of ./repository/WaitingAreaRepository.java ----------

---------- Start of ./repository/ElementEnqueueProcessRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;

import eu.reply.lea.services.yms.task.queue.ElementEnqueueProcess;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.util.Process;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class ElementEnqueueProcessRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;



    public ElementEnqueueProcess findByQueuedElementReference(final QueuedElementReference queuedElementReference,
                                                              final Process.ProcessCompletionType processCompletionType,
                                                              final LockModeType lockModeType) {
        try{
            return em.createNamedQuery(ElementEnqueueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS, ElementEnqueueProcess.class)
                .setParameter("elementQueueId", Long.parseLong(queuedElementReference.getEntityId()))
                .setParameter("processCompletionType", processCompletionType)
                .setLockMode(lockModeType)
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }

    public ElementEnqueueProcess findByQueuedElementReference(final QueuedElementReference queuedElementReference,
                                                              final Process.ProcessCompletionType processCompletionType) {
        return this.findByQueuedElementReference(queuedElementReference, processCompletionType, LockModeType.OPTIMISTIC);
    }
}

---------- End of ./repository/ElementEnqueueProcessRepository.java ----------

---------- Start of ./repository/QueueRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;

import java.util.List;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.masterdata.reference.SiteReference;
import eu.reply.lea.services.yms.task.queue.Queue;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class QueueRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;


    public Queue findByLocation(final LocationReference locationReference) {
        try{
            return em.createNamedQuery(Queue.Q_FIND_BY_LOCATION_REFERENCE,
                                                                      Queue.class)
                .setParameter("locationReferenceId", locationReference.getEntityId())
                .setParameter("locationReferenceEntityName", locationReference.getEntityName())
                .setParameter("locationReferenceModule", locationReference.getModule())
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }
}

---------- End of ./repository/QueueRepository.java ----------

---------- Start of ./repository/QueueProcessTrackerRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;

import eu.reply.lea.services.yms.task.queue.QueueProcessTracker;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class QueueProcessTrackerRepository {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;


    public QueueProcessTracker findByLogisticUnitRef(final EntityReference logisticUnitReference) {
        try{
            return em.createNamedQuery(QueueProcessTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE, QueueProcessTracker.class)
                .setParameter("logisticUnitReferenceId", logisticUnitReference.getEntityId())
                .setParameter("logisticUnitReferenceEntityName", logisticUnitReference.getEntityName())
                .setParameter("logisticUnitReferenceModule", logisticUnitReference.getModule())
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }


    public QueueProcessTracker findByLogisticUnitRef(final EntityReference logisticUnitReference, LockModeType lockMode) {
        try{
            return em.createNamedQuery(QueueProcessTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE, QueueProcessTracker.class)
                .setParameter("logisticUnitReferenceId", logisticUnitReference.getEntityId())
                .setParameter("logisticUnitReferenceEntityName", logisticUnitReference.getEntityName())
                .setParameter("logisticUnitReferenceModule", logisticUnitReference.getModule())
                .setLockMode(lockMode) //The pessimistic lock is almost mandatory since we have two processes that modify this tracker
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }

    public QueueProcessTracker findByQueueProcess(final ProcessReference processReference, final LockModeType lockMode) {
        try{
            return em.createNamedQuery(QueueProcessTracker.Q_FIND_BY_PROCESS_REFERENCE, QueueProcessTracker.class)
                .setParameter("executingProcessReferenceId", processReference.getEntityId())
                .setParameter("executingProcessReferenceEntityName", processReference.getEntityName())
                .setParameter("executingProcessReferenceModule", processReference.getModule())
                .setLockMode(lockMode) //The pessimistic lock is almost mandatory since we have two processes that modify this tracker
                .getSingleResult();
        }catch (NoResultException noResultException){
            return null;
        }
    }
}

---------- End of ./repository/QueueProcessTrackerRepository.java ----------

---------- Start of ./repository/ExecutionAreaRepository.java ----------
package eu.reply.lea.services.yms.task.queue.repository;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.NoResultException;

import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;


@ApplicationScoped
@Logged
public class ExecutionAreaRepository {

    @Inject
    private EntityReferenceHandler entityReferenceHandler;


    public ExecutionArea findByEntityReference(final EntityReference entityReference) {
        try {
            return entityReferenceHandler.getEntity(entityReference, ExecutionArea.class);
        } catch (NoResultException noResultException) {
            return null;
        }
    }
}

---------- End of ./repository/ExecutionAreaRepository.java ----------

---------- Start of ./QueueHandler.java ----------
package eu.reply.lea.services.yms.task.queue;

import java.io.InvalidClassException;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Inject;

import lombok.Getter;

import eu.reply.lea.services.common.model.IdentifiableResource;
import eu.reply.lea.services.masterdata.client.MasterdataServiceException;
import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.YmsEntityHandler;
import eu.reply.lea.services.yms.task.queue.event.ElementDequeuedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementEnqueuedEvent;
import eu.reply.lea.services.yms.task.queue.event.TaskEnqueuedEvent;
import eu.reply.lea.services.yms.task.queue.repository.QueueRepository;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Default
@Logged
@ApplicationScoped
public class QueueHandler extends YmsEntityHandler {


    @Inject
    @Getter
    private Producer<TaskEnqueuedEvent> taskEnqueuedEventProducer;


    @Inject
    @Getter
    private Producer<ElementEnqueuedEvent> elementEnqueuedEventProducer;


    @Inject
    @Getter
    private Producer<ElementDequeuedEvent> elementDequeuedEventProducer;


    @Inject
    private QueueRepository queueRepository;


    /**
     * Construct LocationTaskQueueHandler class
     */
    public QueueHandler() {
        super();
    }


    /**
     * From a set of locations this function retrieves the queues and
     * finds the one with less enqueued task.
     *
     * @param locations
     * @param <T>
     * @return The location with less enqueued tasks
     * @throws MasterdataServiceException        Masterdata query exception
     * @throws InvalidClassException             Impossibility to create a location reference
     */
    public <T extends IdentifiableResource> T selectLocationWithLessEnqueuedTasks(Set<T> locations)
        throws MasterdataServiceException,
        InvalidClassException {
        Integer minNumberTasksEnqueued = null;
        T locationWithLessEnqueuedTasks = null;
        if (locations.isEmpty()) {
            throw new IllegalArgumentException(
                "[LocationTaskQueueHandler].selectLocationWithLessEnqueuedTasks The location set is empty");
        }
        this.debug(
            "[LocationTaskQueueHandler].selectLocationWithLessEnqueuedTasks selecting the location with less enqueued tasks" +
            "between [{}] different locations", locations.size());
        for (T location : locations) {
            Queue queue = this.getLocationTasksQueue(location);
            if (minNumberTasksEnqueued == null || minNumberTasksEnqueued > queue.getQueuedElementsCount()) {
                minNumberTasksEnqueued = queue.getQueuedElementsCount();
                locationWithLessEnqueuedTasks = location;
            }
        }
        this.debug("[LocationTaskQueueHandler].selectLocationWithLessEnqueuedTasks selected location:",
                   locationWithLessEnqueuedTasks);
        return locationWithLessEnqueuedTasks;
    }

    public <T extends IdentifiableResource> Queue getLocationTasksQueue(T location) {
        return queueRepository.findByLocation(new LocationReference(location));
    }




}

---------- End of ./QueueHandler.java ----------

---------- Start of ./ElementEnqueueProcess.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

import lombok.NoArgsConstructor;

import eu.reply.sce.backend.core.entity.EntityReference;


/**
 * This class handles the enqueue of the elements based on the location provided.
 */
@Entity
@NoArgsConstructor
@NamedQueries(value = {
    @NamedQuery(name = ElementEnqueueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS, query =
        "select eq from ElementEnqueueProcess eq left join QueuedElement el "
        + "on el.externalEntityRef.entityId = eq.externalEntityReference.entityId "
        + "and el.externalEntityRef.entityName = eq.externalEntityReference.entityName "
        + "and el.externalEntityRef.module = eq.externalEntityReference.module "
        + "where  el.id = :elementQueueId and eq.processCompletionType = :processCompletionType")
})
public class ElementEnqueueProcess extends ElementQueueProcess {


    public static final String Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS = "ElementEnqueueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS";



    public ElementEnqueueProcess(final EntityReference externalEntityReference) {
        super(externalEntityReference);
    }

}

---------- End of ./ElementEnqueueProcess.java ----------

---------- Start of ./exception/QueueException.java ----------
package eu.reply.lea.services.yms.task.queue.exception;

import eu.reply.sce.backend.core.entity.StringifyUtils;
import eu.reply.sce.backend.core.exception.SerializableException;

import lombok.Getter;

@Getter
public class QueueException extends SerializableException {

    private static final long serialVersionUID = -7621932899363932954L;


    public enum Codes {

        DOCK_AVAILABLE_CAPACITY_EXCEEDED("Can't set effective capacity, Dock serving capacity exceeded"),
        DOCK_AVAILABLE_CAPACITY_NEGATIVE("Can't set effective capacity, negative value");

        private String message;

        Codes(final String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }


    /**
     * constructor
     * @param code The code of the exception
     */
    public QueueException(final Codes code) {
        super(code.toString(), StringifyUtils.toMessage(code.getMessage()));
    }



}

---------- End of ./exception/QueueException.java ----------

---------- Start of ./exception/EmptyQueueRequestException.java ----------
package eu.reply.lea.services.yms.task.queue.exception;


public class EmptyQueueRequestException extends IllegalStateException {
    public EmptyQueueRequestException() {
        super("The queue request is empty. Cannot create a new element process.");
    }
}

---------- End of ./exception/EmptyQueueRequestException.java ----------

---------- Start of ./exception/EnqueueProcessPendingException.java ----------
package eu.reply.lea.services.yms.task.queue.exception;

public class EnqueueProcessPendingException extends IllegalStateException {
    public EnqueueProcessPendingException() {
        super("An enqueue process is still pending. Cannot create a new element process.");
    }
}

---------- End of ./exception/EnqueueProcessPendingException.java ----------

---------- Start of ./exception/DequeueProcessPendingException.java ----------
package eu.reply.lea.services.yms.task.queue.exception;

public class DequeueProcessPendingException extends IllegalStateException {
    public DequeueProcessPendingException() {
        super("A dequeue process is still pending. Cannot create a new element process.");
    }
}
---------- End of ./exception/DequeueProcessPendingException.java ----------

---------- Start of ./observer/TransitQueueStartObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;

import eu.reply.lea.services.yard.reference.TransitReference;
import eu.reply.lea.services.yms.task.event.LocationAwareTaskStatusChangedEventInfo;
import eu.reply.lea.services.yms.task.queue.application.QueueProcessTrackerService;
import eu.reply.lea.services.yms.task.queue.exception.DequeueProcessPendingException;
import eu.reply.lea.services.yms.task.queue.exception.EmptyQueueRequestException;
import eu.reply.lea.services.yms.task.queue.exception.EnqueueProcessPendingException;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;


@Observer
@ApplicationScoped
@Logged
public class TransitQueueStartObserver {

    @Inject
    private QueueProcessTrackerService queueProcessTrackerService;

    @Inject
    private Logger logger;


    public void handleTransitSequenceCreatedEvent(@Observes @ModuleEvent("transitSequenceCreatedEvent") final JsonNode event)
        throws JsonProcessingException {
        TransitReference
            transitReference = ObjectMapperUtility.getMapper().treeToValue(event.path("transitReference"), TransitReference.class);
        try{
            queueProcessTrackerService.startNextQueueProcess(transitReference.toEntityReference());
        } catch (EnqueueProcessPendingException | DequeueProcessPendingException | EmptyQueueRequestException e) {
            logger.warn(e.getMessage());
        }
    }


    public void startNewQueueProcessAfterDequeueCompleted(
        @Observes @ModuleEvent("elementDequeueProcessCompletedNormallyEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference
            externalEntityReference = ObjectMapperUtility.getMapper().treeToValue(event.path("externalEntityReference"), EntityReference.class);
        try {
            queueProcessTrackerService.startNextQueueProcess(externalEntityReference);
        } catch (EnqueueProcessPendingException | DequeueProcessPendingException | EmptyQueueRequestException e) {
            logger.warn(e.getMessage());
        }
    }

    public void handle(@Observes @ModuleEvent("checkInTaskCreatedBeforeAddressingEvent") final JsonNode event)
        throws JsonProcessingException {
        LocationAwareTaskStatusChangedEventInfo payload =
            ObjectMapperUtility.getMapper().treeToValue(event, LocationAwareTaskStatusChangedEventInfo.class);
        try{
            queueProcessTrackerService.startNextQueueProcess(payload.getLogisticUnitRef());
        } catch (EnqueueProcessPendingException | DequeueProcessPendingException | EmptyQueueRequestException e) {
            logger.warn(e.getMessage());
        }
    }


}

---------- End of ./observer/TransitQueueStartObserver.java ----------

---------- Start of ./observer/ExecutionAreaUnblockedObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;


import java.io.Serializable;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.events.map.DockUnBlockedEvent;
import eu.reply.lea.services.masterdata.pojo.map.DockPojo;
import eu.reply.lea.services.masterdata.reference.DockReference;
import eu.reply.lea.services.yms.task.queue.application.ExecutionAreaService;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Observer
@Logged
@ApplicationScoped
public class ExecutionAreaUnblockedObserver implements Serializable {

    @Inject
    private ExecutionAreaService executionAreaService;


    /**
     * Function called after the DockUnBlockedEvent.
     * This function tries to unblock the next task in the queue of the dock.
     *
     * @param dockPojo
     */
    public void onDockUnBlocked(@Observes @ModuleEvent(DockUnBlockedEvent.NAME) DockPojo dockPojo) {
        DockReference dockReference = new DockReference(dockPojo);
        executionAreaService.unblockExecutionArea(dockReference.toEntityReference());
    }
}

---------- End of ./observer/ExecutionAreaUnblockedObserver.java ----------

---------- Start of ./observer/InternalTasksSequenceQueueStartObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;

import eu.reply.lea.services.yms.task.queue.application.QueueProcessTrackerService;
import eu.reply.lea.services.yms.task.queue.exception.DequeueProcessPendingException;
import eu.reply.lea.services.yms.task.queue.exception.EmptyQueueRequestException;
import eu.reply.lea.services.yms.task.queue.exception.EnqueueProcessPendingException;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;


@Observer
@ApplicationScoped
@Logged
public class InternalTasksSequenceQueueStartObserver {

    @Inject
    private QueueProcessTrackerService queueProcessTrackerService;

    @Inject
    private Logger logger;


    public void handleTransitSequenceCreatedEvent(@Observes @ModuleEvent("internalTasksSequenceCreatedEvent") final JsonNode event)
        throws JsonProcessingException {
        EntityReference
            logisticUnitRef = ObjectMapperUtility.getMapper().treeToValue(event.path("logisticUnitRef"), EntityReference.class);
        try{
            queueProcessTrackerService.startNextQueueProcess(logisticUnitRef);
        } catch (EnqueueProcessPendingException | DequeueProcessPendingException | EmptyQueueRequestException e) {
            logger.warn(e.getMessage());
        }
    }

}

---------- End of ./observer/InternalTasksSequenceQueueStartObserver.java ----------

---------- Start of ./observer/AddToAreasOfQueueObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import eu.reply.lea.services.yms.task.queue.application.QueueAreaService;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class AddToAreasOfQueueObserver {
    @Inject
    private QueueAreaService queueAreaService;

    public void handleEnqueuedEvent(
        @Observes @ModuleEvent("enqueuedEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference
            waitingArea = ObjectMapperUtility.getMapper().treeToValue(event.path("waitingAreaReference"), EntityReference.class);
        EntityReference
            executionArea = ObjectMapperUtility.getMapper().treeToValue(event.path("executionAreaReference"), EntityReference.class);
        QueuedElementReference
            queuedElement = ObjectMapperUtility.getMapper().treeToValue(event.path("queuedElement"), QueuedElementReference.class);
        queueAreaService.addToArea(waitingArea, executionArea, queuedElement);
    }

    public void handleExecutionAreaUnblockedEvent(
        @Observes @ModuleEvent("executionAreaUnblockedEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference
            executionAreaReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionAreaReference"), EntityReference.class);
        queueAreaService.addNextOnExecutionArea(executionAreaReference);
    }
}


---------- End of ./observer/AddToAreasOfQueueObserver.java ----------

---------- Start of ./observer/OutWaitingAreaEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.queue.application.QueueAreaService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

@ApplicationScoped
@Logged
public class OutWaitingAreaEventObserver {
    @Inject
    private QueueAreaService queueAreaService;


    public void handle(@Observes @ModuleEvent("queuedElementOutWaitingAreaEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference areaReference = ObjectMapperUtility.getMapper().treeToValue(event.path("areaReference"), EntityReference.class);
        queueAreaService.addNextOnWaitingArea(areaReference);
    }
}


---------- End of ./observer/OutWaitingAreaEventObserver.java ----------

---------- Start of ./observer/DequeuedEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import eu.reply.lea.services.yms.task.queue.application.QueueAreaService;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class DequeuedEventObserver {
    @Inject
    private QueueAreaService queueAreaService;


    public void handleAreaRemoval(@Observes @ModuleEvent("removeFromAreaAfterDequeueEvent") final JsonNode event) throws JsonProcessingException {
        QueuedElementReference
            queuedElement = ObjectMapperUtility.getMapper().treeToValue(event.path("queuedElement"), QueuedElementReference.class);
        EntityReference
            waitingArea = ObjectMapperUtility.getMapper().treeToValue(event.path("waitingAreaReference"), EntityReference.class);
        EntityReference
            executionArea = ObjectMapperUtility.getMapper().treeToValue(event.path("executionAreaReference"), EntityReference.class);

        queueAreaService.removeFromArea(waitingArea, executionArea, queuedElement);
    }


}


---------- End of ./observer/DequeuedEventObserver.java ----------

---------- Start of ./observer/ChangedPriorityEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.application.QueuedElementService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

@ApplicationScoped
@Logged
@Observer
public class ChangedPriorityEventObserver {
    @Inject
    private QueuedElementService queuedElementService;

    public void handle(
        @Observes @ModuleEvent("changedPriorityEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference
            transitReference = ObjectMapperUtility.getMapper().treeToValue(event.path("relatedEntityReference"), EntityReference.class);
        LocationReference
            locationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("locationReference"), LocationReference.class);
        Integer
            priority = ObjectMapperUtility.getMapper().treeToValue(event.path("priority"), Integer.class);
        queuedElementService.setPriority(transitReference, locationReference, priority);
    }
}


---------- End of ./observer/ChangedPriorityEventObserver.java ----------

---------- Start of ./observer/EnqueueElementObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import eu.reply.lea.services.yms.task.queue.application.QueueService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class EnqueueElementObserver {
    @Inject
    private QueueService queueService;


    public void enqueueElement(@Observes @ModuleEvent("elementEnqueueProcessCreatedEvent") final JsonNode event)
        throws JsonProcessingException {

        ProcessReference
            processReference = ObjectMapperUtility.getMapper().treeToValue(event.path("processReference"), ProcessReference.class);
        queueService.queue(processReference);
    }

    public void enqueueNextElement(@Observes @ModuleEvent("elementEnqueueProcessPartialConfirmEvent") final JsonNode event)
        throws JsonProcessingException {
        ProcessReference
            processReference = ObjectMapperUtility.getMapper().treeToValue(event.path("processReference"), ProcessReference.class);
        queueService.queue(processReference);
    }


}


---------- End of ./observer/EnqueueElementObserver.java ----------

---------- Start of ./observer/PreparationTerminatedEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.queue.application.QueueAreaService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;


@ApplicationScoped
@Logged
@Observer
public class PreparationTerminatedEventObserver {

    @Inject
    private QueueAreaService queueAreaService;

    public void handlePreparationTermination(@Observes @ModuleEvent("preparationTerminatedEvent") JsonNode event)
        throws JsonProcessingException {
        EntityReference executionAreaRef = ObjectMapperUtility.getMapper().treeToValue(event.path("executionAreaReference"), EntityReference.class);
        queueAreaService.addNextOnExecutionArea(executionAreaRef);
    }
}

---------- End of ./observer/PreparationTerminatedEventObserver.java ----------

---------- Start of ./observer/DequeueElementObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import eu.reply.lea.services.yms.task.queue.application.QueueService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class DequeueElementObserver {
    @Inject
    private QueueService queueService;


    public void dequeueElement(@Observes @ModuleEvent("elementDequeueProcessCreatedEvent") final JsonNode event)
        throws JsonProcessingException {

        ProcessReference
            processReference = ObjectMapperUtility.getMapper().treeToValue(event.path("processReference"), ProcessReference.class);
        queueService.dequeue(processReference);
    }

    public void dequeueNextElement(@Observes @ModuleEvent("elementDequeueProcessPartialConfirmEvent") final JsonNode event)
        throws JsonProcessingException {
        ProcessReference
            processReference = ObjectMapperUtility.getMapper().treeToValue(event.path("processReference"), ProcessReference.class);
        queueService.dequeue(processReference);
    }


}


---------- End of ./observer/DequeueElementObserver.java ----------

---------- Start of ./observer/ExecutionAreaCreationObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.pojo.map.DockPojo;
import eu.reply.lea.services.masterdata.pojo.map.GatePojo;
import eu.reply.lea.services.masterdata.pojo.map.ZonePojo;
import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.application.ExecutionAreaService;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class ExecutionAreaCreationObserver {
    @Inject
    private ExecutionAreaService executionAreaService;

    public void handleCreationOfExecutionArea(@Observes @ModuleEvent("dockCreated") final DockPojo dockPojo) {
        executionAreaService.createExecutionArea(new LocationReference(dockPojo),
                                                 dockPojo.getCapacity());

    }

    public void handleCreationOfExecutionArea(@Observes @ModuleEvent("gateCreated") final GatePojo gatePojo) {
        executionAreaService.createExecutionArea(new LocationReference(gatePojo),
                                                        gatePojo.getCapacity());

    }

    public void handleCreationOfExecutionArea(@Observes @ModuleEvent("notWaitingZoneCreated") final ZonePojo zonePojo) {
        executionAreaService.createExecutionArea(new LocationReference(zonePojo),
                                                  zonePojo.getCapacity());
    }



}


---------- End of ./observer/ExecutionAreaCreationObserver.java ----------

---------- Start of ./observer/RemoveFromQueuedTasksObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.queue.application.QueueProcessTrackerService;
import eu.reply.lea.services.yms.task.queue.event.TaskDequeueCommandInfo;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;


@Observer
@ApplicationScoped
@Logged
public class RemoveFromQueuedTasksObserver {

    @Inject
    private QueueProcessTrackerService queueProcessTrackerService;

    public void handleTransitSequenceCreatedEvent(
        @Observes @ModuleEvent("taskDequeueCommand") final TaskDequeueCommandInfo event) {
        queueProcessTrackerService.startNewDequeueProcess(event.getTasks(), event
            .getLocationReferences(), event.getTransitReference().toEntityReference());
    }
}

---------- End of ./observer/RemoveFromQueuedTasksObserver.java ----------

---------- Start of ./observer/OutExecutionAreaEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.yms.task.queue.application.QueueAreaService;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

@ApplicationScoped
@Logged
public class OutExecutionAreaEventObserver {
    @Inject
    private QueueAreaService queueAreaService;

    public void handle(@Observes @ModuleEvent("queuedElementOutExecutionAreaEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference executionAreaReference = ObjectMapperUtility.getMapper().treeToValue(event.path("areaReference"), EntityReference.class);
        queueAreaService.addNextOnExecutionArea(executionAreaReference);
    }
}


---------- End of ./observer/OutExecutionAreaEventObserver.java ----------

---------- Start of ./observer/ElementChangedAreaEventObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.ElementChangedAreaEvent;
import eu.reply.lea.services.yms.task.tracking.AreaType;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

@ApplicationScoped
@Logged
@Observer
public class ElementChangedAreaEventObserver {


    @Inject
    private Producer<ElementChangedAreaEvent> elementChangedAreaEventProducer;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void handleInWaitingAreaEvent(@Observes @ModuleEvent("queuedElementInWaitingAreaEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference externalEntityRef = ObjectMapperUtility.getMapper().treeToValue(event.path("externalEntityRef"), EntityReference.class);
        LocationReference
            executionLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionLocationReference"), LocationReference.class);
        LocationReference
            currentLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("currentLocationReference"), LocationReference.class);
        ElementChangedAreaEvent elementChangedAreaEvent = new ElementChangedAreaEvent(externalEntityRef,
                                                                                      currentLocationReference,
                                                                                      executionLocationReference,
                                                                                      AreaType.WAITING_AREA.toString(),
                                                                                      "IN");
        elementChangedAreaEventProducer.fire(elementChangedAreaEvent);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void handleInExecutionAreaEvent(@Observes @ModuleEvent("queuedElementInExecutionAreaEvent") final JsonNode event) throws JsonProcessingException {
        EntityReference externalEntityRef = ObjectMapperUtility.getMapper().treeToValue(event.path("externalEntityRef"), EntityReference.class);
        LocationReference
            executionLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionLocationReference"), LocationReference.class);
        LocationReference
            currentLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("currentLocationReference"), LocationReference.class);
        ElementChangedAreaEvent elementChangedAreaEvent = new ElementChangedAreaEvent(externalEntityRef,
                                                                                      currentLocationReference,
                                                                                      executionLocationReference,
                                                                                      AreaType.EXECUTION_AREA.toString(),
                                                                                      "IN");
        elementChangedAreaEventProducer.fire(elementChangedAreaEvent);
    }


}

---------- End of ./observer/ElementChangedAreaEventObserver.java ----------

---------- Start of ./observer/ElementEnqueueProcessCompletenessObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.application.ElementEnqueueProcessService;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Observer
@ApplicationScoped
@Logged
public class ElementEnqueueProcessCompletenessObserver {

    @Inject
    private ElementEnqueueProcessService elementEnqueueProcessService;


    public void confirmProcess(@Observes @ModuleEvent("queueProcessUpdateForInExecutionAreaEvent") final JsonNode event) throws JsonProcessingException {
        QueuedElementReference
            elementReference = ObjectMapperUtility.getMapper().treeToValue(event.path("queuedElementRef"), QueuedElementReference.class);
        LocationReference
            areaLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("currentLocationReference"), LocationReference.class);
        elementEnqueueProcessService.confirmProcess(elementReference, areaLocationReference);
    }

    public void abortProcess(@Observes @ModuleEvent("queueProcessUpdateForDequeuedEvent") final JsonNode event) throws JsonProcessingException {
        LocationReference
            executionLocationReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionLocationReference"), LocationReference.class);

        QueuedElementReference
            elementReference = ObjectMapperUtility.getMapper().treeToValue(event.path("queuedElement"), QueuedElementReference.class);
        elementEnqueueProcessService.abortProcess(elementReference, executionLocationReference);
    }



}

---------- End of ./observer/ElementEnqueueProcessCompletenessObserver.java ----------

---------- Start of ./observer/ExecutionAreaBlockedObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import java.io.Serializable;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.events.map.DockBlockedEvent;
import eu.reply.lea.services.masterdata.pojo.map.DockPojo;
import eu.reply.lea.services.masterdata.reference.DockReference;
import eu.reply.lea.services.yms.task.queue.application.ExecutionAreaService;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Observer
@Logged
@ApplicationScoped
public class ExecutionAreaBlockedObserver implements Serializable {

    @Inject
    private ExecutionAreaService executionAreaService;

    /**
     * Function called after the DockUnBlockedEvent.
     * This function tries to unblock the next task in the queue of the dock.
     *
     * @param dockPojo
     */
    public void onDockBlocked(@Observes @ModuleEvent(DockBlockedEvent.NAME) DockPojo dockPojo) {
        DockReference dockReference = new DockReference(dockPojo);
        executionAreaService.blockExecutionArea(dockReference.toEntityReference());
    }
}

---------- End of ./observer/ExecutionAreaBlockedObserver.java ----------

---------- Start of ./observer/QueueCreationObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.lea.services.yms.util.ObjectMapperUtility;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;

@ApplicationScoped
@Logged
@Observer
public class QueueCreationObserver {
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    @Inject
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void updateQueueReferenceOfExecutingArea(@Observes @ModuleEvent("queueCreated") final JsonNode event)
        throws JsonProcessingException {
        EntityReference
            queueReference = ObjectMapperUtility.getMapper().treeToValue(event.path("queueReference"), EntityReference.class);
        EntityReference
            executionAreaReference = ObjectMapperUtility.getMapper().treeToValue(event.path("executionAreaReference"), EntityReference.class);
        ExecutionArea executionArea =  entityReferenceHandler.getEntity(executionAreaReference, em );
        executionArea.setQueueReference(queueReference);
    }



}


---------- End of ./observer/QueueCreationObserver.java ----------

---------- Start of ./observer/WaitingAreaCreationObserver.java ----------
package eu.reply.lea.services.yms.task.queue.observer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.pojo.map.ZonePojo;
import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.application.WaitingAreaService;
import eu.reply.sce.backend.core.event.ModuleEvent;
import eu.reply.sce.backend.core.event.Observer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Observer
public class WaitingAreaCreationObserver {
    @Inject
    private WaitingAreaService waitingAreaService;


    public void handleCreationOfWaitingArea(@Observes @ModuleEvent("waitingZoneCreated") final ZonePojo zonePojo) {
        waitingAreaService.createWaitingArea(new LocationReference(zonePojo), zonePojo.getCapacity());
    }



}


---------- End of ./observer/WaitingAreaCreationObserver.java ----------

---------- Start of ./GateQueue.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.*;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.masterdata.reference.SiteReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;

@Getter
@Entity
@DiscriminatorValue(value="GATE")
@NamedQueries(value = {
    @NamedQuery(name = GateQueue.Q_FIND_BY_LOCATION_REFERENCE, query =
        "select q from GateQueue q where q.executionLocationReference.entityId = :locationReferenceId "
        + "and q.executionLocationReference.entityName = :locationReferenceEntityName "
        + "and q.executionLocationReference.module = :locationReferenceModule"),
    @NamedQuery(name = GateQueue.Q_FIND_BY_WAITING_AREA, query =
        "select q from GateQueue q where q.waitingArea.entityId = :waitingAreaReferenceId "
        + "and q.waitingArea.entityName = :waitingAreaReferenceEntityName "
        + "and q.waitingArea.module = :waitingAreaReferenceModule"),
})
public class GateQueue extends Queue {

    /**
     * 
     */
    private static final long serialVersionUID = -1692476388274497900L;
    public static final String Q_FIND_BY_WAITING_AREA = "GateQueue.Q_FIND_BY_WAITING_AREA";
    public static final String Q_FIND_BY_LOCATION_REFERENCE = "GateQueue.Q_FIND_BY_LOCATION_REFERENCE";
    
    protected GateQueue() {
       super();
    }
    
    public GateQueue(final EntityReference executionArea, final EntityReference waitingArea, final LocationReference locationRef, final SiteReference siteReference) {
        super(executionArea,waitingArea, locationRef, siteReference);
    }


}

---------- End of ./GateQueue.java ----------

---------- Start of ./QueueRequestInfo.java ----------
package eu.reply.lea.services.yms.task.queue;

import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.TaskReference;

/**
 * Represents information about a task, including its reference, execution location,
 * and whether it is scheduled.
 */
@Getter
public class QueueRequestInfo {

    private final TaskReference taskReference;
    private final LocationReference executionLocation;
    private final boolean scheduled;

    /**
     * Constructs a {@code QueueRequestInfo} object containing details about the task.
     *
     * @param taskReference      the reference of the task
     * @param executionLocation  the location where the task is to be executed
     * @param scheduled          indicates whether the task is scheduled or not
     */
    public QueueRequestInfo(final TaskReference taskReference, final LocationReference executionLocation,
                            final boolean scheduled) {
        this.taskReference = taskReference;
        this.executionLocation = executionLocation;
        this.scheduled = scheduled;
    }
}
---------- End of ./QueueRequestInfo.java ----------

---------- Start of ./QueueProcessTrackerDomainService.java ----------
package eu.reply.lea.services.yms.task.queue;

import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.TaskReference;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCreatedEvent;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.event.Producer;

public class QueueProcessTrackerDomainService {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private Producer<ElementQueueProcessCreatedEvent> elementQueueProcessCreatedEventProducer;

    /**
     * Initiates the dequeue process for specified transit tasks.
     * This method identifies tasks that need to be dequeued, creates an ElementDequeueProcess,
     * and updates the QueueProcessTracker accordingly.
     *
     * @param queueProcessTracker The tracker managing the queue of transit tasks.
     * @param taskToBeDequeued List of task references to be dequeued.
     * @param taskExecutionLocation List of location references where the tasks will be executed.
     * @throws IllegalArgumentException if the number of tasks to be dequeued doesn't match the number of execution locations.
     */
    public void startDequeueProcess(final QueueProcessTracker queueProcessTracker, final List<TaskReference> taskToBeDequeued,
                                    final List<LocationReference> taskExecutionLocation) {
        if (taskToBeDequeued.size() != taskExecutionLocation.size()) {
            throw new IllegalArgumentException("the number of tasks to be dequeued and the relative execution location should have the same size");
        }
        List<TaskReference> dequeueableTasks = new ArrayList<>();
        List<LocationReference> dequeueableTaskExecutionLocations = new ArrayList<>();
        for (int i = 0; i < taskToBeDequeued.size(); i++) {
            if (queueProcessTracker.isTaskQueued(taskToBeDequeued.get(i)) || queueProcessTracker.isTaskPendingQueuePlacement(taskToBeDequeued.get(i))) {
                dequeueableTasks.add(taskToBeDequeued.get(i));
                dequeueableTaskExecutionLocations.add(taskExecutionLocation.get(i));
            }
        }
        if (dequeueableTasks.isEmpty()){
            return;
        }
        ElementDequeueProcess elementDequeueProcess = queueProcessTracker.createElementDequeueProcess(dequeueableTaskExecutionLocations);
        for (TaskReference taskReference : dequeueableTasks) {
            queueProcessTracker.addTaskPendingDequeue(taskReference);
        }
        em.persist(elementDequeueProcess);
        queueProcessTracker.setExecutingDequeueProcessReference(new ProcessReference(elementDequeueProcess), elementQueueProcessCreatedEventProducer);
    }




}

---------- End of ./QueueProcessTrackerDomainService.java ----------

---------- Start of ./Queue.java ----------
package eu.reply.lea.services.yms.task.queue;

import static eu.reply.lea.services.yms.ModuleConfiguration.MODULE_NAME;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.SortNatural;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.masterdata.reference.SiteReference;
import eu.reply.lea.services.yms.task.queue.event.ElementDequeuedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementEnqueuedEvent;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;
import eu.reply.sce.backend.core.entity.configuration.BusinessKey;
import eu.reply.sce.backend.core.jee.event.Producer;

@Getter
@Entity
@DiscriminatorColumn(name = "LOC_TYPE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@NamedQueries(value = {
    @NamedQuery(name = Queue.Q_FIND_BY_LOCATION_REFERENCE, query =
        "select q from Queue q where q.executionLocationReference.entityId = :locationReferenceId "
        + "and q.executionLocationReference.entityName = :locationReferenceEntityName "
        + "and q.executionLocationReference.module = :locationReferenceModule"),
    @NamedQuery(name = Queue.Q_FIND_BY_WAITING_AREA, query =
        "select q from Queue q where q.waitingArea.entityId = :waitingAreaReferenceId "
        + "and q.waitingArea.entityName = :waitingAreaReferenceEntityName "
        + "and q.waitingArea.module = :waitingAreaReferenceModule"),
})
@Table(indexes = {
    @Index(columnList = "waitingArea_entityId,waitingArea_entityName,waitingArea_module"),
    @Index(columnList = "executionArea_entityId,executionArea_entityName,executionArea_module"),
    @Index(columnList = "site_entityId,site_entityName,site_module"),
    },
    uniqueConstraints = @UniqueConstraint(
        columnNames = {"executionArea_entityId", "executionArea_entityName", "executionArea_module"}))
@BusinessKey(attributes = {"executionLocationReference.businessKey"})
public abstract class Queue extends StandardEntity {

    public static final String Q_FIND_BY_LOCATION_REFERENCE = "AbstractQueue.Q_FIND_BY_LOCATION_REFERENCE";
    public static final String Q_FIND_BY_WAITING_AREA = "AbstractQueue.Q_FIND_BY_WAITING_AREA";
    private static final long serialVersionUID = 2213504911524892516L;


    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "entityId", column = @Column(name = "waitingArea_entityId")),
        @AttributeOverride(name = "entityName", column = @Column(name = "waitingArea_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "waitingArea_module")),
        @AttributeOverride(name = "usageType", column = @Column(name = "waitingArea_usageType")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "waitingArea_businessKey"))
    })
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private EntityReference waitingArea;

    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "entityId", column = @Column(name = "executionArea_entityId")),
        @AttributeOverride(name = "entityName", column = @Column(name = "executionArea_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "executionArea_module")),
        @AttributeOverride(name = "usageType", column = @Column(name = "executionArea_usageType")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "executionArea_businessKey"))
    })
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private EntityReference executionArea;


    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "entityId", column = @Column(name = "execution_location_entityId")),
        @AttributeOverride(name = "entityName", column = @Column(name = "execution_location_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "execution_location_module")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "execution_location_businessKey"))
    })
    @Getter
    @Setter
    private LocationReference executionLocationReference;

    @NotNull
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "entityName", column = @Column(name = "site_entityName")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "site_businesskey")),
        @AttributeOverride(name = "module", column = @Column(name = "site_module", length = 20)),
        @AttributeOverride(name = "entityId", column = @Column(name = "site_entityId", length = 50)) })
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private SiteReference site;

    private int queuedElementsCount = 0;


    @OneToMany(fetch = FetchType.LAZY)
    @SortNatural
    private final SortedSet<QueuedElement> queuedElements = new TreeSet<>();


    protected Queue() {
        super();

    }

    protected Queue(final EntityReference executionArea, final EntityReference waitingArea, final LocationReference locationRef,
                    final SiteReference siteReference) {
        super();
        this.setExecutionArea(executionArea);
        this.setWaitingArea(waitingArea);
        this.setExecutionLocationReference(locationRef);
        this.setSite(siteReference);
    }

    public int getQueuedElementsCount() {
        return queuedElementsCount;
    }

    private void setQueuedElementsCount(final int queuedElementsCount) {
        this.queuedElementsCount = queuedElementsCount;
    }

    /**
     * Returns an unmodifiable and sorted view of the set of queued elements.
     * <p>
     * This method provides access to the current sorted set of {@link QueuedElement} instances,
     * ensuring that the returned set cannot be modified by the caller. Any attempt to
     * modify the returned set will result in an {@link UnsupportedOperationException}.
     * </p>
     *
     * @return an unmodifiable and sorted set of {@link QueuedElement} instances
     */
    public SortedSet<QueuedElement> getQueuedElements() {
        return Collections.unmodifiableSortedSet(queuedElements);
    }

    @JsonIgnore
    public QueuedElement queue(final EntityReference externalEntityReference, EntityManager entityManager, boolean scheduled,
                         final Producer<ElementEnqueuedEvent> elementEnqueuedEventProducer) {
        QueuedElement queuedElement = this.createQueuedElement(externalEntityReference, scheduled);
        if (this.queuedElements.add(queuedElement)) {
            entityManager.persist(queuedElement);
            this.setQueuedElementsCount(this.getQueuedElements().size());
            elementEnqueuedEventProducer.fire(new ElementEnqueuedEvent(
                this.toEntityReference(),
                this.getWaitingArea(),
                this.getExecutionArea(),
                new QueuedElementReference(queuedElement)
            ));
            return queuedElement;
        }
        return null;
    }

    //This method is useful for test purposes
    QueuedElement createQueuedElement(final EntityReference externalEntityReference, boolean scheduled){
        return new QueuedElement(externalEntityReference, scheduled, this.toEntityReference(),
                          this.getExecutionLocationReference());
    }

    @JsonIgnore
    public boolean dequeue(final QueuedElement queuedElement,
                           final Producer<ElementDequeuedEvent> elementDequeuedEventProducer) {
        if (this.queuedElements.remove(queuedElement)) {
            this.setQueuedElementsCount(this.getQueuedElements().size());
            elementDequeuedEventProducer.fire(new ElementDequeuedEvent(
                this.toEntityReference(),
                this.getWaitingArea(),
                this.getExecutionArea(),
                this.getExecutionLocationReference(),
                new QueuedElementReference(queuedElement),
                queuedElement.getExternalEntityRef()));
                queuedElement.removeFromQueue();
            return true;
        }
        return false;
    }

    public EntityReference toEntityReference(){
        return new EntityReference(MODULE_NAME, getClass().getSimpleName(), String.valueOf(this.getId()), null, this.getExecutionLocationReference().getBusinessKey());
    }
}

---------- End of ./Queue.java ----------

---------- Start of ./AbstractArea.java ----------
package eu.reply.lea.services.yms.task.queue;


import static eu.reply.lea.services.yms.ModuleConfiguration.MODULE_NAME;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonIgnore;

import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutAreaEvent;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInAreaEvent;
import eu.reply.lea.services.yms.task.queue.exception.QueueException;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StringCustomizableEntity;
import eu.reply.sce.backend.core.entity.configuration.BusinessKey;
import eu.reply.sce.backend.core.jee.event.Producer;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@BusinessKey(attributes = {"locationReference.businessKey"})
@Table(indexes = {
    @Index(columnList = "location_entityId,location_entityName,location_module")
})
public abstract class AbstractArea extends StringCustomizableEntity {

    @NotNull
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "location_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "location_entityName")),
        @AttributeOverride(name = "entityId", column = @Column(name = "location_entityId")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "location_businessKey")),
    })
    @Getter
    private LocationReference locationReference;

    @NotNull
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private Integer nominalCapacity;

    @NotNull
    @Getter
    private Integer effectiveCapacity;

    @ManyToMany(fetch = FetchType.LAZY)
    private final Set<QueuedElement> elementsInside = new HashSet<>();

    @Getter
    private Integer numberOfElementsInside = 0;


    public AbstractArea(final LocationReference locationReference, final Integer nominalCapacity) {
        this.setLocationReference(locationReference);
        this.setNominalCapacity(nominalCapacity);
        this.setEffectiveCapacity(nominalCapacity);
    }

    protected AbstractArea() {

    }

    private void setLocationReference(final LocationReference locationReference) {
        this.locationReference = locationReference;
    }

    @JsonIgnore
    public Set<QueuedElement> getElementsInside() {
        return Collections.unmodifiableSet(this.elementsInside);
    }

    private void setNumberOfElementsInside(final Integer numberOfElementsInside) {
        this.numberOfElementsInside = numberOfElementsInside;
    }

    protected void setEffectiveCapacity(Integer effectiveCapacity) {
        if (effectiveCapacity > this.getNominalCapacity()) {
            throw new QueueException(QueueException.Codes.DOCK_AVAILABLE_CAPACITY_EXCEEDED);
        } else if (effectiveCapacity < 0) {
            throw new QueueException(QueueException.Codes.DOCK_AVAILABLE_CAPACITY_NEGATIVE);
        }
        this.effectiveCapacity = effectiveCapacity;
    }

    public boolean contains(QueuedElement element) {
        return this.elementsInside.contains(element);
    }

    public boolean add(QueuedElement element, Producer<? extends QueuedElementInAreaEvent> inAreaEventProducer) {
        if (!this.canAdd()) {
            return false;
        }
        boolean added = this.elementsInside.add(element);
        if (added) {
            this.setNumberOfElementsInside(this.elementsInside.size());
            fireInAreaEvent(inAreaEventProducer, element);
        }
        return added;
    }

    public boolean add(Set<QueuedElement> elements,
                       Producer<? extends QueuedElementInAreaEvent> inAreaEventProducer) {
        boolean atLeastOneAdded = false;
        for (QueuedElement element : elements) {
            if (!this.canAdd()) {
                break;
            }
            boolean added = this.add(element,
                                     inAreaEventProducer);
            if (added) {
                atLeastOneAdded = true;
            }
        }
        return atLeastOneAdded;
    }

    public abstract void fireInAreaEvent(Producer<? extends QueuedElementInAreaEvent> inAreaEventProducer,
                                         QueuedElement element);


    public boolean remove(QueuedElement element,
                          Producer<? extends QueuedElementOutAreaEvent> outAreaEventProducer) {
        boolean isRemoved = this.elementsInside.remove(element);
        if (isRemoved) {
            onRemoval(element, outAreaEventProducer);
        }
        return isRemoved;
    }

    protected void onRemoval(QueuedElement element,
                             Producer<? extends QueuedElementOutAreaEvent> outAreaEventProducer) {
        this.setNumberOfElementsInside(this.elementsInside.size());
        fireOutAreaEvent(outAreaEventProducer, element);
    }

    public abstract void fireOutAreaEvent(Producer<? extends QueuedElementOutAreaEvent> outAreaEventProducer,
                                          QueuedElement element);


    protected Boolean isFull() {
        return getNumberOfElementsInside() >= getEffectiveCapacity();
    }


    public abstract Boolean canAdd();


    public EntityReference toEntityReference() {
        return new EntityReference(MODULE_NAME, getClass().getSimpleName(), String.valueOf(this.getId()), null,
                                   this.getLocationReference().getBusinessKey());
    }

}

---------- End of ./AbstractArea.java ----------

---------- Start of ./QueueProcessTrackerTask.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Entity;

import lombok.Getter;

import eu.reply.lea.services.yms.task.TaskReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;

@Entity
@Getter
public class QueueProcessTrackerTask extends StandardEntity {

    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "taskReference_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "taskReference_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "taskReference_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "taskReference_businessKey"))})
    private TaskReference taskReference;

    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "queueProcessTrackerReference_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "queueProcessTrackerReference_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "queueProcessTrackerReference_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "queueProcessTrackerReference_businessKey"))})
    private EntityReference queueProcessTrackerReference;

    public QueueProcessTrackerTask() {
    }

    public QueueProcessTrackerTask(final TaskReference taskReference,
                                   final EntityReference queueProcessTrackerReference) {
        this.taskReference = taskReference;
        this.queueProcessTrackerReference = queueProcessTrackerReference;
    }


    @Override
    public boolean equals(Object anObject) {
        boolean equalObjects = false;

        if (anObject != null && this.getClass() == anObject.getClass()) {
            QueueProcessTrackerTask typedObject = (QueueProcessTrackerTask) anObject;
            equalObjects =
                this.getTaskReference().equals(typedObject.getTaskReference()) &&
                this.getQueueProcessTrackerReference().equals(typedObject.getQueueProcessTrackerReference());
        }

        return equalObjects;
    }

    @Override
    public int hashCode() {
        int hashCodeValue =
            + (12289 * 97)
            + this.getTaskReference().hashCode()
            + this.getQueueProcessTrackerReference().hashCode();

        return hashCodeValue;
    }

}

---------- End of ./QueueProcessTrackerTask.java ----------

---------- Start of ./QueueProcessTracker.java ----------
package eu.reply.lea.services.yms.task.queue;

import static eu.reply.lea.services.yms.ModuleConfiguration.MODULE_NAME;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.JoinTable;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import lombok.Getter;
import lombok.NoArgsConstructor;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.TaskReference;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCreatedEvent;
import eu.reply.lea.services.yms.task.queue.exception.DequeueProcessPendingException;
import eu.reply.lea.services.yms.task.queue.exception.EmptyQueueRequestException;
import eu.reply.lea.services.yms.task.queue.exception.EnqueueProcessPendingException;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;
import eu.reply.sce.backend.core.jee.event.Producer;


/**
 * Tracks the queuing process of transits through multiple tasks.
 * This class manages the assignment of tasks to the queue for execution.
 * It handles tasks pending queue placement and queued tasks.
 */
@NoArgsConstructor
@Entity
@NamedQueries(value = {
    @NamedQuery(name = QueueProcessTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE, query =
        "select tq from QueueProcessTracker tq where tq.logisticUnitReference.entityId = :logisticUnitReferenceId "
        + "and tq.logisticUnitReference.entityName = :logisticUnitReferenceEntityName "
        + "and tq.logisticUnitReference.module = :logisticUnitReferenceModule"),
    @NamedQuery(name = QueueProcessTracker.Q_FIND_BY_PROCESS_REFERENCE, query =
        "select tq from QueueProcessTracker tq where " +
        "(tq.executingEnqueueProcessReference.entityId = :executingProcessReferenceId " +
        "and tq.executingEnqueueProcessReference.entityName = :executingProcessReferenceEntityName " +
        "and tq.executingEnqueueProcessReference.module = :executingProcessReferenceModule) " +
        "or " +
        "(tq.executingDequeueProcessReference.entityId = :executingProcessReferenceId " +
        "and tq.executingDequeueProcessReference.entityName = :executingProcessReferenceEntityName " +
        "and tq.executingDequeueProcessReference.module = :executingProcessReferenceModule)")})
@Table(indexes = {
    @Index(columnList = "logisticUnitRef_entityId,logisticUnitRef_entityName,logisticUnitRef_module"),
    @Index(columnList = "executingEnqueueProcessReference_entityId,executingEnqueueProcessReference_entityName,executingEnqueueProcessReference_module"),
    @Index(columnList = "executingDequeueProcessReference_entityId,executingDequeueProcessReference_entityName,executingDequeueProcessReference_module")
})
public class QueueProcessTracker extends StandardEntity {


    public static final String Q_FIND_BY_LOGISTIC_UNIT_REFERENCE = "QueueProcessTracker.Q_FIND_BY_LOGISTIC_UNIT_REFERENCE";
    public static final String Q_FIND_BY_PROCESS_REFERENCE = "QueueProcessTracker.Q_FIND_BY_PROCESS_REFERENCE";

    @NotNull
    @Getter
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "logisticUnitRef_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "logisticUnitRef_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "logisticUnitRef_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "logisticUnitRef_businessKey")),
        @AttributeOverride(name = "usageType", column = @Column(name = "logisticUnitRef_usageType"))
    })
    private EntityReference logisticUnitReference;


    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinTable(
        name = "QueueProcessTracker_tasksPendingQueuePlacement"
    )
    private List<QueueProcessTrackerTask> tasksPendingQueuePlacement = new ArrayList<>();

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinTable(
        name = "QueueProcessTracker_tasksPendingDequeue"
    )
    private List<QueueProcessTrackerTask> tasksPendingDequeue = new ArrayList<>();


    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinTable(
        name = "QueueProcessTracker_tasksQueued"
    )
    private List<QueueProcessTrackerTask> tasksQueued = new ArrayList<>();

    @Getter
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "executingEnqueueProcessReference_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "executingEnqueueProcessReference_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "executingEnqueueProcessReference_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "executingEnqueueProcessReference_businessKey"))})
    private ProcessReference executingEnqueueProcessReference;

    @Getter
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "entityId", column = @Column(name = "executingDequeueProcessReference_entityId", length = 50)),
        @AttributeOverride(name = "entityName", column = @Column(name = "executingDequeueProcessReference_entityName")),
        @AttributeOverride(name = "module", column = @Column(name = "executingDequeueProcessReference_module", length = 20)),
        @AttributeOverride(name = "businessKey", column = @Column(name = "executingDequeueProcessReference_businessKey"))})
    private ProcessReference executingDequeueProcessReference;


    @Getter
    public boolean isEnqueueProcessPending = false;

    @Getter
    public boolean isDequeueProcessPending = false;

    /**
     * Retrieves a read-only list of tasks that are pending placement in the queue.
     * @return A list of {@link QueueProcessTrackerTask}.
     */
    public List<QueueProcessTrackerTask> getTasksPendingQueuePlacement() {
        return Collections.unmodifiableList(tasksPendingQueuePlacement);
    }

    /**
     * Retrieves a read-only list of tasks that are pending dequeue.
     * @return A list of {@link QueueProcessTrackerTask}.
     */
    public List<QueueProcessTrackerTask> getTasksPendingDequeue() {
        return Collections.unmodifiableList(tasksPendingDequeue);
    }

    /**
     * Adds a task to the list of tasks pending queue placement.
     * @param taskReference The task reference to be added.
     * @return true if the task was added successfully.
     */
    protected boolean addTaskPendingQueuePlacement(final TaskReference taskReference){
        return this.tasksPendingQueuePlacement.add(new QueueProcessTrackerTask(taskReference, this.toEntityReference()));
    }

    /**
     * Adds a task to the list of tasks pending dequeue.
     * @param taskReference The task reference to be added.
     * @return true if the task was added successfully.
     */
    protected boolean addTaskPendingDequeue(final TaskReference taskReference){
        return this.tasksPendingDequeue.add(new QueueProcessTrackerTask(taskReference, this.toEntityReference()));
    }

    /**
     * Checks if a given task is queued.
     * @param taskReferences The task reference to check.
     * @return true if the task is queued, false otherwise.
     */
    public boolean isTaskQueued(final TaskReference taskReferences){
        return this.tasksQueued.contains(new QueueProcessTrackerTask(taskReferences, this.toEntityReference()));
    }

    /**
     * Checks if a given task is pending queue placement.
     * @param taskReferences The task reference to check.
     * @return true if the task is pending queue placement, false otherwise.
     */
    public boolean isTaskPendingQueuePlacement(final TaskReference taskReferences){
        return this.tasksPendingQueuePlacement.contains(new QueueProcessTrackerTask(taskReferences, this.toEntityReference()));
    }


    /**
     * Retrieves a read-only list of tasks that have been placed in the queue.
     * @return A list of {@link TaskReference}.
     */
    public List<QueueProcessTrackerTask> getTasksQueued() {
        return Collections.unmodifiableList(tasksQueued);
    }

    public QueueProcessTracker(final EntityReference logisticUnitReference) {
        this.logisticUnitReference = logisticUnitReference;
    }


    /**
     * Sets the process reference and flags the process as pending. It also fires a creation event for the element queue process.
     * @param processReference The process reference to set.
     * @param elementQueueProcessCreatedEventProducer Producer to fire the event.
     */
    public void setExecutingEnqueueProcessReference(final ProcessReference processReference,
                                                    final Producer<ElementQueueProcessCreatedEvent> elementQueueProcessCreatedEventProducer) {
        this.executingEnqueueProcessReference = processReference;
        this.setEnqueueProcessPending(true);
        elementQueueProcessCreatedEventProducer.fire(new ElementQueueProcessCreatedEvent(processReference));
    }

    /**
     * Sets the process reference and flags the process as pending. It also fires a creation event for the element dequeue process.
     * @param processReference The process reference to set.
     * @param elementQueueProcessCreatedEventProducer Producer to fire the event.
     */
    public void setExecutingDequeueProcessReference(final ProcessReference processReference,
                                                    final Producer<ElementQueueProcessCreatedEvent> elementQueueProcessCreatedEventProducer) {
        this.executingDequeueProcessReference = processReference;
        this.setDequeueProcessPending(true);
        elementQueueProcessCreatedEventProducer.fire(new ElementQueueProcessCreatedEvent(processReference));
    }

    private void setEnqueueProcessPending(final boolean enqueueProcessPending) {
        this.isEnqueueProcessPending = enqueueProcessPending;
    }

    private void setDequeueProcessPending(final boolean dequeueProcessPending) {
        this.isDequeueProcessPending = dequeueProcessPending;
    }

    /**
     * Completes the current process, moves all pending tasks to the queued list, and clears pending tasks.
     */
    public void completedEnqueueProcess(){
        this.executingEnqueueProcessReference = null;
        this.tasksQueued.addAll(this.getTasksPendingQueuePlacement());
        this.tasksPendingQueuePlacement.clear();
        this.setEnqueueProcessPending(false);
    }

    /**
     * Completes the current process, moves all pending tasks to the queued list, and clears pending tasks.
     */
    public void completedDequeueProcess(){
        this.executingDequeueProcessReference = null;
        this.tasksQueued.removeAll(this.getTasksPendingDequeue());
        this.tasksPendingDequeue.clear();
        this.setDequeueProcessPending(false);
    }

    /**
     * Aborts the current queue process, clearing all pending tasks and resetting the process state.
     */
    public void abortedEnqueueProcess(){
        this.executingEnqueueProcessReference = null;
        this.tasksPendingQueuePlacement.clear();
        this.setEnqueueProcessPending(false);
    }

    /**
     * Creates a element queue process for the queueRequest, ensuring no process is currently pending.
     * @param queueRequest request that generates the element queue process.
     * @return {@link ElementEnqueueProcess} Newly created element queue process.
     */
    public ElementEnqueueProcess createElementQueueProcess(final QueueRequest queueRequest) {
        if (this.isEnqueueProcessPending()) {
            throw new EnqueueProcessPendingException();
        }

        if (this.isDequeueProcessPending()) {
            throw new DequeueProcessPendingException();
        }
        ElementEnqueueProcess elementEnqueueProcess = new ElementEnqueueProcess(logisticUnitReference);
        boolean taskAdded = false;
        for (QueueRequestInfo queueRequestInfo : queueRequest.getTaskInfos()){
            if (isTaskQueued(queueRequestInfo.getTaskReference())){
                continue;
            }
            taskAdded = true;
            elementEnqueueProcess.addExecutionLocation(queueRequestInfo.getExecutionLocation(), queueRequestInfo.isScheduled());
            this.addTaskPendingQueuePlacement(queueRequestInfo.getTaskReference());
        }
        if(!taskAdded){
            throw new EmptyQueueRequestException();
        }
        return elementEnqueueProcess;
    }

    /**
     * Creates an element dequeue process for the next location destinations, ensuring no dequeue process is currently pending.
     * @param nextLocation List of next location destinations to dequeue from.
     * @return {@link ElementDequeueProcess} Newly created element dequeue process, or null if no locations to dequeue from.
     * @throws IllegalStateException if a dequeue process is already pending.
     */
    public ElementDequeueProcess createElementDequeueProcess(final List<LocationReference>  nextLocation) {
        if (this.isDequeueProcessPending()) {
            throw new IllegalStateException("It's not possible to create a new dequeue process since one is still running.");
        }
        if (nextLocation.isEmpty()){ //No Location to dequeue from
            return null;
        }
        ElementDequeueProcess elementDequeueProcess = new ElementDequeueProcess(logisticUnitReference, nextLocation);//TODO scheduled here is not appropriate
        return elementDequeueProcess;
    }



    public EntityReference toEntityReference() {
        return new EntityReference(MODULE_NAME, getClass().getSimpleName(), String.valueOf(this.getId()), null, "-");
    }


}

---------- End of ./QueueProcessTracker.java ----------

---------- Start of ./AreaType.java ----------
package eu.reply.lea.services.yms.task.queue;

public enum AreaType {

    EXECUTION_AREA {
        public boolean isExecutionArea() {
            return true;
        }
    },
    WAITING_AREA {
        public boolean isWaitingArea() {
            return true;
        }
    },
    BOOKING_AREA {
        public boolean isBookingArea() {
            return true;
        }
    };


    public boolean isExecutionArea() {
        return false;
    }

    public boolean isWaitingArea() {
        return false;
    }

    public boolean isBookingArea() {
        return false;
    }

}
---------- End of ./AreaType.java ----------

---------- Start of ./ElementQueueProcess.java ----------
package eu.reply.lea.services.yms.task.queue;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCompletedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessPartialConfirmEvent;
import eu.reply.lea.services.yms.util.AbstractProcess;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.event.Producer;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Table(indexes = {
    @Index(name="idx_process_external_entity_status", columnList = "externalEntityRef_entity_id, externalEntityRef_entity_name, externalEntityRef_module, processCompletionType")
})
public abstract class ElementQueueProcess extends AbstractProcess {

    @OneToMany(mappedBy = "elementQueueProcess", cascade = CascadeType.ALL)
    private final List<QueueProcessItem> queueProcessItems = new ArrayList<>();

    @Getter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "externalEntityRef_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "externalEntityRef_entity_name")),
        @AttributeOverride(name = "entityId", column = @Column(name = "externalEntityRef_entity_id")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "externalEntityRef_business_key")),
        @AttributeOverride(name = "usageType", column = @Column(name = "externalEntityRef_usage_type"))
    })
    private EntityReference externalEntityReference;

    protected ElementQueueProcess() {
        super();
    }

    public ElementQueueProcess(final EntityReference externalEntityReference){
        this.externalEntityReference = externalEntityReference;
    }

    public List<QueueProcessItem> getQueueProcessItems() {
        return Collections.unmodifiableList(this.queueProcessItems);
    }

    /**
     * Confirms the process at a specific location and fires events based on the process state.
     *
     * @param locationReference                              The location to confirm.
     * @param elementQueueProcessPartialConfirmEventProducer Producer for firing partial confirmation events.
     * @param elementQueueProcessCompletedEventProducer      Producer for firing completion events.
     * @throws IllegalStateException if the process has already been completed.
     */
    public void confirm(final LocationReference locationReference,
                        final Producer<ElementQueueProcessPartialConfirmEvent> elementQueueProcessPartialConfirmEventProducer,
                        final Producer<ElementQueueProcessCompletedEvent> elementQueueProcessCompletedEventProducer) {
        if (this.isCompleted()) {
            throw new IllegalStateException("The process has already been completed");
        }
        boolean confirmed = this.confirmElement(locationReference);
        this.completeProcess(ProcessCompletionType.CompletedNormally);
        if (!this.isCompleted() && confirmed) {
            elementQueueProcessPartialConfirmEventProducer.fire(
                new ElementQueueProcessPartialConfirmEvent(new ProcessReference(this)));
        }
        if (this.isCompleted()) {
            elementQueueProcessCompletedEventProducer.fire(
                new ElementQueueProcessCompletedEvent(new ProcessReference(this), externalEntityReference,
                                                      this.getProcessCompletionType()));
        }
    }

    /**
     * Aborts the process at a specific location and fires a completion event if the process is completed.
     *
     * @param executionLocation                         The location where the process should be aborted.
     * @param elementQueueProcessCompletedEventProducer Producer for firing completion events.
     * @throws IllegalStateException if the process has already been completed.
     */
    public void abort(final LocationReference executionLocation,
                      final Producer<ElementQueueProcessCompletedEvent> elementQueueProcessCompletedEventProducer) {
        if (this.isCompleted()) {
            throw new IllegalStateException("The process has already been completed");
        }
        this.queueProcessItems.stream().filter(
            queueProcessItem -> queueProcessItem.getExecutionLocation().equals(executionLocation)).forEach(
            queueProcessItem -> queueProcessItem.abort()
        );
        this.abortProcess(ProcessCompletionType.Aborted);
        if (this.isCompleted()) {
            elementQueueProcessCompletedEventProducer.fire(
                new ElementQueueProcessCompletedEvent(new ProcessReference(this), externalEntityReference,
                                                      this.getProcessCompletionType()));
        }
    }

    private boolean confirmElement(final LocationReference locationReference) {
        Long numberOfInitialConfirmedElements = this.numberOfConfirmations();
        this.queueProcessItems.stream().filter(
            queueProcessItem -> queueProcessItem.getExecutionLocation().equals(locationReference)).forEach(
            queueProcessItem -> queueProcessItem.confirm()
        );
        Long numberOfFinalConfirmedElements = this.numberOfConfirmations();
        return numberOfFinalConfirmedElements.compareTo(numberOfInitialConfirmedElements) > 0;
    }

    private Long numberOfConfirmations() {
        return this.queueProcessItems.stream().filter(QueueProcessItem::isConfirmed).count();
    }

    /**
     * Add a new execution location.
     *
     * @param executionLocations List of locations to be added.
     * @param scheduled boolean to indicate if the queueing was scheduled
     * @return A read-only list of location statuses.
     */
    public boolean addExecutionLocation(final LocationReference executionLocations,
                                                 final boolean scheduled) {
        return queueProcessItems.add(new QueueProcessItem(this,
                                                          executionLocations, scheduled,
                                                          queueProcessItems.size()));
    }



    /**
     * Verifies if all element locations within the process have been confirmed.
     * It is a crucial part of the process lifecycle, ensuring that no further action is required for this process
     * to be considered complete.
     *
     * @return true if all element location statuses are confirmed, false otherwise.
     */
    @Override
    protected boolean completenessVerified() {
        return queueProcessItems.stream().allMatch(QueueProcessItem::isConfirmed);
    }

    @Override
    protected boolean abortionVerified() {
        return queueProcessItems.stream().allMatch(el -> el.isAborted() || !el.isStarted());
    }

    /**
     * Retrieves the next Item containing information about the next queue location.
     *
     * @return The next QueueProcessItem to be confirmed, or null if all locations are confirmed.
     */
    @JsonIgnore
    public QueueProcessItem getNextQueueProcessItem() {
        QueueProcessItem queueProcessItem = null;

        for (QueueProcessItem status : this.queueProcessItems) {
            if (!status.isConfirmed() && (queueProcessItem == null
                                          || status.getEnqueueOrder() < queueProcessItem.getEnqueueOrder())) {
                queueProcessItem = status;
            }
        }

        return queueProcessItem;
    }

    /**
     * Marks the queueing process as started for a specific location.
     *
     * @param executionLocation The location where queueing has started.
     */
    public void started(final LocationReference executionLocation) {
        for (QueueProcessItem queueProcessItem : this.queueProcessItems) {
            if (queueProcessItem.getExecutionLocation().equals(executionLocation)) {
                queueProcessItem.start();
            }
        }
    }

}

---------- End of ./ElementQueueProcess.java ----------

---------- Start of ./QueueRequest.java ----------
package eu.reply.lea.services.yms.task.queue;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Represents a queue request consisting of a list of tasks that need to be processed.
 * The tasks are stored in reverse order of their addition.
 */
public class QueueRequest {


    private final List<QueueRequestInfo> queueRequestInfos;

    /**
     * Constructs a {@code QueueRequest} with the provided list of task information.
     * The list of tasks is reversed to maintain a specific ordering.
     *
     * @param queueRequestInfos the list of task information to be included in the queue request
     */
    public QueueRequest(List<QueueRequestInfo> queueRequestInfos) {
        this.queueRequestInfos = new ArrayList<>(queueRequestInfos);
        Collections.reverse(this.queueRequestInfos);
    }

    /**
     * Checks if the queue request is empty, i.e., contains no tasks.
     *
     * @return {@code true} if the queue request has no tasks, {@code false} otherwise
     */
    public boolean isEmpty() {
        return queueRequestInfos.isEmpty();
    }

    /**
     * Returns an unmodifiable list of task information contained in the queue request.
     *
     * @return an unmodifiable list of {@code TaskInfo} objects
     */
    public List<QueueRequestInfo> getTaskInfos() {
        return Collections.unmodifiableList(queueRequestInfos);
    }
}

---------- End of ./QueueRequest.java ----------

---------- Start of ./ExecutionArea.java ----------
package eu.reply.lea.services.yms.task.queue;


import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

import com.fasterxml.jackson.annotation.JsonIgnore;

import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementOutExecutionAreaEvent;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.ExecutionAreaUnblockedEvent;
import eu.reply.lea.services.yms.task.queue.event.QueuedElementInExecutionAreaEvent;
import eu.reply.lea.services.yms.task.queue.event.PreparationTerminatedEvent;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.configuration.BusinessKey;
import eu.reply.sce.backend.core.jee.event.Producer;

@Entity
@BusinessKey(attributes = {"locationReference.businessKey"})
@NamedQueries(value = {
    @NamedQuery(name = ExecutionArea.Q_FIND_BY_LOCATION_REFERENCE, query =
        "select e from ExecutionArea e where e.locationReference.entityId = :locationReferenceId "
        + "and e.locationReference.entityName = :locationReferenceEntityName "
        + "and e.locationReference.module = :locationReferenceModule"),
})
public class ExecutionArea extends AbstractArea {

    public static final String Q_FIND_BY_LOCATION_REFERENCE = "ExecutionArea.Q_FIND_BY_LOCATION_REFERENCE";

    @Getter
    @Setter
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "module", column = @Column(name = "queue_module")),
        @AttributeOverride(name = "entityName", column = @Column(name = "queue_entityName")),
        @AttributeOverride(name = "entityId", column = @Column(name = "queue_entityId")),
        @AttributeOverride(name = "businessKey", column = @Column(name = "queue_businessKey")),
        @AttributeOverride(name = "usageType", column = @Column(name = "queue_usageType"))
    })
    private EntityReference queueReference;

    @Setter(AccessLevel.PRIVATE)
    @Getter
    private boolean preparationRequired = false;


    @Getter
    private boolean blocked = false;

    public ExecutionArea(final LocationReference locationReference, final Integer nominalCapacity) {
        super(locationReference, nominalCapacity);
    }

    public void block(){
        this.blocked = true;
    }

    public void unblock(Producer<ExecutionAreaUnblockedEvent> executionAreaUnblockedEventProducer){
        this.blocked = false;
        executionAreaUnblockedEventProducer.fire(new ExecutionAreaUnblockedEvent(this.toEntityReference(),this.queueReference));
    }

    protected ExecutionArea() {
        super();
    }


    public Boolean canAdd() {
        if (this.isFull() || this.isBlocked()) {
            return false;
        }
        return true;
    }


    @JsonIgnore
    public void terminatePreparation(Producer<PreparationTerminatedEvent> eventProducer) {
        this.setEffectiveCapacity(this.getEffectiveCapacity() + 1);
        eventProducer.fire(new PreparationTerminatedEvent(this.toEntityReference(), this.getQueueReference()));
    }

    @JsonIgnore
    public void enablePreparation(){
        if (this.isPreparationRequired()) {
            throw new IllegalStateException("The preparation is already enabled");
        }
        this.setEffectiveCapacity(this.getNumberOfElementsInside());
        this.setPreparationRequired(true);
    }

    @JsonIgnore
    public void disablePreparation(){
        if (!this.isPreparationRequired()) {
            throw new IllegalStateException("The preparation is already disabled");
        }
        this.setEffectiveCapacity(this.getNominalCapacity());
        this.setPreparationRequired(false);
    }

    @Override
    public boolean add(QueuedElement element, Producer<? extends QueuedElementInAreaEvent> inAreaEventProducer) {
        if(super.add(element, inAreaEventProducer)){
            element.setInExecutionArea(this);
            return true;
        }
        return false;
    }

    @Override
    protected void onRemoval(final QueuedElement element,
                             final Producer<? extends QueuedElementOutAreaEvent> outAreaEventProducer) {
        if (this.isPreparationRequired()) {
            this.setEffectiveCapacity(this.getEffectiveCapacity() - 1);
        }
        super.onRemoval(element, outAreaEventProducer);
    }

    public void fireInAreaEvent(Producer<? extends QueuedElementInAreaEvent> inExecutionAreaProducer, QueuedElement element) {
        ((Producer<QueuedElementInExecutionAreaEvent>) inExecutionAreaProducer).fire(
            new QueuedElementInExecutionAreaEvent(element.toEntityReference(),
                                                  element.getExternalEntityRef(),
                                                  this.toEntityReference(),
                                                  this.getLocationReference(),
                                                  element.getExecutionLocationReference()));
    }

    @Override
    public void fireOutAreaEvent(final Producer<? extends QueuedElementOutAreaEvent> outExecutionAreaProducer,
                                 QueuedElement element) {
        ((Producer<QueuedElementOutExecutionAreaEvent>) outExecutionAreaProducer).fire(
            new QueuedElementOutExecutionAreaEvent(
                element.toEntityReference(),
                element.getExternalEntityRef(),
                this.toEntityReference(),
                this.getLocationReference(),
                element.getExecutionLocationReference()));

    }


}

---------- End of ./ExecutionArea.java ----------

---------- Start of ./ElementDequeueProcessDomainService.java ----------
package eu.reply.lea.services.yms.task.queue;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.LockModeType;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCompletedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessPartialConfirmEvent;
import eu.reply.lea.services.yms.task.queue.repository.QueueProcessTrackerRepository;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
public class ElementDequeueProcessDomainService {

    @Inject
    private QueueProcessTrackerRepository queueProcessTrackerRepository;

    @Inject
    private Producer<ElementQueueProcessPartialConfirmEvent> elementQueueProcessPartialConfirmEventProducer;

    @Inject
    private Producer<ElementQueueProcessCompletedEvent> elementQueueProcessCompletedEventProducer;

    public void confirmProcess(final ElementDequeueProcess
                                       elementDequeueProcess, final LocationReference locationReference){
        elementDequeueProcess.confirm(locationReference, elementQueueProcessPartialConfirmEventProducer,
                                      elementQueueProcessCompletedEventProducer);
        if (elementDequeueProcess.didProcessingComplete()){
            QueueProcessTracker
                transitQueueProcessTracker =  queueProcessTrackerRepository.findByQueueProcess(new ProcessReference(
                elementDequeueProcess), LockModeType.PESSIMISTIC_WRITE); //TODO This should be in the QueueProcessTracker and not here, the queue should not be ever aware of the presence of transits.
            //You should use the elementQueueProcessCompletedEvent to advise the tracker that the process is completed.
            //but this probably would make necessary create a timeout event because otherwise it could get stuck, if the message is lost
            transitQueueProcessTracker.completedDequeueProcess();
        }
    }



}

---------- End of ./ElementDequeueProcessDomainService.java ----------

---------- Start of ./DockQueue.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.*;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.masterdata.reference.SiteReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@DiscriminatorValue(value = "DOCK")
@NamedQueries(value = {
    @NamedQuery(name = DockQueue.Q_FIND_BY_LOCATION_REFERENCE, query =
        "select q from DockQueue q where q.executionLocationReference.entityId = :locationReferenceId "
        + "and q.executionLocationReference.entityName = :locationReferenceEntityName "
        + "and q.executionLocationReference.module = :locationReferenceModule"),
    @NamedQuery(name = DockQueue.Q_FIND_BY_WAITING_AREA, query =
        "select q from DockQueue q where q.waitingArea.entityId = :waitingAreaReferenceId "
        + "and q.waitingArea.entityName = :waitingAreaReferenceEntityName "
        + "and q.waitingArea.module = :waitingAreaReferenceModule"),
})
public class DockQueue extends Queue {
    
    /**
     *
     */
    private static final long serialVersionUID = 8779543330479894754L;

    public static final String Q_FIND_BY_WAITING_AREA = "DockQueue.Q_FIND_BY_WAITING_AREA";


    public static final String Q_FIND_BY_LOCATION_REFERENCE = "DockQueue.Q_FIND_BY_LOCATION_REFERENCE";

    protected DockQueue() {
        super();
    }


    public DockQueue(final EntityReference executionArea, final EntityReference waitingArea, final LocationReference locationRef,
                     final SiteReference siteReference) {
        super(executionArea,waitingArea, locationRef, siteReference);
    }


}

---------- End of ./DockQueue.java ----------

---------- Start of ./boundary/DockQueueProcessor.java ----------
package eu.reply.lea.services.yms.task.queue.boundary;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.DockQueue;
import eu.reply.lea.services.yms.task.queue.event.DockQueueCreatedEvent;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityProcessor;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
@EntityProcessor(entityClass = DockQueue.class)
public class DockQueueProcessor extends QueueProcessor {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private Producer<DockQueueCreatedEvent> createdEventProducer;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    public void persisted(final DockQueue entity) {
        this.createdEventProducer.fire(new DockQueueCreatedEvent(entityReferenceHandler.getEntityReference(em, entity), entity.getExecutionArea()));
    }
    public void persisting(final DockQueue entity) {
        super.persisting(entity);
    }
}

---------- End of ./boundary/DockQueueProcessor.java ----------

---------- Start of ./boundary/ZoneQueueProcessor.java ----------
package eu.reply.lea.services.yms.task.queue.boundary;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.ZoneQueue;
import eu.reply.lea.services.yms.task.queue.event.ZoneQueueCreatedEvent;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityProcessor;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
@EntityProcessor(entityClass = ZoneQueue.class)
public class ZoneQueueProcessor extends QueueProcessor {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private Producer<ZoneQueueCreatedEvent> createdEventProducer;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    public void persisted(final ZoneQueue entity) {
        this.createdEventProducer.fire(
            new ZoneQueueCreatedEvent(entityReferenceHandler.getEntityReference(em, entity), entity.getExecutionArea()));
    }
    public void persisting(final ZoneQueue entity) {
        super.persisting(entity);
    }
}

---------- End of ./boundary/ZoneQueueProcessor.java ----------

---------- Start of ./boundary/GateQueueProcessor.java ----------
package eu.reply.lea.services.yms.task.queue.boundary;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.GateQueue;
import eu.reply.lea.services.yms.task.queue.event.GateQueueCreatedEvent;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityProcessor;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
@EntityProcessor(entityClass = GateQueue.class)
public class GateQueueProcessor extends QueueProcessor {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private Producer<GateQueueCreatedEvent> createdEventProducer;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    public void persisted(final GateQueue entity) {
        this.createdEventProducer.fire(
            new GateQueueCreatedEvent(entityReferenceHandler.getEntityReference(em, entity), entity.getExecutionArea()));
    }

    public void persisting(final GateQueue entity) {
        super.persisting(entity);
    }
}

---------- End of ./boundary/GateQueueProcessor.java ----------

---------- Start of ./boundary/QueueProcessor.java ----------
package eu.reply.lea.services.yms.task.queue.boundary;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.Queue;
import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityProcessor;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
@EntityProcessor(entityClass = Queue.class)
public class QueueProcessor {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    public <T extends Queue> void persisting(final T entity) {
        ExecutionArea executionArea = entityReferenceHandler.getEntity(entity.getExecutionArea(),em);
        entity.setExecutionLocationReference(executionArea.getLocationReference());
    }
}

---------- End of ./boundary/QueueProcessor.java ----------

---------- Start of ./boundary/ExecutionAreaProcessor.java ----------
package eu.reply.lea.services.yms.task.queue.boundary;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.client.MasterdataServiceException;
import eu.reply.lea.services.yard.exceptions.LoadingUnitActionException;
import eu.reply.lea.services.yms.boundary.AbstractProcessor;
import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.lea.services.yms.task.queue.event.PreparationTerminatedEvent;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityProcessor;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
@Default
@EntityProcessor(entityClass = ExecutionArea.class)
public class ExecutionAreaProcessor extends AbstractProcessor {


    @Inject
    private Producer<PreparationTerminatedEvent> preparationTerminatedEventProducer;


    public ExecutionArea terminatePreparation(ExecutionArea entity)
        throws  MasterdataServiceException, LoadingUnitActionException {
        entity.terminatePreparation(preparationTerminatedEventProducer);
        return entity;
    }

    public ExecutionArea enablePreparation(ExecutionArea entity)
        throws  MasterdataServiceException, LoadingUnitActionException {
        entity.enablePreparation();
        return entity;
    }

    public ExecutionArea disablePreparation(ExecutionArea entity)
        throws  MasterdataServiceException, LoadingUnitActionException {
        entity.disablePreparation();
        return entity;
    }

}

---------- End of ./boundary/ExecutionAreaProcessor.java ----------

---------- Start of ./QueueProcessItem.java ----------
package eu.reply.lea.services.yms.task.queue;

import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ManyToOne;

import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.StandardEntity;

@Entity
@Getter
public class QueueProcessItem extends StandardEntity {

    public enum Status {
        CREATED, STARTED {
            public boolean isStarted() {
                return true;
            }
        }, CONFIRMED {
            public boolean isStarted() {
                return true;
            }

            public boolean isConfirmed() {
                return true;
            }
        }, ABORTED {
            public boolean isStarted() {
                return true;
            }

            public boolean isAborted() {
                return true;
            }
        };

        public boolean isStarted() {
            return false;
        }

        public boolean isAborted() {
            return false;
        }

        public boolean isConfirmed() {
            return false;
        }

    }

    @Enumerated(EnumType.STRING)
    private Status status = Status.CREATED;

    private LocationReference executionLocation;

    private boolean scheduled;

    @ManyToOne
    private ElementQueueProcess elementQueueProcess;

    private int enqueueOrder;

    public QueueProcessItem(final ElementQueueProcess elementQueueProcess, final LocationReference executionLocation,
                            final boolean scheduled,
                            final int enqueueOrder) {
        this.elementQueueProcess = elementQueueProcess;
        this.executionLocation = executionLocation;
        this.scheduled = scheduled;
        this.enqueueOrder = enqueueOrder;
    }

    public QueueProcessItem() {

    }

    public boolean isConfirmed() {
        return this.status.isConfirmed();
    }

    public boolean isAborted() {
        return this.status.isAborted();
    }

    public boolean isStarted() {
        return this.status.isStarted();
    }

    void confirm() {
        this.status = Status.CONFIRMED;
    }

    void abort() {
        this.status = Status.ABORTED;
    }

    public void start() {
        this.status = Status.STARTED;
    }
}
---------- End of ./QueueProcessItem.java ----------

---------- Start of ./ElementDequeueProcess.java ----------
package eu.reply.lea.services.yms.task.queue;

import java.util.List;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

import lombok.NoArgsConstructor;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.EntityReference;


/**
 * This class handles the dequeue of the elements based on the location provided.
 */
@Entity
@NoArgsConstructor
@NamedQueries(value = {
    @NamedQuery(name = ElementDequeueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS, query =
        "select eq from ElementDequeueProcess eq left join QueuedElement el "
        + "on el.externalEntityRef.entityId = eq.externalEntityReference.entityId "
        + "and el.externalEntityRef.entityName = eq.externalEntityReference.entityName "
        + "and el.externalEntityRef.module = eq.externalEntityReference.module "
        + "where  el.id = :elementQueueId and eq.processCompletionType = :processCompletionType")
})
public class ElementDequeueProcess extends ElementQueueProcess {


    public static final String Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS = "ElementDequeueProcess.Q_FIND_BY_QUEUED_ELEMENT_AND_COMPLETENESS";

    public ElementDequeueProcess(final EntityReference externalEntityReference, final List<LocationReference> executionLocations) {
        super(externalEntityReference);
        for (LocationReference executionLocation : executionLocations){
            this.addExecutionLocation(executionLocation, false);
        }
    }

}

---------- End of ./ElementDequeueProcess.java ----------

---------- Start of ./QueuedElementReference.java ----------
package eu.reply.lea.services.yms.task.queue;


import static eu.reply.lea.services.yms.ModuleConfiguration.MODULE_NAME;

import javax.persistence.Embeddable;

import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.TypedEntityReference;

@Embeddable
public class QueuedElementReference extends TypedEntityReference<QueuedElement> {

    public QueuedElementReference(final QueuedElement queuedElement) {
        super(MODULE_NAME, queuedElement.getClass().getSimpleName(), Long.toString(queuedElement.getId()));
    }

    public QueuedElementReference() {

    }

    public QueuedElementReference(String module, String entityName, String entityId, String businessKey) {
        super(module, entityName, entityId, businessKey);
    }

    @Override
    public TypedEntityReference<QueuedElement> clone() {
        return cloneTo(new QueuedElementReference());
    }

    public EntityReference toEntityReference(){
        return new EntityReference(this.getModule(),this.getEntityName(),this.getEntityId(), null, this.getBusinessKey());
    }

}
---------- End of ./QueuedElementReference.java ----------

---------- Start of ./application/QueueAreaService.java ----------
package eu.reply.lea.services.yms.task.queue.application;

import java.util.List;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.lea.services.yms.task.queue.Queue;
import eu.reply.lea.services.yms.task.queue.QueueAreaDomainService;
import eu.reply.lea.services.yms.task.queue.QueuedElement;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.task.queue.WaitingArea;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
public class QueueAreaService {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;

    @Inject
    private QueueAreaDomainService queueAreaDomainService;


    //After enqueued event
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void addToArea(final EntityReference waitingAreaRef, final EntityReference executionAreaRef, QueuedElementReference queuedElementRef){
        ExecutionArea executionArea = entityReferenceHandler.getEntity(executionAreaRef,em);
        WaitingArea waitingArea = entityReferenceHandler.getEntity(waitingAreaRef,em);
        QueuedElement queuedElement = entityReferenceHandler.getEntity(queuedElementRef.toEntityReference(),em);
        queueAreaDomainService.addToArea(executionArea, waitingArea, queuedElement);
    }

    //on removed from queue
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void removeFromArea(final EntityReference waitingAreaRef, final EntityReference executionAreaRef, final QueuedElementReference queuedElementRef){
        WaitingArea waitingArea = entityReferenceHandler.getEntity(waitingAreaRef, em);
        ExecutionArea executionArea = entityReferenceHandler.getEntity(executionAreaRef,em);
        QueuedElement queuedElement = entityReferenceHandler.getEntity(queuedElementRef.toEntityReference(),em);
        queueAreaDomainService.removeFromArea(executionArea,waitingArea, queuedElement);
    }

    //on removed from execution area and on added to waiting area
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void addNextOnExecutionArea(final EntityReference executionAreaRef) {
        ExecutionArea executionArea = entityReferenceHandler.getEntity(executionAreaRef,em);
        Queue queue = entityReferenceHandler.getEntity(executionArea.getQueueReference(), em);
        queueAreaDomainService.addNextOnExecutionArea(executionArea, queue);
    }

    //on removed from waiting area
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void addNextOnWaitingArea(final EntityReference waitingArea) {
        List<Queue> queues = em.createNamedQuery(Queue.Q_FIND_BY_WAITING_AREA)
            .setParameter("waitingAreaReferenceId", waitingArea.getEntityId())
            .setParameter("waitingAreaReferenceEntityName", waitingArea.getEntityName())
            .setParameter("waitingAreaReferenceModule", waitingArea.getModule())
            .getResultList();
        queueAreaDomainService.addNextOnWaitingArea(queues);
    }


}

---------- End of ./application/QueueAreaService.java ----------

---------- Start of ./application/WaitingAreaService.java ----------
package eu.reply.lea.services.yms.task.queue.application;


import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.WaitingArea;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
public class WaitingAreaService {


    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    @Inject
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityRefHandler;


    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void createWaitingArea(final LocationReference entityReference, final int capacity) {
        WaitingArea waitingArea = new WaitingArea(entityReference,
                                                  capacity);
        em.persist(waitingArea);
    }


    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public WaitingArea getWaitingArea(final EntityReference waitingAreaReference) {
        return entityRefHandler.getEntity(waitingAreaReference, em);
    }
}

---------- End of ./application/WaitingAreaService.java ----------

---------- Start of ./application/ExecutionAreaService.java ----------
package eu.reply.lea.services.yms.task.queue.application;


import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.ExecutionArea;
import eu.reply.lea.services.yms.task.queue.event.ExecutionAreaUnblockedEvent;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@ApplicationScoped
@Logged
public class ExecutionAreaService {

    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    @Inject
    private EntityManager em;

    @Inject
    private Producer<ExecutionAreaUnblockedEvent> executionAreaUnblockedEventProducer;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void createExecutionArea(final LocationReference entityReference, final int capacity) {
        ExecutionArea executionArea = new ExecutionArea(entityReference,capacity);
        em.persist(executionArea);
    }


    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void unblockExecutionArea(final EntityReference entityReference){
        ExecutionArea executionArea = em.createNamedQuery(ExecutionArea.Q_FIND_BY_LOCATION_REFERENCE, ExecutionArea.class)
            .setParameter("locationReferenceId", entityReference.getEntityId())
            .setParameter("locationReferenceEntityName", entityReference.getEntityName())
            .setParameter("locationReferenceModule", entityReference.getModule())
            .getSingleResult();
        executionArea.unblock(executionAreaUnblockedEventProducer);
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void blockExecutionArea(final EntityReference entityReference){
        ExecutionArea executionArea = em.createNamedQuery(ExecutionArea.Q_FIND_BY_LOCATION_REFERENCE, ExecutionArea.class)
            .setParameter("locationReferenceId", entityReference.getEntityId())
            .setParameter("locationReferenceEntityName", entityReference.getEntityName())
            .setParameter("locationReferenceModule", entityReference.getModule())
            .getSingleResult();
        executionArea.block();
    }
}

---------- End of ./application/ExecutionAreaService.java ----------

---------- Start of ./application/QueueProcessTrackerService.java ----------
package eu.reply.lea.services.yms.task.queue.application;

import java.util.List;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.TaskReference;
import eu.reply.lea.services.yms.task.queue.ElementEnqueueProcess;
import eu.reply.lea.services.yms.task.queue.QueueProcessTracker;
import eu.reply.lea.services.yms.task.queue.QueueProcessTrackerDomainService;
import eu.reply.lea.services.yms.task.queue.repository.QueueProcessTrackerRepository;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCreatedEvent;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.event.Producer;

public class QueueProcessTrackerService {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private QueueProcessTrackerRepository queueProcessTrackerRepository;

    @Inject
    private QueueRequestService queueRequestService;

    @Inject
    private QueueProcessTrackerDomainService queueProcessTrackerDomainService;

    @Inject
    private Producer<ElementQueueProcessCreatedEvent> elementQueueProcessCreatedEventProducer;


    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void startNextQueueProcess(final EntityReference logisticUnitReference) {
        QueueProcessTracker
            queueProcessTracker = queueProcessTrackerRepository.findByLogisticUnitRef(logisticUnitReference, LockModeType.PESSIMISTIC_WRITE);
        if(queueProcessTracker == null){
            queueProcessTracker = new QueueProcessTracker(logisticUnitReference);
            em.persist(queueProcessTracker);
        }

        ElementEnqueueProcess
            elementEnqueueProcess = queueProcessTracker.createElementQueueProcess(queueRequestService.getQueueRequest(logisticUnitReference));
        em.persist(elementEnqueueProcess);
        queueProcessTracker.setExecutingEnqueueProcessReference(new ProcessReference(elementEnqueueProcess), elementQueueProcessCreatedEventProducer);
  }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void startNewDequeueProcess(final List<TaskReference> tasks,
                                       final List<LocationReference> locationReferences,
                                       final EntityReference logisticUnitReference) {
        QueueProcessTracker
            queueProcessTracker = queueProcessTrackerRepository.findByLogisticUnitRef(logisticUnitReference, LockModeType.PESSIMISTIC_WRITE);
        if(queueProcessTracker == null){
            throw new IllegalStateException(String.format("To perform a dequeue, the QueueProcessTracker for logistic unit %s has to be already created",logisticUnitReference));
        }
        queueProcessTrackerDomainService.startDequeueProcess(queueProcessTracker, tasks, locationReferences);
    }

}

---------- End of ./application/QueueProcessTrackerService.java ----------

---------- Start of ./application/QueueService.java ----------
package eu.reply.lea.services.yms.task.queue.application;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;

import eu.reply.lea.services.yms.task.queue.ElementDequeueProcess;
import eu.reply.lea.services.yms.task.queue.ElementDequeueProcessDomainService;
import eu.reply.lea.services.yms.task.queue.ElementEnqueueProcess;
import eu.reply.lea.services.yms.task.queue.Queue;
import eu.reply.lea.services.yms.task.queue.QueueProcessItem;
import eu.reply.lea.services.yms.task.queue.QueuedElement;
import eu.reply.lea.services.yms.task.queue.event.ElementDequeuedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementEnqueuedEvent;
import eu.reply.lea.services.yms.task.queue.repository.QueueRepository;
import eu.reply.lea.services.yms.task.queue.repository.QueuedElementRepository;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.jee.configuration.ModuleConfiguration;
import eu.reply.sce.backend.core.jee.configuration.entity.EntityReferenceHandler;
import eu.reply.sce.backend.core.jee.event.Producer;

public class QueueService {

    @Inject
    @ModuleConfiguration.Module(ModuleConfiguration.Module.CURRENT)
    private EntityManager em;

    @Inject
    private EntityReferenceHandler entityReferenceHandler;
    @Inject
    private QueueRepository queueRepository;
    @Inject
    private Producer<ElementEnqueuedEvent> elementEnqueuedEventProducer;
    @Inject
    private Producer<ElementDequeuedEvent> elementDequeuedEventProducer;
    @Inject
    private QueuedElementRepository queuedElementRepository;
    @Inject
    private ElementDequeueProcessDomainService elementDequeueProcessDomainService;


    @Transactional(Transactional.TxType.REQUIRED)
    public void queue(final ProcessReference processReference) {
        ElementEnqueueProcess elementEnqueueProcess =
            entityReferenceHandler.getEntity(processReference.toEntityReference(), em);
        if (elementEnqueueProcess == null) {
            return;
        }
        QueueProcessItem queueProcessItem = elementEnqueueProcess.getNextQueueProcessItem();
        if (queueProcessItem == null){
            return;
        }
        elementEnqueueProcess.started(queueProcessItem.getExecutionLocation());
        Queue queue = queueRepository.findByLocation(queueProcessItem.getExecutionLocation());
        queue.queue(elementEnqueueProcess.getExternalEntityReference(), em, queueProcessItem.isScheduled(),
                    elementEnqueuedEventProducer);
    }

    @Transactional(Transactional.TxType.REQUIRED)
    public void dequeue(final ProcessReference processReference) {
        ElementDequeueProcess elementDequeueProcess =
            entityReferenceHandler.getEntity(processReference.toEntityReference(), em);

        if (elementDequeueProcess == null) {
            return;
        }
        QueueProcessItem queueProcessItem = elementDequeueProcess.getNextQueueProcessItem();
        if (queueProcessItem == null){
            return;
        }
        elementDequeueProcess.started(queueProcessItem.getExecutionLocation());
        Queue queue = queueRepository.findByLocation(queueProcessItem.getExecutionLocation());
        QueuedElement queuedElement = queuedElementRepository.findByExternalEntityAndExecutionLocationAndIsInsideTheQueue(
            elementDequeueProcess.getExternalEntityReference(),queueProcessItem.getExecutionLocation(), true);
        if(queuedElement != null){
            queue.dequeue(queuedElement, elementDequeuedEventProducer);
        }
        elementDequeueProcessDomainService.confirmProcess(elementDequeueProcess, queueProcessItem.getExecutionLocation()); //IN case it has already been
        //Dequeue we confirm the dequeue process anyway
    }

    @Transactional(Transactional.TxType.REQUIRED)
    public Queue findQueue(EntityReference queueReference){
        return entityReferenceHandler.getEntity(queueReference, em);
    }
}

---------- End of ./application/QueueService.java ----------

---------- Start of ./application/QueuedElementService.java ----------
package eu.reply.lea.services.yms.task.queue.application;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.Queue;
import eu.reply.lea.services.yms.task.queue.repository.QueueRepository;
import eu.reply.sce.backend.core.entity.EntityReference;

public class QueuedElementService {

    @Inject
    private QueueRepository queueRepository;


    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void setPriority(final EntityReference externalEntityReference, final LocationReference locationReference, final int priority) {
        Queue queue = queueRepository.findByLocation(locationReference);
        queue.getQueuedElements().stream().filter(queuedElement ->
            queuedElement.getExternalEntityRef().equals(externalEntityReference)
        ).forEach(queuedElement -> {
            queuedElement.setPriority(priority);
        });
    }

}

---------- End of ./application/QueuedElementService.java ----------

---------- Start of ./application/QueueRequestService.java ----------
package eu.reply.lea.services.yms.task.queue.application;


import eu.reply.lea.services.yms.task.queue.QueueRequest;
import eu.reply.sce.backend.core.entity.EntityReference;

/**
 * Service interface for handling queue requests.
 * Implementations of this interface are responsible for providing
 * a {@link QueueRequest} for a given logistic unit reference.
 */
public interface QueueRequestService {

    /**
     * Retrieves a {@link QueueRequest} associated with the provided logistic unit reference.
     *
     * @param logisticUnitReference the reference of the logistic unit
     * @return the {@link QueueRequest} for the specified logistic unit
     */
    QueueRequest getQueueRequest(final EntityReference logisticUnitReference);

}

---------- End of ./application/QueueRequestService.java ----------

---------- Start of ./application/ElementEnqueueProcessService.java ----------
package eu.reply.lea.services.yms.task.queue.application;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.LockModeType;
import javax.transaction.Transactional;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.ElementEnqueueProcess;
import eu.reply.lea.services.yms.task.queue.ElementEnqueueProcessDomainService;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.lea.services.yms.task.queue.repository.ElementEnqueueProcessRepository;
import eu.reply.lea.services.yms.util.Process;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
public class ElementEnqueueProcessService {

    @Inject
    private ElementEnqueueProcessRepository elementEnqueueProcessRepository;

    @Inject
    private ElementEnqueueProcessDomainService elementEnqueueProcessDomainService;

    @Transactional(Transactional.TxType.REQUIRED)
    public void confirmProcess(final QueuedElementReference queuedElementReference, final LocationReference executionLocation){
        ElementEnqueueProcess
            elementEnqueueProcess =  elementEnqueueProcessRepository.findByQueuedElementReference(queuedElementReference, Process.ProcessCompletionType.NotCompleted);
        if (elementEnqueueProcess == null){
            return;
        }
        elementEnqueueProcessDomainService.confirmProcess(elementEnqueueProcess, executionLocation);
    }

    @Transactional(Transactional.TxType.REQUIRED)
    public void abortProcess(final QueuedElementReference queuedElementReference, final LocationReference executionLocation){
        //Pessimistic write because there can be multiple task dequeued at the same time.
        //A better approach could be to respond to the completed dequeue Process instead.
        //Doing so you would be sure that there won't be any other dequeue process running, therefore not multiple concurrent calls
        ElementEnqueueProcess
            elementEnqueueProcess =  elementEnqueueProcessRepository.findByQueuedElementReference(queuedElementReference,
                                                                                                  Process.ProcessCompletionType.NotCompleted,
                                                                                                  LockModeType.PESSIMISTIC_WRITE);
        if (elementEnqueueProcess == null){
            return;
        }
        elementEnqueueProcessDomainService.abortProcess(elementEnqueueProcess, executionLocation);
    }

}

---------- End of ./application/ElementEnqueueProcessService.java ----------

---------- Start of ./event/ElementQueueProcessCompletedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import lombok.Getter;
import lombok.NoArgsConstructor;

import eu.reply.lea.services.yms.util.Process;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.entity.EntityReference;

@NoArgsConstructor
@Getter
public class ElementQueueProcessCompletedEvent implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 597245520467284022L;

    private ProcessReference processReference;

    private EntityReference externalEntityReference;

    private Process.ProcessCompletionType processCompletionType;


    public ElementQueueProcessCompletedEvent(final ProcessReference processReference,
                                             final EntityReference externalEntityReference,
                                             final Process.ProcessCompletionType processCompletionType) {
        this.processReference = processReference;
        this.externalEntityReference = externalEntityReference;
        this.processCompletionType = processCompletionType;
    }


}

---------- End of ./event/ElementQueueProcessCompletedEvent.java ----------

---------- Start of ./event/ElementQueueProcessCreatedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.lea.services.yms.util.ProcessReference;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class ElementQueueProcessCreatedEvent implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 597245520467284022L;

    private ProcessReference processReference;


    public ElementQueueProcessCreatedEvent(final ProcessReference processReference) {
        this.processReference = processReference;
    }




}

---------- End of ./event/ElementQueueProcessCreatedEvent.java ----------

---------- Start of ./event/GateQueueCreatedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GateQueueCreatedEvent implements Serializable {

    private EntityReference queueReference;

    private EntityReference executionAreaReference;

    public GateQueueCreatedEvent(final EntityReference queueReference, final EntityReference executionAreaReference) {
        this.queueReference = queueReference;
        this.executionAreaReference = executionAreaReference;
    }
}

---------- End of ./event/GateQueueCreatedEvent.java ----------

---------- Start of ./event/ElementEnqueuedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class ElementEnqueuedEvent implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 597245520467284022L;


    private EntityReference queueReference;
    private EntityReference waitingAreaReference;
    private EntityReference executionAreaReference;
    private QueuedElementReference queuedElement;


    public ElementEnqueuedEvent(EntityReference queueReference,
                                EntityReference waitingAreaReference,
                                EntityReference executionAreaReference,
                                QueuedElementReference queuedElement) {
        this.queueReference = queueReference;
        this.waitingAreaReference = waitingAreaReference;
        this.executionAreaReference = executionAreaReference;
        this.queuedElement = queuedElement;
    }




}

---------- End of ./event/ElementEnqueuedEvent.java ----------

---------- Start of ./event/PreparationTerminatedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;


@Getter
@Setter
public class PreparationTerminatedEvent implements Serializable {


    private EntityReference executionAreaReference;
    private EntityReference queueReference;

    public PreparationTerminatedEvent(EntityReference executionAreaReference, EntityReference queueReference) {
        this.executionAreaReference = executionAreaReference;
        this.queueReference = queueReference;
    }

    // Getters and setters
}

---------- End of ./event/PreparationTerminatedEvent.java ----------

---------- Start of ./event/DockQueueCreatedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class DockQueueCreatedEvent implements Serializable {

    private EntityReference queueReference;

    private EntityReference executionAreaReference;

    public DockQueueCreatedEvent(final EntityReference queueReference, final EntityReference executionAreaReference) {
        this.queueReference = queueReference;
        this.executionAreaReference = executionAreaReference;
    }
}

---------- End of ./event/DockQueueCreatedEvent.java ----------

---------- Start of ./event/ElementDequeuedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class ElementDequeuedEvent implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 597245520467284022L;


    private EntityReference queueReference;
    private EntityReference waitingAreaReference;
    private EntityReference executionAreaReference;
    private QueuedElementReference queuedElement;
    private LocationReference executionLocationReference;
    private EntityReference queuedExternalEntityRef;


    public ElementDequeuedEvent(EntityReference queueReference,
                                EntityReference waitingAreaReference,
                                EntityReference executionAreaReference,
                                LocationReference executionLocationReference,
                                QueuedElementReference queuedElement,
                                EntityReference queuedExternalEntityRef) {
        this.queueReference = queueReference;
        this.waitingAreaReference = waitingAreaReference;
        this.executionAreaReference = executionAreaReference;
        this.executionLocationReference = executionLocationReference;
        this.queuedElement = queuedElement;
        this.queuedExternalEntityRef = queuedExternalEntityRef;
    }




}

---------- End of ./event/ElementDequeuedEvent.java ----------

---------- Start of ./event/QueuedElementInAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;


@Getter
@Setter
public abstract class QueuedElementInAreaEvent implements Serializable {


    public QueuedElementReference queuedElementRef;
    public EntityReference externalEntityRef;
    public EntityReference areaReference;
    private LocationReference currentLocationReference;
    private LocationReference executionLocationReference;

    public QueuedElementInAreaEvent(final QueuedElementReference queuedElementRef,
                                    final EntityReference externalEntityRef,
                                    final EntityReference areaReference,
                                    final LocationReference currentLocationReference,
                                    final LocationReference executionLocationReference) {
        this.queuedElementRef = queuedElementRef;
        this.externalEntityRef = externalEntityRef;
        this.areaReference = areaReference;
        this.currentLocationReference = currentLocationReference;
        this.executionLocationReference = executionLocationReference;

    }
}

---------- End of ./event/QueuedElementInAreaEvent.java ----------

---------- Start of ./event/QueuedElementInWaitingAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

public class QueuedElementInWaitingAreaEvent extends QueuedElementInAreaEvent {


    public QueuedElementInWaitingAreaEvent(final QueuedElementReference queuedElementRef, final EntityReference externalEntityRef,
                                           final EntityReference areaReference, final LocationReference currentLocationReference,
                                           final LocationReference executionLocationReference) {
        super(queuedElementRef, externalEntityRef, areaReference, currentLocationReference, executionLocationReference);
    }
}

---------- End of ./event/QueuedElementInWaitingAreaEvent.java ----------

---------- Start of ./event/ExecutionAreaUnblockedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class ExecutionAreaUnblockedEvent implements Serializable {

    private EntityReference executionAreaReference;

    private EntityReference queueReference;


    public ExecutionAreaUnblockedEvent(final EntityReference executionAreaReference, final EntityReference queueReference) {
        this.queueReference = queueReference;
        this.executionAreaReference = executionAreaReference;

    }
}

---------- End of ./event/ExecutionAreaUnblockedEvent.java ----------

---------- Start of ./event/TaskDequeueCommandInfo.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;
import java.util.List;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yard.reference.TransitReference;
import eu.reply.lea.services.yms.task.TaskReference;

@NoArgsConstructor
@Getter
@Setter
public class TaskDequeueCommandInfo implements Serializable {

    private static final long serialVersionUID = 597245578267284022L;

    private List<TaskReference> tasks;
    private List<LocationReference> locationReferences;
    private TransitReference transitReference;

    public TaskDequeueCommandInfo(final List<TaskReference> tasks,
                                  final List<LocationReference> locationReferences,
                                  final TransitReference transitReference) {
        this.tasks = tasks;
        this.locationReferences = locationReferences;
        this.transitReference = transitReference;
    }

}

---------- End of ./event/TaskDequeueCommandInfo.java ----------

---------- Start of ./event/ElementChangedAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;


import java.io.Serializable;
import java.time.Instant;

import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.sce.backend.core.entity.EntityReference;

/**
 * This event is used to nofity services outside the queue service that the area for
 * an entity has changed
 */
@Getter
public class ElementChangedAreaEvent implements Serializable {

    private EntityReference externalEntityReference;

    private LocationReference currentLocationReference;
    private LocationReference executionLocationReference;
    private String areaType;

    private String inOut;
    private String eventInstant;

    public ElementChangedAreaEvent(EntityReference externalEntityReference, LocationReference currentLocationReference,
                                   LocationReference executionLocationReference,
                                   String areaType, String inOut) {
        this.externalEntityReference = externalEntityReference;
        this.currentLocationReference = currentLocationReference;
        this.executionLocationReference = executionLocationReference;
        this.areaType = areaType;
        this.inOut = inOut;
        this.eventInstant = Instant.now().toString();
    }

}
---------- End of ./event/ElementChangedAreaEvent.java ----------

---------- Start of ./event/QueuedElementOutWaitingAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

public class QueuedElementOutWaitingAreaEvent extends QueuedElementOutAreaEvent {


    public QueuedElementOutWaitingAreaEvent(final QueuedElementReference queuedElementRef,
                                            final EntityReference externalEntityRef, final EntityReference areaReference,
                                            final LocationReference currentLocationReference,
                                            final LocationReference executionLocationReference) {
        super(queuedElementRef, externalEntityRef, areaReference, currentLocationReference, executionLocationReference);
    }
}

---------- End of ./event/QueuedElementOutWaitingAreaEvent.java ----------

---------- Start of ./event/QueuedElementOutExecutionAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import lombok.Getter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

@Getter
public class QueuedElementOutExecutionAreaEvent extends QueuedElementOutAreaEvent {


    public QueuedElementOutExecutionAreaEvent(final QueuedElementReference queuedElementRef, final EntityReference externalEntityRef,
                                              final EntityReference areaReference,
                                              final LocationReference currentLocationReference,
                                              final LocationReference executionLocationReference) {
        super(queuedElementRef, externalEntityRef, areaReference, currentLocationReference, executionLocationReference);
    }
}

---------- End of ./event/QueuedElementOutExecutionAreaEvent.java ----------

---------- Start of ./event/QueuedElementOutAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import lombok.Getter;
import lombok.Setter;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;


@Getter
@Setter
public abstract class QueuedElementOutAreaEvent implements Serializable {


    public QueuedElementReference queuedElementRef;
    public EntityReference externalEntityRef;
    public EntityReference areaReference;
    private LocationReference currentLocationReference;
    private LocationReference executionLocationReference;

    public QueuedElementOutAreaEvent(final QueuedElementReference queuedElementRef,
                                     final EntityReference externalEntityRef,
                                     final EntityReference areaReference,
                                     final LocationReference currentLocationReference,
                                     final LocationReference executionLocationReference) {
        this.queuedElementRef = queuedElementRef;
        this.externalEntityRef = externalEntityRef;
        this.areaReference = areaReference;
        this.currentLocationReference = currentLocationReference;
        this.executionLocationReference = executionLocationReference;

    }
}

---------- End of ./event/QueuedElementOutAreaEvent.java ----------

---------- Start of ./event/TaskDequeueCommand.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import lombok.Getter;
import lombok.Setter;

import eu.reply.sce.backend.core.event.ConfigurableEvent;

@Getter
@Setter
public class TaskDequeueCommand extends ConfigurableEvent<TaskDequeueCommandInfo> {

    private static final long serialVersionUID = 597245578267284022L;


    public TaskDequeueCommand(final TaskDequeueCommandInfo payload) {
        super(payload);
    }
}

---------- End of ./event/TaskDequeueCommand.java ----------

---------- Start of ./event/ElementQueueProcessPartialConfirmEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.lea.services.yms.util.ProcessReference;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class ElementQueueProcessPartialConfirmEvent implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 597245520467284022L;

    private ProcessReference processReference;


    public ElementQueueProcessPartialConfirmEvent(final ProcessReference processReference) {
        this.processReference = processReference;
    }




}

---------- End of ./event/ElementQueueProcessPartialConfirmEvent.java ----------

---------- Start of ./event/ZoneQueueCreatedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ZoneQueueCreatedEvent implements Serializable {

    private EntityReference queueReference;

    private EntityReference executionAreaReference;

    public ZoneQueueCreatedEvent(final EntityReference queueReference, final EntityReference executionAreaReference) {
        this.queueReference = queueReference;
        this.executionAreaReference = executionAreaReference;
    }
}

---------- End of ./event/ZoneQueueCreatedEvent.java ----------

---------- Start of ./event/TaskEnqueuedEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import java.io.Serializable;

import lombok.Getter;
import lombok.NoArgsConstructor;

import eu.reply.sce.backend.core.entity.EntityReference;

@NoArgsConstructor
@Getter
public class TaskEnqueuedEvent implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 597245520467284082L;


    private EntityReference taskReference;
    
    public TaskEnqueuedEvent(EntityReference taskReference) {
        this.taskReference=taskReference;

    }


}

---------- End of ./event/TaskEnqueuedEvent.java ----------

---------- Start of ./event/QueuedElementInExecutionAreaEvent.java ----------
package eu.reply.lea.services.yms.task.queue.event;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.QueuedElementReference;
import eu.reply.sce.backend.core.entity.EntityReference;

import lombok.Getter;

@Getter
public class QueuedElementInExecutionAreaEvent extends QueuedElementInAreaEvent {


    public QueuedElementInExecutionAreaEvent(final QueuedElementReference queuedElementRef, final EntityReference externalEntityRef,
                                             final EntityReference areaReference,
                                             final LocationReference currentLocationReference,
                                             final LocationReference executionLocationReference) {
        super(queuedElementRef, externalEntityRef, areaReference, currentLocationReference, executionLocationReference);
    }
}

---------- End of ./event/QueuedElementInExecutionAreaEvent.java ----------

---------- Start of ./ElementEnqueueProcessDomainService.java ----------
package eu.reply.lea.services.yms.task.queue;


import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.LockModeType;

import eu.reply.lea.services.masterdata.reference.LocationReference;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessCompletedEvent;
import eu.reply.lea.services.yms.task.queue.event.ElementQueueProcessPartialConfirmEvent;
import eu.reply.lea.services.yms.task.queue.repository.QueueProcessTrackerRepository;
import eu.reply.lea.services.yms.util.ProcessReference;
import eu.reply.sce.backend.core.jee.event.Producer;
import eu.reply.sce.backend.core.jee.log.Logged;

@Logged
@ApplicationScoped
public class ElementEnqueueProcessDomainService {

    @Inject
    private QueueProcessTrackerRepository queueProcessTrackerRepository;

    @Inject
    private Producer<ElementQueueProcessPartialConfirmEvent> elementQueueProcessPartialConfirmEventProducer;

    @Inject
    private Producer<ElementQueueProcessCompletedEvent> elementQueueProcessCompletedEventProducer;

    public void confirmProcess(final ElementEnqueueProcess
                                   elementEnqueueProcess, final LocationReference locationReference){
        elementEnqueueProcess.confirm(locationReference, elementQueueProcessPartialConfirmEventProducer,
                                      elementQueueProcessCompletedEventProducer);
        if (elementEnqueueProcess.didProcessingComplete()){
            QueueProcessTracker
                transitQueueProcessTracker =  queueProcessTrackerRepository.findByQueueProcess(new ProcessReference(
                elementEnqueueProcess), LockModeType.PESSIMISTIC_WRITE); //TODO This should be in the QueueProcessTracker and not here, the queue should not be ever aware of the presence of transits.
            //You should use the elementQueueProcessCompletedEvent to advise the tracker that the process is completed.
            //but this probably would make necessary create a timeout event because otherwise it could get stuck, if the message is lost
            transitQueueProcessTracker.completedEnqueueProcess();
        }
    }

    public void abortProcess(final ElementEnqueueProcess
                                 elementEnqueueProcess, final LocationReference executionLocation){
        elementEnqueueProcess.abort(executionLocation,
                                    elementQueueProcessCompletedEventProducer);
        if (elementEnqueueProcess.isAborted()){
            QueueProcessTracker
                transitQueueProcessTracker =  queueProcessTrackerRepository.findByQueueProcess(new ProcessReference(
                elementEnqueueProcess), LockModeType.PESSIMISTIC_WRITE); //TODO This should be in the QueueProcessTracker and not here, the queue should not be ever aware of the presence of transits.
            //You should use the elementQueueProcessCompletedEvent to advise the tracker that the process is completed.
            //but this probably would make necessary create a timeout event because otherwise it could get stuck, if the message is lost
            transitQueueProcessTracker.abortedEnqueueProcess();
        }
    }

}

---------- End of ./ElementEnqueueProcessDomainService.java ----------





COnsidering the code above, wheneever a task is unlocked a dequeue for that location is performed, 
But this is wrong becuase if i have two tasks on the same location the dequeue should not happen, but the logistic unit should remain in the same position.
How can i solve this problem?