eu.ggnet.dwoss.stock.StockTransactionProcessorOperation.java Source code

Java tutorial

Introduction

Here is the source code for eu.ggnet.dwoss.stock.StockTransactionProcessorOperation.java

Source

/*
 * Copyright (C) 2014 GG-Net GmbH - Oliver Gnther
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.ggnet.dwoss.stock;

import java.util.*;
import java.util.stream.Collectors;

import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.validation.*;

import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.ggnet.dwoss.event.SalesChannelChange;
import eu.ggnet.dwoss.event.UnitHistory;
import eu.ggnet.dwoss.mandator.api.value.Mandator;
import eu.ggnet.dwoss.progress.MonitorFactory;
import eu.ggnet.dwoss.progress.SubMonitor;
import eu.ggnet.dwoss.rules.SalesChannel;
import eu.ggnet.dwoss.stock.assist.Stocks;
import eu.ggnet.dwoss.stock.eao.StockTransactionEao;
import eu.ggnet.dwoss.stock.eao.StockUnitEao;
import eu.ggnet.dwoss.stock.emo.*;
import eu.ggnet.dwoss.stock.entity.*;
import eu.ggnet.dwoss.util.UserInfoException;
import eu.ggnet.dwoss.util.validation.ConstraintViolationFormater;

import static eu.ggnet.dwoss.stock.entity.StockTransactionParticipationType.*;
import static eu.ggnet.dwoss.stock.entity.StockTransactionStatusType.*;

/**
 * Implementation of StockTransctionProcessor.
 *
 * @author oliver.guenther
 */
// TODO: Look up methodes here and in the StockTransactionEmo. I assume there some overlays.
@Stateless
public class StockTransactionProcessorOperation implements StockTransactionProcessor {

    private final static Logger L = LoggerFactory.getLogger(StockTransactionProcessorOperation.class);

    private final static Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();

    @Inject
    @Stocks
    private EntityManager stockEm;

    @Inject
    private Event<UnitHistory> history;

    @Inject
    private Event<SalesChannelChange> channelChanger;

    @Inject
    private MonitorFactory monitorFactory;

    @Inject
    private Mandator mandator;

    /**
     * Rolls all StockTransaction in, completing them and setting the Stock.
     *
     * @param detachtedTransactions the transactions
     * @param arranger              the arranger
     */
    @Override
    public List<Integer> rollIn(List<StockTransaction> detachtedTransactions, String arranger) {
        SubMonitor m = monitorFactory.newSubMonitor("RollIn", detachtedTransactions.size() * 2);
        StockTransactionEao stockTransactionEao = new StockTransactionEao(stockEm);
        StockTransactionEmo stockTransactionEmo = new StockTransactionEmo(stockEm);
        List<StockTransaction> transactions = new ArrayList<>();
        m.message("loading Transactions");
        for (StockTransaction detachedTransaction : detachtedTransactions) {
            transactions.add(stockTransactionEao.findById(detachedTransaction.getId()));
            m.worked(1);
        }
        m.setWorkRemaining(3);
        m.message("rolling in");
        List<StockUnit> stockUnits = stockTransactionEmo.completeRollIn(arranger, transactions);
        m.worked(2, "adding History");
        for (StockUnit stockUnit : stockUnits) {
            if (mandator.isApplyDefaultChannelOnRollIn()) {
                SalesChannel channel = stockUnit.getStock().getPrimaryChannel();
                channelChanger.fire(new SalesChannelChange(stockUnit.getUniqueUnitId(), channel));
                history.fire(new UnitHistory(stockUnit.getUniqueUnitId(),
                        "Rolled in " + stockUnit.getStock().getName() + " with " + channel.getName(), arranger));
            } else {
                history.fire(new UnitHistory(stockUnit.getUniqueUnitId(),
                        "Rolled in " + stockUnit.getStock().getName(), arranger));
            }
        }
        m.finish();
        return stockUnits.stream().map(x -> x.getId()).collect(Collectors.toList());
    }

    /**
     * Prepares the transfer of multiple units.
     * Creates an amount of needed transactions in the form,
     * - that the transactions are correct (all units of a transaction have the same source as the transaciton)
     * - that no transaction has more units than maxUnitSize.
     * <p/>
     * @param stockUnits the stockUnits to transfer
     * @param arranger   the arranger
     * @param comment    a optional comment
     * @return A map with uniqueUnitIds and comments for their history.
     * @throws UserInfoException
     */
    @Override
    public SortedMap<Integer, String> perpareTransfer(List<StockUnit> stockUnits, int destinationStockId,
            String arranger, String comment) throws UserInfoException {
        if (stockUnits == null || stockUnits.isEmpty())
            return new TreeMap<>();
        return new StockTransactionEmo(stockEm)
                .prepare(
                        Transfer.builder().destinationStockId(destinationStockId)
                                .stockUnitIds(
                                        stockUnits.stream().map(StockUnit::getId).collect(Collectors.toList()))
                                .arranger(arranger).comment(comment).maxTransactionSize(10).build(),
                        monitorFactory.newSubMonitor("Umfuhr vorbereiten"));
    }

