com.qcadoo.mes.productionPerShift.dataProvider.ProductionPerShiftDataProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.qcadoo.mes.productionPerShift.dataProvider.ProductionPerShiftDataProvider.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.dataProvider;

import static com.qcadoo.model.api.search.SearchOrders.desc;
import static com.qcadoo.model.api.search.SearchProjections.alias;
import static com.qcadoo.model.api.search.SearchProjections.groupField;
import static com.qcadoo.model.api.search.SearchProjections.list;
import static com.qcadoo.model.api.search.SearchProjections.max;
import static com.qcadoo.model.api.search.SearchProjections.rowCount;
import static com.qcadoo.model.api.search.SearchProjections.sum;
import static com.qcadoo.model.api.search.SearchRestrictions.eq;
import static com.qcadoo.model.api.search.SearchRestrictions.in;
import static com.qcadoo.model.api.search.SearchRestrictions.isNull;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.collect.Sets;
import com.qcadoo.mes.productionPerShift.constants.DailyProgressFields;
import com.qcadoo.mes.productionPerShift.constants.ProductionPerShiftConstants;
import com.qcadoo.mes.productionPerShift.constants.ProgressForDayFields;
import com.qcadoo.mes.productionPerShift.domain.ProductionPerShiftId;
import com.qcadoo.mes.technologies.constants.TechnologyOperationComponentFields;
import com.qcadoo.mes.technologies.tree.domain.TechnologyOperationId;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.search.JoinType;
import com.qcadoo.model.api.search.SearchCriteriaBuilder;
import com.qcadoo.model.api.search.SearchCriterion;
import com.qcadoo.model.api.search.SearchProjectionList;
import com.qcadoo.model.api.search.SearchQueryBuilder;
import com.qcadoo.model.api.utils.EntityUtils;

/**
 * This service aims to be some common PPS data-access facade which provides common building blocks for retrieving data.
 * 
 * @author Marcin Kubala
 * @since 1.3.0
 */
@Service
public class ProductionPerShiftDataProvider {

    private static final String PPS_FOR_TOC_ID_QUERY = "select                      \n"
            + "  pps.id as ppsId                                                    \n"
            + "from #productionPerShift_productionPerShift pps                      \n"
            + "  inner join pps.order o                                             \n"
            + "where                                                                \n"
            + "  o.id in (select                                                    \n"
            + "      o.id                                                           \n"
            + "    from #technologies_technologyOperationComponent toc              \n"
            + "      inner join toc.technology tech                                 \n"
            + "      inner join tech.orders o                                       \n"
            + "    where                                                            \n"
            + "      toc.id = :tocId                                                \n"
            + "    order by o.id desc)                                              \n"
            + "order by pps.id desc                                                 \n";

    private static final String ID = "id";

    private static final String DOT = ".";

    /**
     * Alias for technologyOperationComponent. Use this constant when building additional search criterion for
     * #countSumOfQuantities
     */
    public static final String TECHNOLOGY_OPERATION_COMPONENT_ALIAS = "toc_alias";

    /**
     * Alias for ProgressForDay. Use this constant when building additional search criterion for #countSumOfQuantities
     */
    public static final String PROGRESS_FOR_DAY_ALIAS = "pfd_alias";

    /**
     * Alias for dailyProgress. Use this constant when building additional search criterion for #countSumOfQuantities
     */
    public static final String DAILY_PROGRESS_ALIAS = "dp_alias";

    private static final String TECH_ALIAS = "tech";

    private static final String QUANTITY_SUM_PROJECTION = "quantitySumProjection";

    private static final String ID_PROJECTION = "idProjection";

    /**
     * Restrict
     */
    public static final SearchCriterion ONLY_ROOT_OPERATIONS_CRITERIA = isNull(
            TECHNOLOGY_OPERATION_COMPONENT_ALIAS + DOT + TechnologyOperationComponentFields.PARENT);

    @Autowired
    private DataDefinitionService dataDefinitionService;

    /**
     * Returns id of production per shift record, that belongs to the same production order as given technology operation.
     * 
     * @param tocId
     *            identifier of the technology operation component
     * @return production per shift record id or none, if such pps cannot be found
     * @since 1.4
     */
    public Optional<ProductionPerShiftId> find(final TechnologyOperationId tocId) {
        SearchQueryBuilder sqb = getProductionPerShiftDD().find(PPS_FOR_TOC_ID_QUERY).setLong("tocId", tocId.get());
        sqb.setMaxResults(1);
        return Optional.ofNullable(sqb.uniqueResult()).flatMap(e -> Optional.ofNullable((Long) e.getField("ppsId")))
                .map(ProductionPerShiftId::new);
    }

