PutawayAdapter.java

/*
 * Copyright 2005-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openwms.wms.movements.impl;

import org.ameba.annotation.Measured;
import org.ameba.annotation.TxService;
import org.openwms.wms.movements.Message;
import org.openwms.wms.movements.MovementProperties;
import org.openwms.wms.movements.spi.common.putaway.PutawayApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

/**
 * A PutawayAdapter is a Spring managed transactional event listener that acts as an adapter to the {@link PutawayApi} that is called after
 * a {@link Movement} has been created successfully.
 *
 * @author Heiko Scherrer
 */
@TxService
@RefreshScope
class PutawayAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(PutawayAdapter.class);
    private final ApplicationEventPublisher eventPublisher;
    private final MovementProperties properties;
    private final MovementRepository repository;
    private final PutawayApi putawayApi;

    PutawayAdapter(ApplicationEventPublisher eventPublisher, MovementProperties properties, MovementRepository repository, PutawayApi putawayApi) {
        this.eventPublisher = eventPublisher;
        this.properties = properties;
        this.repository = repository;
        this.putawayApi = putawayApi;
    }

    @ConditionalOnExpression("${owms.movement.putaway-resolution-enabled}")
    @Measured
    @TransactionalEventListener
    @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = {Exception.class})
    public void onEvent(MovementEvent event) {
        var movement = event.getSource();
        if (event.getType() == MovementEvent.Type.CREATED) {
            if (movement.emptyTargetLocation()) {
                try {
                    var movementTarget = properties.findTarget(movement.getTargetLocationGroup());
                    LOGGER.debug("Call putaway strategy to find target location for movement [{}] in [{}]", movement.getPersistentKey(), movementTarget.getSearchLocationGroupNames());
                    var target = putawayApi.findAndAssignNextInLocGroup(
                            movement.getInitiator(),
                            movementTarget.getSearchLocationGroupNames(),
                            movement.getTransportUnitBk().getValue(),
                            2
                    );
                    LOGGER.debug("Putaway strategy returned [{}] as next target for movement [{}]", target.getLocationId(), movement.getPersistentKey());
                    movement.setTargetLocation(target.getErpCode());
                    if (target.getErpCode() != null) {
                        eventPublisher.publishEvent(new MovementTargetChangedEvent(movement));
                    }
                } catch (Exception e) {
                    LOGGER.error("Error calling the putaway strategy: " + e.getMessage(), e);
                    movement.addProblem(new ProblemHistory(movement, new Message.Builder().withMessageText(e.getMessage()).build()));
                }
                repository.save(movement);
            } else {
                LOGGER.debug("Target is already set and not being resolved");
            }
        }
    }
}