/*
* @(#)RefundProcess.java
*
* Copyright 2009 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 pt.ist.expenditureTrackingSystem.domain.acquisitions.refund;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
import java.util.Set;
import org.fenixedu.bennu.core.domain.User;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import module.finance.util.Money;
import module.workflow.activities.ActivityInformation;
import module.workflow.activities.GiveProcess;
import module.workflow.activities.ReleaseProcess;
import module.workflow.activities.StealProcess;
import module.workflow.activities.TakeProcess;
import module.workflow.activities.WorkflowActivity;
import module.workflow.domain.ProcessFile;
import module.workflow.domain.WorkflowProcess;
import module.workflow.util.ClassNameBundle;
import module.workflow.util.PresentableProcessState;
import pt.ist.expenditureTrackingSystem._development.Bundle;
import pt.ist.expenditureTrackingSystem.domain.ExpenditureTrackingSystem;
import pt.ist.expenditureTrackingSystem.domain.ProcessState;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.AcquisitionItemClassification;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.CPVReference;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.Financer;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.RefundProcessState;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.RefundProcessStateType;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.RequestItem;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.AllocateFundsPermanently;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.AllocateProjectFundsPermanently;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.Approve;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.Authorize;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.FundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.GenericAddPayingUnit;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.GenericAssignPayingUnitToItem;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.GenericRemovePayingUnit;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.ProjectFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.RemoveCancelProcess;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.RemoveFundsPermanentlyAllocated;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.RemovePermanentProjectFunds;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.UnApprove;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.activities.commons.UnAuthorize;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.CancelRefundProcess;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.ChangeFinancersAccountingUnit;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.ChangeProcessRequester;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.ChangeRefundItemClassification;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.ConfirmInvoices;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.CreateRefundInvoice;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.CreateRefundItem;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.DeleteRefundItem;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.DistributeRealValuesForPayingUnits;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.EditRefundInvoice;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.EditRefundItem;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.RefundPerson;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.RemoveFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.RemoveProjectFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.RemoveRefundInvoice;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.RevertInvoiceConfirmationSubmition;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.SetSkipSupplierFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.SubmitForApproval;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.SubmitForInvoiceConfirmation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.UnSubmitForApproval;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.UnSubmitForFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.UnconfirmInvoices;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.refund.activities.UnsetSkipSupplierFundAllocation;
import pt.ist.expenditureTrackingSystem.domain.acquisitions.simplified.Util;
import pt.ist.expenditureTrackingSystem.domain.authorizations.Authorization;
import pt.ist.expenditureTrackingSystem.domain.dto.CreateRefundProcessBean;
import pt.ist.expenditureTrackingSystem.domain.organization.Person;
import pt.ist.expenditureTrackingSystem.domain.organization.Supplier;
import pt.ist.expenditureTrackingSystem.domain.organization.Unit;
import pt.ist.expenditureTrackingSystem.domain.util.DomainException;
import pt.ist.fenixframework.Atomic;
@ClassNameBundle(bundle = "ExpenditureResources")
/**
*
* @author João Neves
* @author Paulo Abrantes
* @author Luis Cruz
* @author João Alfaiate
*
*/
public class RefundProcess extends RefundProcess_Base {
private static List<WorkflowActivity<RefundProcess, ? extends ActivityInformation<RefundProcess>>> activities =
new ArrayList<WorkflowActivity<RefundProcess, ? extends ActivityInformation<RefundProcess>>>();
static {
activities.add(new Approve<RefundProcess>());
activities.add(new UnApprove<RefundProcess>());
activities.add(new CancelRefundProcess());
activities.add(new ConfirmInvoices());
activities.add(new RemoveFundAllocation());
activities.add(new RemoveProjectFundAllocation());
activities.add(new RevertInvoiceConfirmationSubmition());
activities.add(new SetSkipSupplierFundAllocation());
activities.add(new SubmitForApproval());
activities.add(new SubmitForInvoiceConfirmation());
activities.add(new UnconfirmInvoices());
activities.add(new UnsetSkipSupplierFundAllocation());
activities.add(new UnSubmitForApproval());
activities.add(new UnSubmitForFundAllocation());
activities.add(new CreateRefundItem());
activities.add(new EditRefundItem());
activities.add(new DeleteRefundItem());
activities.add(new CreateRefundInvoice());
activities.add(new RemoveRefundInvoice());
activities.add(new GenericAddPayingUnit<RefundProcess>());
activities.add(new GenericRemovePayingUnit<RefundProcess>());
activities.add(new Authorize<RefundProcess>());
activities.add(new UnAuthorize<RefundProcess>());
activities.add(new GenericAssignPayingUnitToItem<RefundProcess>());
activities.add(new DistributeRealValuesForPayingUnits());
activities.add(new FundAllocation<RefundProcess>());
activities.add(new ProjectFundAllocation<RefundProcess>());
activities.add(new RemoveFundsPermanentlyAllocated<RefundProcess>());
activities.add(new RemovePermanentProjectFunds<RefundProcess>());
activities.add(new TakeProcess<RefundProcess>());
activities.add(new GiveProcess<RefundProcess>());
activities.add(new ReleaseProcess<RefundProcess>());
activities.add(new StealProcess<RefundProcess>());
// activities.add(new GiveProcess<RefundProcess>());
activities.add(new EditRefundInvoice());
activities.add(new AllocateProjectFundsPermanently<RefundProcess>());
activities.add(new AllocateFundsPermanently<RefundProcess>());
activities.add(new ChangeFinancersAccountingUnit());
activities.add(new ChangeProcessRequester());
activities.add(new ChangeRefundItemClassification());
activities.add(new RemoveCancelProcess<RefundProcess>());
activities.add(new RefundPerson());
// activities.add(new MarkProcessAsCCPProcess());
// activities.add(new UnmarkProcessAsCCPProcess());
}
public RefundProcess(Person requestor, String refundeeName, String refundeeFiscalCode, Unit requestingUnit) {
super();
new RefundRequest(this, requestor, refundeeName, refundeeFiscalCode, requestingUnit);
new RefundProcessState(this, RefundProcessStateType.IN_GENESIS);
setSkipSupplierFundAllocation(Boolean.FALSE);
setProcessNumber(constructProcessNumber());
}
public RefundProcess(Person requestor, Person refundee, Unit requestingUnit) {
super();
new RefundRequest(this, requestor, refundee, requestingUnit);
new RefundProcessState(this, RefundProcessStateType.IN_GENESIS);
setSkipSupplierFundAllocation(Boolean.FALSE);
setProcessNumber(constructProcessNumber());
}
protected String constructProcessNumber() {
final ExpenditureTrackingSystem instance = getExpenditureTrackingSystem();
if (instance.hasProcessPrefix()) {
return instance.getInstitutionalProcessNumberPrefix() + "/" + getYear() + "/" + getAcquisitionProcessNumber();
}
return getYear() + "/" + getAcquisitionProcessNumber();
}
@Atomic
public static RefundProcess createNewRefundProcess(CreateRefundProcessBean bean) {
RefundProcess process =
bean.isExternalPerson() ? new RefundProcess(bean.getRequestor(), bean.getRefundeeName(),
bean.getRefundeeFiscalCode(), bean.getRequestingUnit()) : new RefundProcess(bean.getRequestor(),
bean.getRefundee(), bean.getRequestingUnit());
process.setUnderCCPRegime(bean.isUnderCCP());
if (bean.isRequestUnitPayingUnit()) {
process.getRequest().addPayingUnit(bean.getRequestingUnit());
}
if (bean.isForMission()) {
if (bean.getMissionProcess() == null) {
throw new DomainException(Bundle.EXPENDITURE, "mission.process.is.mandatory");
}
process.setMissionProcess(bean.getMissionProcess());
}
return process;
}
protected RefundProcessState getLastProcessState() {
return (RefundProcessState) Collections.max(getProcessStates(), ProcessState.COMPARATOR_BY_WHEN);
}
public RefundProcessState getProcessState() {
return getLastProcessState();
}
@Override
public boolean isInGenesis() {
return getProcessState().isInGenesis();
}
@Override
public Person getRequestor() {
return getRequest().getRequester();
}
@Override
public void submitForApproval() {
new RefundProcessState(this, RefundProcessStateType.SUBMITTED_FOR_APPROVAL);
}
public void unSubmitForApproval() {
final RefundProcessState refundProcessState = getProcessState();
refundProcessState.setRefundProcessStateType(RefundProcessStateType.IN_GENESIS);
}
@Override
public boolean isPendingApproval() {
final RefundProcessState refundProcessState = getProcessState();
return refundProcessState.isPendingApproval();
}
@Override
public void submitForFundAllocation() {
createFundAllocationRequest(false);
new RefundProcessState(this, RefundProcessStateType.APPROVED);
}
public void createFundAllocationRequest(final boolean isFinalFundAllocation) {
final RefundRequest refundRequest = getRequest();
refundRequest.createFundAllocationRequest(isFinalFundAllocation);
}
@Override
public boolean isInApprovedState() {
return getProcessState().isInApprovedState();
}
@Override
public boolean isInAllocatedToUnitState() {
return getProcessState().isInAllocatedToUnitState();
}
@Override
protected void authorize() {
new RefundProcessState(this, RefundProcessStateType.AUTHORIZED);
}
@Override
public boolean isPendingFundAllocation() {
return isInApprovedState();
}
@Override
public void allocateFundsToUnit() {
new RefundProcessState(this, RefundProcessStateType.FUNDS_ALLOCATED);
}
@Override
public boolean isInAuthorizedState() {
return getProcessState().isAuthorized();
}
public void unApproveByAll() {
getRequest().unSubmitForFundsAllocation();
}
public boolean isInSubmittedForInvoiceConfirmationState() {
return getProcessState().isInSubmittedForInvoiceConfirmationState();
}
public List<RefundableInvoiceFile> getRefundableInvoices() {
List<RefundableInvoiceFile> invoices = new ArrayList<RefundableInvoiceFile>();
for (RequestItem item : getRequest().getRequestItems()) {
invoices.addAll(((RefundItem) item).getRefundableInvoices());
}
return invoices;
}
public void confirmInvoicesByPerson(Person person) {
for (RequestItem item : getRequest().getRequestItems()) {
item.confirmInvoiceBy(person);
}
if (getRequest().isConfirmedForAllInvoices()) {
createFundAllocationRequest(true);
confirmInvoices();
}
}
public void unconfirmInvoicesByPerson(Person person) {
for (RequestItem item : getRequest().getRequestItems()) {
item.unconfirmInvoiceBy(person);
}
submitForInvoiceConfirmation();
}
public void revertInvoiceConfirmationSubmition() {
new RefundProcessState(this, RefundProcessStateType.AUTHORIZED);
}
public void submitForInvoiceConfirmation() {
new RefundProcessState(this, RefundProcessStateType.SUBMITTED_FOR_INVOICE_CONFIRMATION);
}
public void confirmInvoices() {
new RefundProcessState(this, RefundProcessStateType.INVOICES_CONFIRMED);
}
public boolean isPendingInvoicesConfirmation() {
return getProcessState().isPendingInvoicesConfirmation();
}
@Override
public boolean isActive() {
return getProcessState().isActive();
}
public Integer getYear() {
return getPaymentProcessYear().getYear();
}
/*
* use getProcessNumber() instead
*/
@Override
@Deprecated
public String getAcquisitionProcessId() {
return getProcessNumber();
}
@Override
public boolean isInvoiceConfirmed() {
return getProcessState().isInvoiceConfirmed();
}
@Override
public void allocateFundsPermanently() {
new RefundProcessState(this, RefundProcessStateType.FUNDS_ALLOCATED_PERMANENTLY);
}
@Override
public boolean isAllocatedPermanently() {
return getProcessState().isAllocatedPermanently();
}
@Override
public void resetEffectiveFundAllocationId() {
getRequest().resetEffectiveFundAllocationId();
confirmInvoice();
}
protected void confirmInvoice() {
new RefundProcessState(this, RefundProcessStateType.INVOICES_CONFIRMED);
}
public boolean hasFundsAllocatedPermanently() {
return getProcessState().hasFundsAllocatedPermanently();
}
public void refundPerson(final String paymentReference) {
getRequest().setPaymentReference(paymentReference);
new RefundProcessState(this, RefundProcessStateType.REFUNDED);
}
@Override
public boolean isPayed() {
return getRequest().isPayed();
}
public boolean isAnyRefundInvoiceAvailable() {
for (RefundItem item : getRequest().getRefundItemsSet()) {
if (!item.getRefundableInvoices().isEmpty()) {
return true;
}
}
return false;
}
@Override
public boolean isAccessible(User user) {
return isAvailableForPerson(user.getExpenditurePerson());
}
public boolean isAvailableForCurrentUser() {
final Person loggedPerson = Person.getLoggedPerson();
return loggedPerson != null && isAvailableForPerson(loggedPerson);
}
public boolean isAvailableForPerson(final Person person) {
final User user = person.getUser();
return ExpenditureTrackingSystem.isAcquisitionCentralGroupMember(user)
|| ExpenditureTrackingSystem.isAcquisitionCentralManagerGroupMember(user)
|| ExpenditureTrackingSystem.isAccountingManagerGroupMember(user)
|| ExpenditureTrackingSystem.isProjectAccountingManagerGroupMember(user)
|| ExpenditureTrackingSystem.isTreasuryMemberGroupMember(user)
|| ExpenditureTrackingSystem.isAcquisitionsProcessAuditorGroupMember(user)
|| ExpenditureTrackingSystem.isFundCommitmentManagerGroupMember(user) || getRequestor() == person
|| getRequest().getRequestingUnit().isResponsible(person) || isResponsibleForAtLeastOnePayingUnit(person)
|| isAccountingEmployee(person) || isProjectAccountingEmployee(person) || isTreasuryMember(person)
|| isObserver(person);
}
@Override
public boolean isAuthorized() {
return super.isAuthorized() && getRefundableInvoices().isEmpty();
}
@Override
public boolean isCanceled() {
return getProcessState().isCanceled();
}
@Override
public boolean isRefundProcess() {
return true;
}
public void cancel() {
getRequest().cancel();
new RefundProcessState(this, RefundProcessStateType.CANCELED);
}
@Override
public String getProcessStateDescription() {
return getLastProcessState().getLocalizedName();
}
@Override
public Set<Supplier> getSuppliers() {
Set<Supplier> suppliers = new HashSet<Supplier>();
for (RefundableInvoiceFile invoice : getRefundableInvoices()) {
final Supplier supplier = invoice.getSupplier();
if (supplier != null) {
suppliers.add(supplier);
}
}
return suppliers;
}
@Override
public boolean isAppiableForYear(final int year) {
return Util.isAppiableForYear(year, this);
}
@Override
public String getProcessStateName() {
return getProcessState().getLocalizedName();
}
@Override
public int getProcessStateOrder() {
return getProcessState().getRefundProcessStateType().ordinal();
}
@Override
public <T extends WorkflowActivity<? extends WorkflowProcess, ? extends ActivityInformation>> List<T> getActivities() {
return (List<T>) activities;
}
public <T extends WorkflowActivity<? extends WorkflowProcess, ? extends ActivityInformation>> Stream<T> getActivityStream() {
final List activities = this.activities;
return activities.stream();
}
@Override
public void revertToState(ProcessState processState) {
final RefundProcessState refundProcessState = (RefundProcessState) processState;
final RefundProcessStateType refundProcessStateType = refundProcessState.getRefundProcessStateType();
if (refundProcessStateType != null && refundProcessStateType != RefundProcessStateType.CANCELED) {
new RefundProcessState(this, refundProcessStateType);
}
}
@Override
public String getLocalizedName() {
return BundleUtil.getString("resources/AcquisitionResources", "label.RefundProcess");
}
public Boolean getShouldSkipSupplierFundAllocation() {
return !getUnderCCPRegime() || super.getSkipSupplierFundAllocation();
}
@Override
public PresentableProcessState getPresentableAcquisitionProcessState() {
return getProcessState().getRefundProcessStateType();
}
@Override
public List<? extends PresentableProcessState> getAvailablePresentableStates() {
return Arrays.asList(RefundProcessStateType.values());
}
@Override
public List<Class<? extends ProcessFile>> getAvailableFileTypes() {
List<Class<? extends ProcessFile>> availableFileTypes = new ArrayList<Class<? extends ProcessFile>>();
availableFileTypes.add(RefundableInvoiceFile.class);
availableFileTypes.addAll(super.getAvailableFileTypes());
return availableFileTypes;
}
@Override
public List<Class<? extends ProcessFile>> getUploadableFileTypes() {
List<Class<? extends ProcessFile>> uploadableFileTypes = super.getUploadableFileTypes();
uploadableFileTypes.remove(RefundableInvoiceFile.class);
return uploadableFileTypes;
}
@Override
public Money getTotalValue() {
return getRequest().getCurrentTotalValue();
}
@Override
public Set<CPVReference> getCPVReferences() {
final RefundRequest request = getRequest();
return request.getCPVReferences();
}
public void checkIsFundAllocationAllowed() {
final Map<Supplier, Map<CPVReference, Money>> allocationMap = new HashMap<Supplier, Map<CPVReference, Money>>();
for (final RefundItem refundItem : getRequest().getRefundItemsSet()) {
final CPVReference cpvReference = refundItem.getCPVReference();
for (final RefundableInvoiceFile refundInvoice : refundItem.getRefundableInvoices()) {
final Supplier supplier = refundInvoice.getSupplier();
if (supplier != null) {
final String key = cpvReference.getExternalId() + supplier.getExternalId();
final Money refundableValue = refundInvoice.getRefundableValue();
if (!allocationMap.containsKey(supplier)) {
allocationMap.put(supplier, new HashMap<CPVReference, Money>());
}
final Map<CPVReference, Money> map = allocationMap.get(supplier);
if (map.containsKey(cpvReference)) {
map.put(cpvReference, refundableValue.add(map.get(cpvReference)));
} else {
map.put(cpvReference, refundableValue);
}
}
}
}
final boolean checkSupplierLimitsByCPV = ExpenditureTrackingSystem.getInstance().checkSupplierLimitsByCPV();
for (final Entry<Supplier, Map<CPVReference, Money>> entry : allocationMap.entrySet()) {
final Supplier supplier = entry.getKey();
final Map<CPVReference, Money> map = entry.getValue();
Money total = Money.ZERO;
for (final Entry<CPVReference, Money> centry : map.entrySet()) {
final CPVReference cpvReference = centry.getKey();
final Money value = centry.getValue();
if (checkSupplierLimitsByCPV && !supplier.isFundAllocationAllowed(cpvReference.getCode(), value)) {
throw new DomainException(Bundle.ACQUISITION,
"acquisitionProcess.message.exception.SupplierDoesNotAlloweAmount");
}
total = total.add(value);
}
if (!checkSupplierLimitsByCPV && !supplier.isFundAllocationAllowed(total)) {
throw new DomainException(Bundle.ACQUISITION, "acquisitionProcess.message.exception.SupplierDoesNotAlloweAmount");
}
}
}
@Override
public AcquisitionItemClassification getGoodsOrServiceClassification() {
final RefundRequest request = getRequest();
return request.getGoodsOrServiceClassification();
}
@Deprecated
public boolean hasUnderCCPRegime() {
return getUnderCCPRegime() != null;
}
@Deprecated
public boolean hasRequest() {
return getRequest() != null;
}
}