    /**
     * Returns sum of quantities from matching daily shift progress entries. Notice that corrected quantities take precedence over
     * planned ones (see documentation for #findIdsOfEffectiveProgressForDay).<br/>
     * <br/>
     * 
     * By default this method counts quantities for each operations in technology tree. If you want to count only quantities for
     * root operations pass ProductionPerShiftDataProvider#ONLY_ROOT_OPERATIONS_CRITERIA search criterion as a second argument.
     * 
     * @param technologyId
     *            id of technology
     * @param additionalCriteria
     *            optional additional criteria.
     * @return sum of quantities from matching daily shift progresses or BigDecimal#ZERO if there is no matching progress entries.
     * @since 1.3.0
     */
    public BigDecimal countSumOfQuantities(final Long technologyId, final SearchCriterion additionalCriteria) {
        Set<Long> pfdIds = findIdsOfEffectiveProgressForDay(technologyId);

        if (pfdIds.isEmpty()) {
            return BigDecimal.ZERO;
        }

        SearchProjectionList projectionList = list();
        projectionList.add(
                alias(sum(DAILY_PROGRESS_ALIAS + DOT + DailyProgressFields.QUANTITY), QUANTITY_SUM_PROJECTION));
        // To prevent NPE during conversion to generic entity
        projectionList.add(rowCount());

        SearchCriteriaBuilder scb = getDailyProgressDD().findWithAlias(DAILY_PROGRESS_ALIAS);
        scb.setProjection(projectionList);
        scb.createAlias(DAILY_PROGRESS_ALIAS + DOT + DailyProgressFields.PROGRESS_FOR_DAY, PROGRESS_FOR_DAY_ALIAS,
                JoinType.INNER);
        scb.createAlias(PROGRESS_FOR_DAY_ALIAS + DOT + ProgressForDayFields.TECHNOLOGY_OPERATION_COMPONENT,
                TECHNOLOGY_OPERATION_COMPONENT_ALIAS, JoinType.INNER);
        scb.add(in(PROGRESS_FOR_DAY_ALIAS + DOT + ID, pfdIds));
        if (additionalCriteria != null) {
            scb.add(additionalCriteria);
        }
        scb.addOrder(desc(QUANTITY_SUM_PROJECTION));

        Entity projection = scb.setMaxResults(1).uniqueResult();
        BigDecimal quantitiesSum = projection.getDecimalField(QUANTITY_SUM_PROJECTION);
        return ObjectUtils.defaultIfNull(quantitiesSum, BigDecimal.ZERO);
    }

    /**
     * This method returns a set of corrected or planned (if corrected progress doesn't exist for given day & operation
     * combination) progress ids for given technology.<br/>
     * <br/>
     * 
     * I assume that each order has its own copy of technology tree.<br/>
     * <br/>
     * 
     * Corrected progresses take precedence over planned ones. For example:<br/>
     * <br/>
     * 
     * If you have defined following planned progresses for some operation OP-1 at 1st, 2nd and 3rd day and you have also progress
     * correction for the same operation at 2nd day, you'll get ids of the following records:
     * <ul>
     * <li>planned progress for 1st day</li>
     * <li>corrected progress for 2nd day</li>
     * <li>planned progress for 3rd day</li>
     * </ul>
     * 
     * @return set of ids for progressForDay entities, related to given technology.
     * @since 1.3.0
     */
    public Set<Long> findIdsOfEffectiveProgressForDay(final Long technologyId) {
        DataDefinition progressForDayDD = getProgressForDayDD();

        SearchProjectionList projectionsList = list();
        projectionsList.add(alias(max(PROGRESS_FOR_DAY_ALIAS + DOT + ID), ID_PROJECTION));
        projectionsList.add(groupField(TECHNOLOGY_OPERATION_COMPONENT_ALIAS + DOT + ID));
        projectionsList.add(groupField(PROGRESS_FOR_DAY_ALIAS + DOT + ProgressForDayFields.ACTUAL_DATE_OF_DAY));

        SearchCriteriaBuilder scb = progressForDayDD.findWithAlias(PROGRESS_FOR_DAY_ALIAS);
        scb.setProjection(projectionsList);
        scb.createAlias(PROGRESS_FOR_DAY_ALIAS + DOT + ProgressForDayFields.TECHNOLOGY_OPERATION_COMPONENT,
                TECHNOLOGY_OPERATION_COMPONENT_ALIAS, JoinType.INNER);
        scb.createAlias(TECHNOLOGY_OPERATION_COMPONENT_ALIAS + DOT + TechnologyOperationComponentFields.TECHNOLOGY,
                TECH_ALIAS, JoinType.INNER);
        scb.add(eq(TECH_ALIAS + DOT + ID, technologyId));
        scb.addOrder(desc(ID_PROJECTION));

        List<Entity> idsProjection = scb.list().getEntities();

        return Sets.newHashSet(EntityUtils.<Long>getFieldsView(idsProjection, ID_PROJECTION));
    }

    private DataDefinition getProductionPerShiftDD() {
        return dataDefinitionService.get(ProductionPerShiftConstants.PLUGIN_IDENTIFIER,
                ProductionPerShiftConstants.MODEL_PRODUCTION_PER_SHIFT);
    }

    private DataDefinition getProgressForDayDD() {
        return dataDefinitionService.get(ProductionPerShiftConstants.PLUGIN_IDENTIFIER,
                ProductionPerShiftConstants.MODEL_PROGRESS_FOR_DAY);
    }

    private DataDefinition getDailyProgressDD() {
        return dataDefinitionService.get(ProductionPerShiftConstants.PLUGIN_IDENTIFIER,
                ProductionPerShiftConstants.MODEL_DAILY_PROGRESS);
    }
}