com.qcadoo.mes.productionPerShift.util.ProgressPerShiftViewSaver.java Source code

Java tutorial

Introduction

Here is the source code for com.qcadoo.mes.productionPerShift.util.ProgressPerShiftViewSaver.java

Source

/**
 * ***************************************************************************
 * Copyright (c) 2010 Qcadoo Limited
 * Project: Qcadoo MES
 * Version: 1.3
 *
 * This file is part of Qcadoo.
 *
 * Qcadoo is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * ***************************************************************************
 */
package com.qcadoo.mes.productionPerShift.util;

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

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.qcadoo.commons.functional.Either;
import com.qcadoo.commons.functional.FluentOptional;
import com.qcadoo.mes.productionPerShift.constants.ProductionPerShiftFields;
import com.qcadoo.mes.productionPerShift.constants.ProgressForDayFields;
import com.qcadoo.mes.productionPerShift.constants.ProgressType;
import com.qcadoo.mes.productionPerShift.constants.TechnologyOperationComponentFieldsPPS;
import com.qcadoo.mes.productionPerShift.dataProvider.ProgressForDayDataProvider;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.validators.ErrorMessage;
import com.qcadoo.view.api.ComponentState;
import com.qcadoo.view.api.ViewDefinitionState;
import com.qcadoo.view.api.components.AwesomeDynamicListComponent;
import com.qcadoo.view.api.components.FormComponent;
import com.qcadoo.view.api.components.LookupComponent;

@Service
public class ProgressPerShiftViewSaver {

    private static final Predicate<Entity> IS_VALID = new Predicate<Entity>() {

        @Override
        public boolean apply(final Entity progressForDay) {
            return progressForDay.isValid();
        }
    };

    private static final Function<Entity, Entity> SAVE = new Function<Entity, Entity>() {

        @Override
        public Entity apply(final Entity input) {
            return input.getDataDefinition().save(input);
        }
    };

    private static final Function<LookupComponent, Optional<Entity>> GET_LOOKUP_ENTITY = new Function<LookupComponent, Optional<Entity>>() {

        @Override
        public Optional<Entity> apply(final LookupComponent lookup) {
            return Optional.fromNullable(lookup.getEntity());
        }
    };

    private static final String FORM_COMPONENT_REF = "form";

    private static final String PROGRESS_ADL_REF = "progressForDays";

    private static final String OPERATION_LOOKUP_REF = "productionPerShiftOperation";

    private static final String PROGRESS_TYPE_COMBO_REF = "plannedProgressType";

    private static final String CORRECTION_CAUSE_TYPES_ADL_REF = "plannedProgressCorrectionTypes";

    @Autowired
    private ProgressForDayDataProvider progressForDayDataProvider;

    public boolean save(final ViewDefinitionState view) {
        Optional<Entity> maybeToc = getEntityFromLookup(view, OPERATION_LOOKUP_REF);
        if (maybeToc.isPresent()) {
            return saveProgressesAndForm(view, maybeToc.get());
        } else {
            return saveForm(view);
        }
    }

    @Transactional
    private boolean saveProgressesAndForm(final ViewDefinitionState view,
            final Entity technologyOperationComponent) {
        AwesomeDynamicListComponent progressForDaysADL = (AwesomeDynamicListComponent) view
                .getComponentByReference(PROGRESS_ADL_REF);
        List<Entity> progressForDays = progressForDaysADL.getEntities();
        ProgressType progressType = extractProgressType(view);
        boolean hasCorrections = progressType == ProgressType.CORRECTED;
        List<Entity> savedProgresses = validateAndSaveProgresses(progressForDays,
                technologyOperationComponent.getId(), hasCorrections);
        if (Iterables.all(savedProgresses, IS_VALID)) {
            return tryUpdateTechnologyOperation(view, technologyOperationComponent, hasCorrections,
                    savedProgresses);
        } else {
            progressForDaysADL.setFieldValue(savedProgresses);
            showValidationErrors(view, FluentIterable.from(savedProgresses)
                    .transformAndConcat(new Function<Entity, Iterable<ErrorMessage>>() {

                        @Override
                        public Iterable<ErrorMessage> apply(final Entity input) {
                            return input.getGlobalErrors();
                        }
                    }));
            rollbackCurrentTransaction();
            return false;
        }
    }

