nl.strohalm.cyclos.services.accounts.guarantees.CertificationServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.services.accounts.guarantees.CertificationServiceImpl.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
Cyclos 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 General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.services.accounts.guarantees;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import nl.strohalm.cyclos.access.AdminMemberPermission;
import nl.strohalm.cyclos.dao.accounts.guarantees.CertificationDAO;
import nl.strohalm.cyclos.dao.accounts.guarantees.CertificationLogDAO;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.accounts.Currency;
import nl.strohalm.cyclos.entities.accounts.guarantees.Certification;
import nl.strohalm.cyclos.entities.accounts.guarantees.Certification.Status;
import nl.strohalm.cyclos.entities.accounts.guarantees.CertificationLog;
import nl.strohalm.cyclos.entities.accounts.guarantees.CertificationQuery;
import nl.strohalm.cyclos.entities.accounts.guarantees.Guarantee;
import nl.strohalm.cyclos.entities.accounts.guarantees.GuaranteeType;
import nl.strohalm.cyclos.entities.accounts.guarantees.PaymentObligation;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.services.InitializingService;
import nl.strohalm.cyclos.services.accounts.guarantees.exceptions.CertificationStatusChangeException;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.services.permissions.PermissionServiceLocal;
import nl.strohalm.cyclos.utils.DateHelper;
import nl.strohalm.cyclos.utils.Period;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.notifications.MemberNotificationHandler;
import nl.strohalm.cyclos.utils.query.Page;
import nl.strohalm.cyclos.utils.query.PageImpl;
import nl.strohalm.cyclos.utils.query.PageParameters;
import nl.strohalm.cyclos.utils.query.QueryParameters.ResultType;
import nl.strohalm.cyclos.utils.validation.GeneralValidation;
import nl.strohalm.cyclos.utils.validation.PeriodValidation;
import nl.strohalm.cyclos.utils.validation.PeriodValidation.ValidationType;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.ValidationException;
import nl.strohalm.cyclos.utils.validation.Validator;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Transformer;

public class CertificationServiceImpl implements CertificationServiceLocal, InitializingService {

    private class ExistingActiveCertificationValidation implements GeneralValidation {
        private static final long serialVersionUID = 840449718151754491L;

        @Override
        public ValidationError validate(final Object object) {
            final Certification certification = (Certification) object;
            final GuaranteeType guaranteeType = certification.getGuaranteeType();
            final Member buyer = certification.getBuyer();
            final Member issuer = certification.getIssuer();

            if (guaranteeType == null || buyer == null || issuer == null) {
                return null;
            } else if (willBeActive(certification)
                    && getActiveCertification(guaranteeType, buyer, issuer) != null) {
                return new ValidationError("certification.error.certificationActiveExists");
            } else {
                return null;
            }
        }

        private boolean willBeActive(final Certification certification) {
            Calendar begin = certification.getValidity() == null ? null : certification.getValidity().getBegin();
            Calendar end = certification.getValidity() == null ? null : certification.getValidity().getEnd();

            if (begin == null || end == null) {
                return false;
            } else {
                final Calendar currentDate = DateHelper.truncate(Calendar.getInstance());
                begin = DateHelper.truncate(begin);
                end = DateHelper.truncate(end);

                return (begin.before(currentDate) || begin.equals(currentDate))
                        && (end.after(currentDate) || end.equals(currentDate));
            }
        }
    }

    private PermissionServiceLocal permissionService;
    private GuaranteeServiceLocal guaranteeService;
    private CertificationDAO certificationDao;
    private CertificationLogDAO certificationLogDao;
    private MemberNotificationHandler memberNotificationHandler;
    private FetchServiceLocal fetchService;

    @Override
    public boolean canChangeStatus(final Certification certification, final Certification.Status newStatus) {
        boolean isIssuer;
        switch (newStatus) {
        case ACTIVE:
            final Certification activeCert = getActiveCertification(certification.getGuaranteeType().getCurrency(),
                    certification.getBuyer(), certification.getIssuer());
            isIssuer = guaranteeService.isIssuer() && certification.getIssuer().equals(LoggedUser.accountOwner());
            return isIssuer && isInSomeStatus(certification, Status.SUSPENDED)
                    && (activeCert == null || Status.SCHEDULED == calculateInitialStatus(certification));
        case CANCELLED:
            final boolean hasPermission = permissionService
                    .hasPermission(AdminMemberPermission.GUARANTEES_CANCEL_CERTIFICATIONS_AS_MEMBER);
            return hasPermission
                    && isInSomeStatus(certification, Status.ACTIVE, Status.SUSPENDED, Status.SCHEDULED);
        case SUSPENDED:
            isIssuer = guaranteeService.isIssuer() && certification.getIssuer().equals(LoggedUser.accountOwner());
            return isIssuer && isInSomeStatus(certification, Status.ACTIVE, Status.SCHEDULED);
        default:
            throw new CertificationStatusChangeException(newStatus,
                    "Can't change certification's status, unsupported target status: " + newStatus);
        }
    }

