/* * @(#)ParticipantAuthorizationChain.java * * Copyright 2011 Instituto Superior Tecnico * Founding Authors: Luis Cruz, Nuno Ochoa, Paulo Abrantes * * https://fenix-ashes.ist.utl.pt/ * * This file is part of the Expenditure Tracking Module. * * The Expenditure Tracking Module is free software: you can * redistribute it and/or modify it under the terms of the GNU Lesser General * Public License as published by the Free Software Foundation, either version * 3 of the License, or (at your option) any later version. * * The Expenditure Tracking Module 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Expenditure Tracking Module. If not, see <http://www.gnu.org/licenses/>. * */ package module.mission.domain.util; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import module.mission.domain.MissionSystem; import module.organization.domain.Accountability; import module.organization.domain.AccountabilityType; import module.organization.domain.OrganizationalModel; import module.organization.domain.Party; import module.organization.domain.Person; import module.organization.domain.Unit; import org.fenixedu.bennu.core.domain.User; import org.joda.time.LocalDate; import pt.ist.expenditureTrackingSystem.domain.authorizations.Authorization; import pt.ist.fenixframework.Atomic; /** * * @author Luis Cruz * */ public class ParticipantAuthorizationChain implements Serializable { private Person person; private AuthorizationChain authorizationChain; public ParticipantAuthorizationChain(final Person person, final AuthorizationChain authorizationChain) { setPerson(person); setAuthorizationChain(authorizationChain); } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public AuthorizationChain getAuthorizationChain() { return authorizationChain; } public void setAuthorizationChain(final AuthorizationChain authorizationChain) { this.authorizationChain = removeSelfAuthorizationSteps(authorizationChain); } private AuthorizationChain removeSelfAuthorizationSteps(final AuthorizationChain authorizationChain) { final AuthorizationChain next = authorizationChain.getNext(); if (next == null) { return authorizationChain; } if (canSelfAuthorize(authorizationChain.getUnit())) { return removeSelfAuthorizationSteps(next); } authorizationChain.setNext(removeSelfAuthorizationSteps(next)); return authorizationChain; } private boolean canSelfAuthorize(final Unit unit) { return unit.getChildAccountabilityStream().anyMatch( a -> MissionSystem.AUTHORIZATION_PREDICATE.test(a) && a.getChild() == person && a.isActiveNow()); } private static Collection<AccountabilityType> getAccountabilityTypes() { final OrganizationalModel organizationalModel = MissionSystem.getInstance().getOrganizationalModel(); return organizationalModel == null ? null : organizationalModel.getAccountabilityTypesSet(); } public static Collection<ParticipantAuthorizationChain> getParticipantAuthorizationChains(final Person person) { if (!isEmployeeOfInstitution(person) || !MissionSystem.getInstance().useWorkingPlaceAuthorizationChain()) { return Collections.emptySet(); } final Collection<Accountability> parentAccountabilities = person.getParentAccountabilityStream().filter(a -> MissionSystem.REQUIRE_AUTHORIZATION_PREDICATE.test(a)) .collect(Collectors.toSet()); final Collection<ParticipantAuthorizationChain> participantAuthorizationChains = new ArrayList<ParticipantAuthorizationChain>(); for (final AuthorizationChain authorizationChain : getParticipantAuthorizationChains(parentAccountabilities)) { final ParticipantAuthorizationChain participantAuthorizationChain = new ParticipantAuthorizationChain(person, authorizationChain); participantAuthorizationChains.add(participantAuthorizationChain); } return participantAuthorizationChains; } public static Collection<ParticipantAuthorizationChain> getParticipantAuthorizationChains(final Person person, final Unit unit) { final Collection<ParticipantAuthorizationChain> participantAuthorizationChains = new ArrayList<ParticipantAuthorizationChain>(); final OrganizationalModel organizationalModel = MissionSystem.getInstance().getOrganizationalModel(); final Set<AccountabilityType> accountabilityTypes = organizationalModel.getAccountabilityTypesSet(); for (final Party party : organizationalModel.getPartiesSet()) { if (party.isUnit()) { final Unit topLevelUnot = (Unit) party; topLevelUnot.getChildAccountabilityStream().filter(a -> match(a, accountabilityTypes) && a.getChild().isUnit()) .map(a -> (Unit) a.getChild()).forEach(new Consumer<Unit>() { @Override public void accept(final Unit childUnit) { final AuthorizationChain topLevelUnitChain = new AuthorizationChain(topLevelUnot); final AuthorizationChain childChain = new AuthorizationChain(childUnit, topLevelUnitChain); final Unit firstUnitWithResponsible = findFirstUnitWithResponsible(unit); final AuthorizationChain authorizationChain = new AuthorizationChain(firstUnitWithResponsible, childChain); final ParticipantAuthorizationChain participantAuthorizationChain = new ParticipantAuthorizationChain(person, authorizationChain); participantAuthorizationChains.add(participantAuthorizationChain); if (!unit.getChildAccountabilityStream().anyMatch( a -> MissionSystem.AUTHORIZATION_PREDICATE.test(a) && a.getChild().isPerson())) { createResponsibleForUnit(firstUnitWithResponsible); } } });; } } return participantAuthorizationChains; } private static boolean match(final Accountability a, final Collection<AccountabilityType> accountabilityTypes) { return accountabilityTypes.isEmpty() || accountabilityTypes.contains(a.getAccountabilityType()); } private static Unit findFirstUnitWithResponsible(final Unit unit) { final pt.ist.expenditureTrackingSystem.domain.organization.Unit expenditureUnit = unit.getExpenditureUnit(); if (expenditureUnit != null) { for (final Authorization authorization : expenditureUnit.getAuthorizationsSet()) { if (authorization.isValid()) { return unit; } } final pt.ist.expenditureTrackingSystem.domain.organization.Unit parentUnit = expenditureUnit.getParentUnit(); if (parentUnit != null) { return findFirstUnitWithResponsible(parentUnit.getUnit()); } } return null; } @Atomic private static void createResponsibleForUnit(final Unit unit) { for (final Authorization authorization : unit.getExpenditureUnit().getAuthorizationsSet()) { if (authorization.isValid()) { final pt.ist.expenditureTrackingSystem.domain.organization.Person authority = authorization.getPerson(); final User user = authority.getUser(); if (user != null && user.getPerson() != null) { MissionSystem.getInstance().getMissionAuthorizationAccountabilityTypesSet().stream() .flatMap(t -> t.getAccountabilityTypesSet().stream()).distinct() .forEach(t -> unit.addChild(user.getPerson(), t, new LocalDate(), null, null)); } } } } public static ParticipantAuthorizationChain getMostLikelyParticipantAuthorizationChain(final Person person) { final Collection<ParticipantAuthorizationChain> participantAuthorizationChains = ParticipantAuthorizationChain.getParticipantAuthorizationChains(person); final int count = participantAuthorizationChains.size(); if (count > 0) { if (count == 1) { final ParticipantAuthorizationChain participantAuthorizationChain = participantAuthorizationChains.iterator().next(); return participantAuthorizationChain; } else { return Collections.max(participantAuthorizationChains, new Comparator<ParticipantAuthorizationChain>() { @Override public int compare(ParticipantAuthorizationChain o1, ParticipantAuthorizationChain o2) { return o1.getChainSize() - o2.getChainSize(); } }); } } return null; } public static boolean isEmployeeOfInstitution(final Person person) { final MissionSystem system = MissionSystem.getInstance(); final OrganizationalModel model = system.getOrganizationalModel(); final AccountabilityType employeeType = system.getEmploymentAccountabilityType(); return person.getParentAccountabilityStream().anyMatch( a -> a.getAccountabilityType() == employeeType && model.getPartiesSet().contains(a.getParent())); } protected int getChainSize() { return getAuthorizationChain().getChainSize(); } private static Collection<AuthorizationChain> getParticipantAuthorizationChains( final Collection<Accountability> accountabilities) { final Collection<AuthorizationChain> result = new ArrayList<AuthorizationChain>(); for (final Accountability accountability : accountabilities) { if (accountability.isActiveNow()) { final AccountabilityType accountabilityType = accountability.getAccountabilityType(); final Set<AccountabilityType> accountabilityTypes = MissionSystem.getInstance().getAccountabilityTypesForAuthorization(accountabilityType); final Predicate<Accountability> predicate = new Predicate<Accountability>() { @Override public boolean test(Accountability a) { return accountabilityTypes.contains(a.getAccountabilityType()); } }; if (accountabilityTypes != null && !accountabilityTypes.isEmpty()) { for (final AuthorizationChain chain : getParticipantAuthorizationChains(predicate, accountability)) { if (chain.isForCurrentInstitution()) { result.add(chain); } } } } } return result; } public static Collection<AuthorizationChain> getParticipantAuthorizationChains(final Predicate<Accountability> predicate, final Accountability accountability) { final Party party = accountability.getParent(); if (party.isUnit()) { final Unit unit = (Unit) party; final Supplier<Stream<Accountability>> parentAccountabilities = () -> unit.getParentAccountabilityStream().filter(a -> match(a, getAccountabilityTypes())); if (hasPersonResponsible(predicate, party)) { final Collection<AuthorizationChain> result = new ArrayList<AuthorizationChain>(); if (parentAccountabilities.get().findAny().orElse(null) == null) { final AuthorizationChain authorizationChain = new AuthorizationChain(unit); result.add(authorizationChain); return result; } else { parentAccountabilities.get().flatMap(a -> getParticipantAuthorizationChains(predicate, a).stream()) .map(c -> new AuthorizationChain(unit, c)).forEach(c -> result.add(c)); } return result; } else { return parentAccountabilities.get().flatMap(a -> getParticipantAuthorizationChains(predicate, a).stream()) .collect(Collectors.toSet()); } } return Collections.emptyList(); } private static boolean hasPersonResponsible(final Predicate<Accountability> predicate, final Party party) { return party.getChildAccountabilityStream().anyMatch(a -> predicate.test(a) && a.isActive(new LocalDate())); } }