    private boolean tryUpdateTechnologyOperation(final ViewDefinitionState view,
            final Entity technologyOperationComponent, final boolean hasCorrections,
            final List<Entity> savedProgresses) {
        Either<List<ErrorMessage>, Entity> tocSetupResults = setupTechnologyOperation(technologyOperationComponent,
                savedProgresses, hasCorrections);
        if (tocSetupResults.isRight()) {
            return saveForm(view);
        } else {
            showValidationErrors(view, tocSetupResults.getLeft());
            rollbackCurrentTransaction();
            return false;
        }
    }

    private void rollbackCurrentTransaction() {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }

    private Either<List<ErrorMessage>, Entity> setupTechnologyOperation(final Entity toc,
            final List<Entity> savedProgresses, final boolean hasCorrections) {
        List<Entity> otherTypeProgresses = findProgressesMatching(toc, !hasCorrections);
        List<Entity> tocProgresses = Lists.newLinkedList(Iterables.concat(otherTypeProgresses, savedProgresses));
        toc.setField(TechnologyOperationComponentFieldsPPS.PROGRESS_FOR_DAYS, tocProgresses);
        toc.setField(TechnologyOperationComponentFieldsPPS.HAS_CORRECTIONS, hasCorrections);
        if (saved(toc).isValid()) {
            return Either.right(toc);
        }
        return Either.left(toc.getGlobalErrors());
    }

    private List<Entity> findProgressesMatching(final Entity technologyOperationComponent,
            final boolean hasCorrections) {
        return progressForDayDataProvider.findForOperation(technologyOperationComponent, hasCorrections);
    }

    private ProgressType extractProgressType(final ViewDefinitionState view) {
        String progressTypeStringValue = ObjectUtils
                .toString(view.getComponentByReference(PROGRESS_TYPE_COMBO_REF).getFieldValue());
        return ProgressType.parseString(progressTypeStringValue);
    }

    private Optional<Entity> getEntityFromLookup(final ViewDefinitionState view, final String referenceName) {
        Optional<LookupComponent> maybeLookupComponent = view.tryFindComponentByReference(referenceName);
        return FluentOptional.wrap(maybeLookupComponent).flatMap(GET_LOOKUP_ENTITY).toOpt();
    }

    private boolean saveForm(final ViewDefinitionState view) {
        FormComponent form = getFormComponent(view);
        Entity ppsEntity = form.getPersistedEntityWithIncludedFormValues();
        if (trySaveCorrectionCauses(view, ppsEntity)) {
            return trySavePps(view, ppsEntity);
        }
        return false;
    }

    private boolean trySavePps(final ViewDefinitionState view, final Entity ppsEntity) {
        Entity savedPps = saved(ppsEntity);
        if (savedPps.isValid()) {
            showSaveSuccessMessage(view);
            return true;
        } else {
            showValidationErrors(view, savedPps.getGlobalErrors());
            return false;
        }
    }

    private void showSaveSuccessMessage(final ViewDefinitionState view) {
        FormComponent form = getFormComponent(view);
        form.addMessage("qcadooView.message.saveMessage", ComponentState.MessageType.SUCCESS);
    }

    private boolean trySaveCorrectionCauses(final ViewDefinitionState view, final Entity ppsEntity) {
        List<Entity> savedCorrectionCauses = FluentIterable
                .from(ppsEntity.getHasManyField(ProductionPerShiftFields.PLANNED_PROGRESS_CORRECTION_TYPES))
                .transform(SAVE).toList();
        ppsEntity.setField(ProductionPerShiftFields.PLANNED_PROGRESS_CORRECTION_TYPES, savedCorrectionCauses);
        if (Iterables.all(savedCorrectionCauses, IS_VALID)) {
            return true;
        }
        setCorrectionCauses(view, savedCorrectionCauses);
        showValidationErrors(view, Collections.<ErrorMessage>emptyList());
        return false;
    }