    /**
     * @return true only if the certification's status is CANCELED and the logged user has the right permission granted
     */
    @Override
    public boolean canDelete(Certification certification) {
        certification = fetchService.fetch(certification, Certification.Relationships.ISSUER);
        final boolean hasPermision = permissionService.manages(certification.getIssuer()) && permissionService
                .hasPermission(AdminMemberPermission.GUARANTEES_CANCEL_CERTIFICATIONS_AS_MEMBER);
        return hasPermision && isInSomeStatus(certification, Certification.Status.CANCELLED);
    }

    @Override
    public void changeStatus(final Long certificationId, Certification.Status newStatus) {
        Certification certification = load(certificationId);

        final boolean changeAllowed = canChangeStatus(certification, newStatus);

        if (!changeAllowed) {
            throw new CertificationStatusChangeException(newStatus);
        } else {
            // if it's an activation we must check by certification's starting date
            if (newStatus == Status.ACTIVE) {
                newStatus = calculateInitialStatus(certification);
            }
            final CertificationLog log = certification.changeStatus(newStatus, LoggedUser.user().getElement());
            saveLog(log);
            certification = save(certification, false);

            switch (newStatus) {
            case CANCELLED:
                memberNotificationHandler.certificationCanceledNotification(certificationId);
                break;
            default:
                memberNotificationHandler.certificationStatusChangedNotification(certification);
            }
        }
    }

    @Override
    public Certification getActiveCertification(final Currency currency, final Member buyer, final Member issuer) {
        final List<Certification> activeCertifications = certificationDao.getActiveCertificationsForBuyer(buyer,
                currency);

        for (final Certification cert : activeCertifications) {
            if (issuer.equals(cert.getIssuer())) {
                return cert;
            }
        }

        return null;
    }

    @Override
    public List<Member> getCertificationIssuers(final PaymentObligation paymentObligation) {
        final List<Certification> activeCertifications = certificationDao
                .getActiveCertificationsForBuyer(paymentObligation.getBuyer(), paymentObligation.getCurrency());

        final ArrayList<Member> issuers = new ArrayList<Member>();
        for (final Certification cert : activeCertifications) {
            issuers.add(cert.getIssuer());
        }

        return issuers;
    }

    @Override
    public BigDecimal getUsedAmount(final Certification certification, final boolean includePendingGuarantees) {
        // if we musn't take into account the loan repay then use the DAO implementation
        // return certificationDao.getUsedAmount(certification);

        // this implementation take into account the loan repayment
        final List<Guarantee.Status> statusList = new ArrayList<Guarantee.Status>();
        statusList.add(Guarantee.Status.ACCEPTED);
        if (includePendingGuarantees) {
            statusList.add(Guarantee.Status.PENDING_ADMIN);
            statusList.add(Guarantee.Status.PENDING_ISSUER);
        }
        final List<Guarantee> guarantees = guaranteeService.getGuarantees(certification, PageParameters.all(),
                statusList);

        BigDecimal notPaidAmount = BigDecimal.ZERO;
        for (final Guarantee g : guarantees) {
            if (g.getStatus() == Guarantee.Status.ACCEPTED && g.getLoan() != null) {
                notPaidAmount = notPaidAmount.add(g.getLoan().getRemainingAmount());
            } else { // if pending there is no associated loan
                notPaidAmount = notPaidAmount.add(g.getAmount());
            }
        }

        return notPaidAmount;
    }

    @Override
    public void initializeService() {
        processCertifications(Calendar.getInstance());
    }

    @Override
    public Certification load(final Long id, final Relationship... fetch) {
        return certificationDao.load(id, fetch);
    }

    @Override
    public void processCertifications(final Calendar taskTime) {
        processCertifications(taskTime, Certification.Status.ACTIVE);
        processCertifications(taskTime, Certification.Status.EXPIRED);
    }

    @Override
    public int remove(final Long certificationId) {
        return certificationDao.delete(certificationId);
    }

    @Override
    public Certification save(final Certification certification) {
        return save(certification, true);
    }

    @Override
    public List<Certification> search(final CertificationQuery queryParameters) {
        return certificationDao.seach(queryParameters);
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<CertificationDTO> searchWithUsedAmount(final CertificationQuery queryParameters) {
        final List<Certification> certifications = certificationDao.seach(queryParameters);

        final Transformer transformer = new Transformer() {
            @Override
            public Object transform(final Object input) {
                final Certification certification = (Certification) input;
                return new CertificationDTO(certification, getUsedAmount(certification, false));
            }
        };

        List<CertificationDTO> result = (List<CertificationDTO>) CollectionUtils.collect(certifications,
                transformer, new ArrayList<CertificationDTO>());
        if (certifications instanceof Page) {
            final Page<Certification> original = (Page<Certification>) certifications;
            final PageParameters pageParameters = new PageParameters(original.getPageSize(),
                    original.getCurrentPage());
            result = new PageImpl<CertificationDTO>(pageParameters, original.getTotalCount(), result);
        }

        return result;
    }

    public void setCertificationDao(final CertificationDAO certificationDao) {
        this.certificationDao = certificationDao;
    }

    public void setCertificationLogDao(final CertificationLogDAO certificationLogDao) {
        this.certificationLogDao = certificationLogDao;
    }

    public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
        this.fetchService = fetchService;
    }