    /**
     * Remove the stockUnit represented by the refurbishId from a stock transaction, if that transaction exists and is in state prepared.
     * <p/>
     * @param refurbishId the refurbishId
     * @param arranger    the arranger
     * @param comment     a optional comment
     * @throws UserInfoException if no unit exists, the unit is not on a transaction or the transaction has another state then prepared.
     */
    @Override
    public void removeFromPreparedTransaction(final String refurbishId, final String arranger, final String comment)
            throws UserInfoException {
        StockUnit stockUnit = new StockUnitEao(stockEm).findByRefurbishId(refurbishId);
        if (stockUnit == null)
            throw new UserInfoException("SopoNr: " + refurbishId + " existiert nicht.");
        if (!stockUnit.isInTransaction())
            throw new UserInfoException("SopoNr: " + refurbishId + " nicht in Transaction.");
        StockTransaction transaction = stockUnit.getTransaction();
        if (transaction.getStatus().getType() != PREPARED) {
            throw new UserInfoException("SopoNr: " + refurbishId + " auf Transaction, aber Transaction("
                    + transaction.getId() + ") not in Status prepared");
        }
        // The one case we remove the position as well, but dont need to set the stock because of beeing in status prepared
        StockTransactionPosition position = stockUnit.getPosition();
        stockEm.remove(position);
        transaction.setComment(transaction.getComment() + ", Unit " + stockUnit.getRefurbishId() + " removed by "
                + arranger + ", cause=" + comment);
        history.fire(new UnitHistory(stockUnit.getUniqueUnitId(),
                "Unit returned to Stock(" + transaction.getSource().getId() + ") "
                        + transaction.getSource().getName() + ", removed from Transaction, cause: " + comment,
                arranger));
        L.info("{} removed from {}", stockUnit, transaction);
    }

    /**
     * Cancels a stock transaction.
     * <p/>
     * @param transaction the transaction to cancel.
     * @param arranger    the arranger
     * @param comment     a comment to describe why
     * @throws UserInfoException if the transaction is not in state prepared.
     */
    @Override
    public void cancel(StockTransaction transaction, final String arranger, final String comment)
            throws UserInfoException {
        transaction = stockEm.find(StockTransaction.class, transaction.getId());
        if (transaction.getStatus().getType() != PREPARED) {
            throw new UserInfoException("Supplied transaction is not in state prepared, but "
                    + transaction.getStatus() + ", cancel not allowed");
        }
        StockTransactionStatus status = new StockTransactionStatus(CANCELLED, new Date(), comment);
        status.addParticipation(new StockTransactionParticipation(ARRANGER, arranger));
        transaction.addStatus(status);
        for (StockUnit stockUnit : transaction.getUnits()) {
            history.fire(new UnitHistory(stockUnit.getUniqueUnitId(),
                    "Unit returned to Stock(" + transaction.getSource().getId() + ") "
                            + transaction.getSource().getName() + ", cancelled Transaction(" + transaction.getId()
                            + ")",
                    arranger));
            L.info("cancelTransaction(): Returning {} to Stock {} ", stockUnit, transaction.getSource());
            stockUnit.setPosition(null);
            // Nothing to do because of special case prepared transaction, which allows the unit to be on transaction and on stock
        }
    }

    /**
     * Bring a list of transactions from prepared into the state in transfer via commission.
     * <p/>
     * @param transactions the transaction to commission
     * @param picker       the pricker of units
     * @param deliverer    the transferer.
     * @throws RuntimeException if the transaction is in some form invalid.
     */
    // TODO: how about some validations.
    @Override
    public void commission(List<StockTransaction> transactions, String picker, String deliverer)
            throws RuntimeException {
        for (StockTransaction transaction : transactions) {
            L.info("Commissioning {}", transaction);
            Set<ConstraintViolation<StockTransaction>> violations = VALIDATOR.validate(transaction);
            if (!violations.isEmpty())
                throw new RuntimeException("Invalid StockTransaction in PRE-Validate: "
                        + ConstraintViolationFormater.toSingleLine(violations));
            transaction = stockEm.find(StockTransaction.class, transaction.getId());
            Date before = transaction.addStatus(COMMISSIONED, PICKER, picker, DELIVERER, deliverer);
            transaction.addStatus(DateUtils.addSeconds(before, 1), IN_TRANSFER, DELIVERER, deliverer);
            violations = VALIDATOR.validate(transaction);
            if (!violations.isEmpty())
                throw new RuntimeException("Invalid StockTransaction in POST-Validate: "
                        + ConstraintViolationFormater.toSingleLine(violations));
            for (StockUnit stockUnit : transaction.getUnits()) {
                stockUnit.setStock(null);
            }
        }
    }

    /**
     * Receive a list of transactions in the destination stock.
     * <p/>
     * @param transactions the transactions to receive.
     * @param deliverer    the deliverer
     * @param reciever     the receiver
     */
    @Override
    public void receive(List<StockTransaction> transactions, String deliverer, String reciever) {
        StockLocationDiscoverer discoverer = new StockLocationDiscoverer(stockEm);
        for (StockTransaction transaction : transactions) {
            transaction = stockEm.find(StockTransaction.class, transaction.getId());
            transaction.addStatus(RECEIVED, DELIVERER, deliverer, RECEIVER, reciever);
            L.info("Receiving {} in {}", transaction, transaction.getDestination());
            Stock destination = transaction.getDestination();
            for (StockUnit stockUnit : transaction.getUnits()) {
                stockUnit.setPosition(null);
                discoverer.discoverAndSetLocation(stockUnit, destination);
                history.fire(new UnitHistory(stockUnit.getUniqueUnitId(),
                        "Unit received in Stock(" + destination.getId() + ") " + destination.getName(), reciever));
            }
        }
    }
}