    private void setCorrectionCauses(final ViewDefinitionState view, final List<Entity> savedCorrectionCauses) {
        for (AwesomeDynamicListComponent correctionCausesAdl : view
                .<AwesomeDynamicListComponent>tryFindComponentByReference(CORRECTION_CAUSE_TYPES_ADL_REF).asSet()) {
            correctionCausesAdl.setFieldValue(savedCorrectionCauses);
        }
    }

    private Entity saved(final Entity entity) {
        return SAVE.apply(entity);
    }

    private void showValidationErrors(final ViewDefinitionState view, final Iterable<ErrorMessage> errorMessages) {
        FormComponent form = getFormComponent(view);
        form.addMessage("qcadooView.message.saveFailedMessage", ComponentState.MessageType.FAILURE);
        for (ErrorMessage errorMessage : errorMessages) {
            form.addMessage(errorMessage.getMessage(), ComponentState.MessageType.FAILURE, errorMessage.getVars());
        }
    }

    private FormComponent getFormComponent(final ViewDefinitionState view) {
        return (FormComponent) view.getComponentByReference(FORM_COMPONENT_REF);
    }

    private List<Entity> validateAndSaveProgresses(final List<Entity> progressForDays,
            final Long technologyOperationId, final boolean hasCorrections) {
        List<Entity> progressWithValidatedDailyProgresses = validateDailyProgresses(progressForDays,
                technologyOperationId, hasCorrections);
        if (Iterables.all(progressWithValidatedDailyProgresses, IS_VALID)) {
            return FluentIterable.from(progressWithValidatedDailyProgresses).transform(SAVE).toList();
        }
        return progressWithValidatedDailyProgresses;
    }

    private List<Entity> validateDailyProgresses(final List<Entity> progressForDays,
            final Long technologyOperationId, final boolean hasCorrections) {
        return FluentIterable.from(progressForDays).transform(new Function<Entity, Entity>() {

            @Override
            public Entity apply(final Entity progressForDay) {
                return validateDailyProgressesFor(progressForDay, technologyOperationId, hasCorrections);
            }
        }).toList();
    }

    private Entity validateDailyProgressesFor(final Entity progressForDay, final Long technologyOperationId,
            final boolean hasCorrections) {
        Either<? extends List<Entity>, Void> validationResults = validateDailyProgressesFor(progressForDay);
        if (validationResults.isLeft()) {
            progressForDay.setField(ProgressForDayFields.DAILY_PROGRESS, validationResults.getLeft());
            progressForDay.setNotValid();
            return progressForDay;
        } else {
            progressForDay.setField(ProgressForDayFields.CORRECTED, hasCorrections);
            progressForDay.setField(ProgressForDayFields.TECHNOLOGY_OPERATION_COMPONENT, technologyOperationId);
            return progressForDay;
        }
    }

    private Either<? extends List<Entity>, Void> validateDailyProgressesFor(final Entity progressForDay) {
        List<Entity> savedDailyProgresses = FluentIterable
                .from(progressForDay.getHasManyField(ProgressForDayFields.DAILY_PROGRESS))
                .transform(new Function<Entity, Entity>() {

                    @Override
                    public Entity apply(final Entity dailyProgress) {
                        return tryValidateDailyProgress(dailyProgress).fold(Functions.<Entity>identity(),
                                Functions.constant(dailyProgress));
                    }
                }).toList();
        if (Iterables.all(savedDailyProgresses, IS_VALID)) {
            return Either.right(null);
        }
        return Either.left(savedDailyProgresses);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private Either<Entity, Void> tryValidateDailyProgress(final Entity dailyProgress) {
        Entity savedDailyProgress = saved(dailyProgress);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        // reset id to avoid 'entity cannot be found' exceptions.
        savedDailyProgress.setId(dailyProgress.getId());
        if (savedDailyProgress.isValid()) {
            return Either.right(null);
        }
        return Either.left(savedDailyProgress);
    }

}