/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo MES
* Version: 1.4
*
* 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.assignmentToShift.hooks;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Range;
import com.qcadoo.mes.assignmentToShift.constants.AssignmentToShiftFields;
import com.qcadoo.mes.assignmentToShift.dataProviders.AssignmentToShiftCriteria;
import com.qcadoo.mes.assignmentToShift.dataProviders.AssignmentToShiftDataProvider;
import com.qcadoo.mes.assignmentToShift.states.constants.AssignmentToShiftState;
import com.qcadoo.mes.assignmentToShift.states.constants.AssignmentToShiftStateChangeDescriber;
import com.qcadoo.mes.basic.shift.Shift;
import com.qcadoo.mes.basic.shift.ShiftsFactory;
import com.qcadoo.mes.states.service.StateChangeEntityBuilder;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.search.SearchCriterion;
import com.qcadoo.model.api.utils.EntityUtils;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import static com.qcadoo.model.api.search.SearchOrders.asc;
import static com.qcadoo.model.api.search.SearchProjections.*;
import static com.qcadoo.model.api.search.SearchRestrictions.*;
@Service
public class AssignmentToShiftHooks {
private static final String L_ALREADY_EXISTS = "assignmentToShift.assignmentToShift.entityAlreadyExists";
@Autowired
private StateChangeEntityBuilder stateChangeEntityBuilder;
@Autowired
private AssignmentToShiftStateChangeDescriber describer;
@Autowired
private AssignmentToShiftDataProvider assignmentToShiftDataProvider;
@Autowired
private ShiftsFactory shiftsFactory;
public void onCreate(final DataDefinition assignmentToShiftDD, final Entity assignmentToShift) {
setExternalSynchronized(assignmentToShift);
setInitialState(assignmentToShift);
}
public void onCopy(final DataDefinition assignmentToShiftDD, final Entity assignmentToShift) {
setExternalSynchronized(assignmentToShift);
setNextDay(assignmentToShift);
setInitialState(assignmentToShift);
}
public boolean onValidate(final DataDefinition assignmentToShiftDD, final Entity assignmentToShift) {
return checkUniqueEntity(assignmentToShift);
}
private void setExternalSynchronized(final Entity assignmentToShift) {
assignmentToShift.setField(AssignmentToShiftFields.EXTERNAL_SYNCHRONIZED, true);
}
void setInitialState(final Entity assignmentToShift) {
stateChangeEntityBuilder.buildInitial(describer, assignmentToShift, AssignmentToShiftState.DRAFT);
}
void setNextDay(final Entity assignmentToShift) {
Optional<LocalDate> maybeNewDate = resolveNextStartDate(assignmentToShift);
assignmentToShift.setField(AssignmentToShiftFields.START_DATE, maybeNewDate.transform(TO_DATE).orNull());
}
private Optional<LocalDate> resolveNextStartDate(final Entity assignmentToShift) {
LocalDate startDate = LocalDate.fromDateFields(assignmentToShift.getDateField(AssignmentToShiftFields.START_DATE));
Shift shift = shiftsFactory.buildFrom(assignmentToShift.getBelongsToField(AssignmentToShiftFields.SHIFT));
Entity factory = assignmentToShift.getBelongsToField(AssignmentToShiftFields.FACTORY);
Entity crew = assignmentToShift.getBelongsToField(AssignmentToShiftFields.CREW);
Set<LocalDate> occupiedDates = findNextStartDatesMatching(assignmentToShift.getDataDefinition(), startDate, shift,
factory, crew);
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
int daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR);
Iterable<Integer> daysRange = ContiguousSet.create(Range.closed(1, daysInYear), DiscreteDomain.integers());
return FluentIterable.from(daysRange).transform(startDate::plusDays).firstMatch((final LocalDate localDate) -> !occupiedDates.contains(localDate) && shift.worksAt(localDate));
}
private static final Function<Date, LocalDate> TO_LOCAL_DATE = LocalDate::fromDateFields;
private static final Function<LocalDate, Date> TO_DATE = LocalDate::toDate;
private Set<LocalDate> findNextStartDatesMatching(final DataDefinition assignmentToShiftDD, final LocalDate laterThan,
final Shift shift, final Entity factory, Entity crew) {
AssignmentToShiftCriteria criteria = AssignmentToShiftCriteria.empty();
criteria.withCriteria(gt(AssignmentToShiftFields.START_DATE, laterThan.toDate()));
criteria.withShiftCriteria(idEq(shift.getId()));
criteria.withFactoryCriteria(idEq(factory.getId()));
if(Objects.nonNull(crew)){
criteria.withCrewCriteria(idEq(crew.getId()));
}
List<Entity> matchingStartDatesProjection = assignmentToShiftDataProvider.findAll(criteria,
Optional.of(alias(field(AssignmentToShiftFields.START_DATE), AssignmentToShiftFields.START_DATE)),
Optional.of(asc(AssignmentToShiftFields.START_DATE)));
return FluentIterable.from(matchingStartDatesProjection)
.transform(EntityUtils.<Date> getFieldExtractor(AssignmentToShiftFields.START_DATE)).transform(TO_LOCAL_DATE)
.toSet();
}
boolean checkUniqueEntity(final Entity assignmentToShift) {
Date startDate = assignmentToShift.getDateField(AssignmentToShiftFields.START_DATE);
Entity shift = assignmentToShift.getBelongsToField(AssignmentToShiftFields.SHIFT);
Entity factory = assignmentToShift.getBelongsToField(AssignmentToShiftFields.FACTORY);
Entity crew = assignmentToShift.getBelongsToField(AssignmentToShiftFields.CREW);
AssignmentToShiftCriteria criteria = AssignmentToShiftCriteria.empty();
SearchCriterion additionalCriteria = eq(AssignmentToShiftFields.START_DATE, startDate);
criteria.withShiftCriteria(idEq(shift.getId()));
criteria.withFactoryCriteria(idEq(factory.getId()));
if (crew != null) {
criteria.withCrewCriteria(idEq(crew.getId()));
} else {
additionalCriteria = and(additionalCriteria, isNull(AssignmentToShiftFields.CREW));
}
if (assignmentToShift.getId() != null) {
additionalCriteria = and(additionalCriteria, idNe(assignmentToShift.getId()));
}
criteria.withCriteria(additionalCriteria);
for (Entity matchingAssignment : assignmentToShiftDataProvider.find(criteria, Optional.of(alias(id(), "id"))).asSet()) {
addErrorMessages(assignmentToShift.getDataDefinition(), assignmentToShift);
return false;
}
return true;
}
private void addErrorMessages(final DataDefinition assignmentToShiftDD, final Entity assignmentToShift) {
assignmentToShift.addError(assignmentToShiftDD.getField(AssignmentToShiftFields.START_DATE), L_ALREADY_EXISTS);
assignmentToShift.addError(assignmentToShiftDD.getField(AssignmentToShiftFields.SHIFT), L_ALREADY_EXISTS);
assignmentToShift.addError(assignmentToShiftDD.getField(AssignmentToShiftFields.FACTORY), L_ALREADY_EXISTS);
assignmentToShift.addError(assignmentToShiftDD.getField(AssignmentToShiftFields.CREW), L_ALREADY_EXISTS);
}
}