    public void setGuaranteeServiceLocal(final GuaranteeServiceLocal guaranteeService) {
        this.guaranteeService = guaranteeService;
    }

    public void setMemberNotificationHandler(final MemberNotificationHandler memberNotificationHandler) {
        this.memberNotificationHandler = memberNotificationHandler;
    }

    public void setPermissionServiceLocal(final PermissionServiceLocal permissionService) {
        this.permissionService = permissionService;
    }

    @Override
    public void validate(final Certification certification) {
        // we must run this general validation before to prevent validation process if this fails
        final GeneralValidation val = new ExistingActiveCertificationValidation();
        final ValidationError error = val.validate(certification);
        if (error != null) {
            final ValidationException vex = new ValidationException();
            vex.addGeneralError(error);
            vex.throwIfHasErrors();
        } else {
            getValidator().validate(certification);
        }
    }

    private Status calculateInitialStatus(final Certification certification) {
        final Calendar currentDate = DateHelper.truncate(Calendar.getInstance());
        return DateHelper.truncate(certification.getValidity().getBegin()).after(currentDate)
                ? Certification.Status.SCHEDULED
                : Certification.Status.ACTIVE;
    }

    private Certification getActiveCertification(GuaranteeType guaranteeType, final Member buyer,
            final Member issuer) {
        guaranteeType = fetchService.fetch(guaranteeType, GuaranteeType.Relationships.CURRENCY);
        return getActiveCertification(guaranteeType.getCurrency(), buyer, issuer);
    }

    private Validator getValidator() {
        final Validator validator = new Validator("certification");
        validator.property("amount").required().positiveNonZero();
        validator.property("guaranteeType").required().key("certification.guaranteeType");
        validator.property("buyer").required().key("certification.buyerUsername");
        validator.property("issuer").required().key("certification.issuerUsername");
        validator.property("validity").add(new PeriodValidation(ValidationType.BOTH_REQUIRED_AND_NOT_EXPIRED))
                .key("certification.validity");
        return validator;
    }

    private void initialize(final Certification certification) {
        final Status status = calculateInitialStatus(certification);
        certification.setStatus(status);
    }

    /**
     * Checks if the certification has some of the specified states
     */
    private boolean isInSomeStatus(final Certification certification, final Status... status) {
        for (final Status s : status) {
            if (certification.getStatus() == s) {
                return true;
            }
        }
        return false;
    }

    private void processCertifications(Calendar time, Certification.Status newStatus) {
        time = DateHelper.truncate(time);
        final Set<Relationship> fetch = new HashSet<Relationship>();
        fetch.add(Certification.Relationships.BUYER);
        fetch.add(Certification.Relationships.ISSUER);
        fetch.add(Certification.Relationships.LOGS);

        final CertificationQuery query = new CertificationQuery();
        query.setResultType(ResultType.ITERATOR);
        query.setFetch(fetch);

        if (newStatus == Certification.Status.ACTIVE) {
            query.setStartIn(Period.endingAt(time));
            query.setStatusList(Collections.singletonList(Certification.Status.SCHEDULED));
        } else {
            time.add(Calendar.DATE, -1); // this is to discard the certifications expiring today
            query.setEndIn(Period.endingAt(time));
            query.setStatusList(Arrays.asList(Certification.Status.ACTIVE, Certification.Status.SUSPENDED));

        }
        final List<Certification> certifications = search(query);

        for (final Certification certification : certifications) {
            if (newStatus == Certification.Status.ACTIVE) { // we must search for an already active certification
                final Certification alreadyActiveCertification = getActiveCertification(
                        certification.getGuaranteeType(), certification.getBuyer(), certification.getIssuer());
                if (alreadyActiveCertification != null) {
                    newStatus = Certification.Status.SUSPENDED;
                }
            }
            certification.setStatus(newStatus);
            final CertificationLog log = certification.changeStatus(newStatus, null);
            saveLog(log);
            save(certification, false);
            // Notify
            memberNotificationHandler.certificationStatusChangedNotification(certification);
        }
    }

    private Certification save(Certification certification, final boolean validate) {
        if (validate) {
            validate(certification);
        }
        if (certification.isTransient()) {
            initialize(certification);
            certification = certificationDao.insert(certification);
            final CertificationLog log = certification.getNewLog(certification.getStatus(),
                    LoggedUser.user().getElement());
            saveLog(log);
            // Notify
            memberNotificationHandler.certificationIssuedNotification(certification);
            return certification;
        } else {
            return certificationDao.update(certification);
        }
    }

    private CertificationLog saveLog(final CertificationLog certificationLog) {
        if (certificationLog.isTransient()) {
            return certificationLogDao.insert(certificationLog);
        } else {
            return certificationLogDao.update(certificationLog);
        }
    }

}