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

Java tutorial

Introduction

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

Source

/*
 This file is part of Cyclos.
    
 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.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.dao.accounts.guarantees.PaymentObligationDAO;
import nl.strohalm.cyclos.dao.accounts.guarantees.PaymentObligationLogDAO;
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.PaymentObligation;
import nl.strohalm.cyclos.entities.accounts.guarantees.PaymentObligationLog;
import nl.strohalm.cyclos.entities.accounts.guarantees.PaymentObligationQuery;
import nl.strohalm.cyclos.entities.accounts.guarantees.PaymentObligation.Status;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.services.accounts.CurrencyService;
import nl.strohalm.cyclos.services.accounts.guarantees.exceptions.PaymentObligationStatusChangeException;
import nl.strohalm.cyclos.services.fetch.FetchService;
import nl.strohalm.cyclos.services.permissions.PermissionService;
import nl.strohalm.cyclos.utils.DateHelper;
import nl.strohalm.cyclos.utils.Period;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.TimePeriod;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.access.PermissionRequestorImpl;
import nl.strohalm.cyclos.utils.query.QueryParameters.ResultType;
import nl.strohalm.cyclos.utils.validation.GeneralValidation;
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;

public class PaymentObligationServiceImpl implements PaymentObligationService {

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

        public ValidationError validate(final Object object) {
            final PaymentObligation paymentObligation = (PaymentObligation) object;
            final Member buyer = paymentObligation.getBuyer();
            Currency currency = paymentObligation.getCurrency();

            if (buyer == null || currency == null) {
                return null;
            } else if (certificationService.getCertificationIssuersForBuyer(buyer, currency).isEmpty()) {
                // we must load to get the entity (if not the name will be null)
                currency = currencyService.load(currency.getId());
                return new ValidationError("paymentObligation.error.certificationActiveNotExists",
                        currency.getName());
            } else {
                return null;
            }
        }
    }

    private class MaxPublicationDateBeforeExpirationDateValidation implements GeneralValidation {

        private static final long serialVersionUID = 1L;

        public ValidationError validate(final Object object) {
            final PaymentObligation paymentObligation = (PaymentObligation) object;
            final Calendar maxPublishDate = paymentObligation.getMaxPublishDate();
            final Calendar expirationDate = paymentObligation.getExpirationDate();
            if (maxPublishDate != null && expirationDate != null && maxPublishDate.after(expirationDate)) {
                return new ValidationError("paymentObligation.error.maxPublicationDateAfterExpirationDate");
            }
            return null;
        }
    }

    private PaymentObligationDAO paymentObligationDao;
    private PaymentObligationLogDAO paymentObligationLogDao;
    private GuaranteeService guaranteeService;
    private CertificationService certificationService;
    private CurrencyService currencyService;
    private PermissionService permissionService;
    private FetchService fetchService;

    public boolean canChangeStatus(final PaymentObligation paymentObligation, final Status newStatus) {
        boolean isBuyer;
        boolean isSeller;
        switch (newStatus) {
        case PUBLISHED:
            final Calendar today = DateHelper.truncate(Calendar.getInstance());
            final Calendar maxPublishDate = paymentObligation.getMaxPublishDate();
            isBuyer = guaranteeService.isBuyer() && paymentObligation.getBuyer().equals(LoggedUser.accountOwner());
            final boolean validDates = maxPublishDate.equals(today) || maxPublishDate.after(today);
            return isBuyer && validDates && isInSomeStatus(paymentObligation, Status.REGISTERED);
        case REGISTERED: // conceal
            isBuyer = guaranteeService.isBuyer() && paymentObligation.getBuyer().equals(LoggedUser.accountOwner());
            return isBuyer && isInSomeStatus(paymentObligation, Status.PUBLISHED);
        case ACCEPTED:
        case REJECTED:
            isSeller = guaranteeService.isSeller()
                    && paymentObligation.getSeller().equals(LoggedUser.accountOwner());
            return isSeller && isInSomeStatus(paymentObligation, Status.PUBLISHED);
        default:
            throw new PaymentObligationStatusChangeException(newStatus,
                    "Can't change payment obligation's status, unsupported target status: " + newStatus);
        }
    }

    public boolean canDelete(final PaymentObligation paymentObligation) {
        return isInSomeStatus(paymentObligation, Status.REGISTERED) && guaranteeService.isBuyer()
                && paymentObligation.getBuyer().equals(LoggedUser.accountOwner());
    }

    public void changeStatus(final Long paymentObligationId, final Status newStatus) {
        final PaymentObligation paymentObligation = load(paymentObligationId);

        final boolean changeAllowed = canChangeStatus(paymentObligation, newStatus);

        if (!changeAllowed) {
            throw new PaymentObligationStatusChangeException(newStatus);
        } else {
            final PaymentObligationLog log = paymentObligation.changeStatus(newStatus,
                    LoggedUser.user().getElement());
            saveLog(log);
            save(paymentObligation, false);
        }
    }

    public Long[] checkPaymentObligationPeriod(final PaymentObligationPackDTO dto) {
        if (dto.getPaymentObligations() == null || dto.getPaymentObligations().length == 0) {
            return new Long[0];
        }

        final Long[] paymentObligationIds = dto.getPaymentObligations();
        final List<PaymentObligation> paymentObligations = paymentObligationDao
                .loadOrderedByExpiration(paymentObligationIds);

        // take the first to get the currency and buyer (all belong to the same buyer and have the same currency)
        final PaymentObligation firstPaymentObligation = paymentObligations.get(0);

        final Certification certification = certificationService.getActiveCertification(
                firstPaymentObligation.getCurrency(), firstPaymentObligation.getBuyer(), dto.getIssuer());
        final TimePeriod paymentObligationPeriod = certification.getGuaranteeType().getPaymentObligationPeriod();

        final Calendar limit = Calendar.getInstance();
        limit.setTimeInMillis(firstPaymentObligation.getExpirationDate().getTimeInMillis());
        limit.add(paymentObligationPeriod.getField().getCalendarValue(), paymentObligationPeriod.getNumber());

        final List<Long> exceeded = new ArrayList<Long>();
        for (final PaymentObligation po : paymentObligations) {
            if (po.getExpirationDate().after(limit)) {
                exceeded.add(po.getId());
            }
        }

        return exceeded.toArray(new Long[exceeded.size()]);
    }

    public PaymentObligation.Status[] getSellerStatusToFilter() {
        final PaymentObligation.Status[] status = new PaymentObligation.Status[4];
        status[0] = PaymentObligation.Status.PUBLISHED;
        status[1] = PaymentObligation.Status.ACCEPTED;
        status[2] = PaymentObligation.Status.REJECTED;
        status[3] = PaymentObligation.Status.EXPIRED;

        return status;
    }

    public PaymentObligation.Status[] getStatusToFilter() {
        final boolean isSeller = guaranteeService.isSeller();
        final boolean isBuyer = guaranteeService.isBuyer();

        if (isSeller && !isBuyer) {
            return getSellerStatusToFilter();
        } else {
            return PaymentObligation.Status.values();
        }
    }

    public PaymentObligation load(final Long id, final Relationship... fetch) {
        PaymentObligation po = paymentObligationDao.load(id, fetch);

        if (LoggedUser.isMember() || LoggedUser.isOperator()) {
            // if the logged user can issue certifications to the payment obligation's buyer group
            // then is an issuer that can view the payment obligation
            // we must do this control in this way because it's not supported by the IPermissionRequestor
            final Member accountOwner = fetchService.fetch((Member) LoggedUser.accountOwner(),
                    RelationshipHelper.nested(Element.Relationships.GROUP,
                            MemberGroup.Relationships.CAN_ISSUE_CERTIFICATION_TO_GROUPS));
            po = fetchService.fetch(po,
                    RelationshipHelper.nested(PaymentObligation.Relationships.BUYER, Element.Relationships.GROUP));
            if (accountOwner.getMemberGroup().getCanIssueCertificationToGroups()
                    .contains(po.getBuyer().getMemberGroup())) {
                return po;
            }
        }

        final PermissionRequestorImpl permissionRequestor = new PermissionRequestorImpl();
        permissionRequestor.memberPermissions("memberGuarantees", "sellWithPaymentObligations",
                "buyWithPaymentObligations");
        permissionRequestor.operatorPermissions("operatorGuarantees", "sellWithPaymentObligations",
                "buyWithPaymentObligations");
        permissionRequestor.adminPermissions("adminMemberGuarantees", "viewPaymentObligations");
        permissionRequestor.manages(po.getSeller(), po.getBuyer());

        if (!permissionService.checkPermissions(permissionRequestor)) {
            throw new PermissionDeniedException();
        }
        return po;
    }

    public void processPaymentObligations(final Calendar taskTime) {
        final Calendar time = DateHelper.truncate(taskTime);

        final PaymentObligationQuery query = new PaymentObligationQuery();
        time.add(Calendar.DATE, -1); // this is to discard the POs expiring today
        query.setExpiration(Period.endingAt(time));
        query.setResultType(ResultType.ITERATOR);
        query.setApplyExpirationToMaxPublishDate(true);
        final Set<Relationship> fetch = new HashSet<Relationship>();
        fetch.add(PaymentObligation.Relationships.LOGS);
        query.setFetch(fetch);
        query.setStatusList(Arrays.asList(PaymentObligation.Status.PUBLISHED));
        final List<PaymentObligation> paymentObligations = paymentObligationDao.search(query);
        for (final PaymentObligation paymentObligation : paymentObligations) {
            final Calendar expirationDate = DateHelper.truncate(paymentObligation.getExpirationDate());
            // the expiration date or the max publication date are before task's time
            final PaymentObligation.Status newStatus = expirationDate.before(taskTime)
                    ? PaymentObligation.Status.EXPIRED
                    : PaymentObligation.Status.REGISTERED;

            paymentObligation.setStatus(newStatus);
            final PaymentObligationLog log = paymentObligation.changeStatus(newStatus, null);
            saveLog(log);
            save(paymentObligation, false);
        }
    }

    public void reject(final Long paymentObligationId) {
        changeStatus(paymentObligationId, PaymentObligation.Status.REJECTED);
    }

    public int remove(final Long paymentObligationId) {
        return paymentObligationDao.delete(paymentObligationId);
    }

    public PaymentObligation save(final PaymentObligation paymentObligation) {
        return save(paymentObligation, true);
    }

    public PaymentObligation save(PaymentObligation paymentObligation, final boolean validate) {
        if (validate) {
            validate(paymentObligation);
        }
        if (paymentObligation.isTransient()) {
            initialize(paymentObligation);
            paymentObligation = paymentObligationDao.insert(paymentObligation);
            final PaymentObligationLog paymentObligationLog = paymentObligation
                    .getNewLog(paymentObligation.getStatus(), LoggedUser.user().getElement());
            saveLog(paymentObligationLog);
            return paymentObligation;
        } else {
            return paymentObligationDao.update(paymentObligation);
        }
    }

    public PaymentObligationLog saveLog(final PaymentObligationLog paymentObligationLog) {
        if (paymentObligationLog.isTransient()) {
            return paymentObligationLogDao.insert(paymentObligationLog);
        } else {
            return paymentObligationLogDao.update(paymentObligationLog);
        }
    }

    @SuppressWarnings("unchecked")
    public List<PaymentObligation> search(PaymentObligationQuery queryParameters) {
        final boolean isSeller = guaranteeService.isSeller();
        final boolean isBuyer = guaranteeService.isBuyer();

        if (isBuyer && isSeller) {
            queryParameters.setLoggedMember((Member) LoggedUser.accountOwner());
        } else if (isBuyer) {
            queryParameters.setBuyer((Member) LoggedUser.accountOwner());
        } else if (isSeller) {
            queryParameters.setSeller((Member) LoggedUser.accountOwner());
            final List<PaymentObligation.Status> sellerStatusToFilter = Arrays.asList(getSellerStatusToFilter());
            if (sellerStatusToFilter.isEmpty()) {
                return Collections.EMPTY_LIST;
            } else if (CollectionUtils.isEmpty(queryParameters.getStatusList())) {
                queryParameters = queryParameters.clone();
                queryParameters.setStatusList(sellerStatusToFilter);
            } else { // check valid status selection
                for (final PaymentObligation.Status st : queryParameters.getStatusList()) {
                    if (!sellerStatusToFilter.contains(st)) {
                        throw new IllegalArgumentException(
                                "Payment Obligation status not allowed to filter: " + st);
                    }
                }
            }
        }
        return paymentObligationDao.search(queryParameters);
    }

    public void setCertificationService(final CertificationService certificationService) {
        this.certificationService = certificationService;
    }

    public void setCurrencyService(final CurrencyService currencyService) {
        this.currencyService = currencyService;
    }

    public void setFetchService(final FetchService fetchService) {
        this.fetchService = fetchService;
    }

    public void setGuaranteeService(final GuaranteeService guaranteeService) {
        this.guaranteeService = guaranteeService;
    }

    public void setPaymentObligationDao(final PaymentObligationDAO paymentObligationDao) {
        this.paymentObligationDao = paymentObligationDao;
    }

    public void setPaymentObligationLogDao(final PaymentObligationLogDAO paymentObligationLogDao) {
        this.paymentObligationLogDao = paymentObligationLogDao;
    }

    public void setPermissionService(final PermissionService permissionService) {
        this.permissionService = permissionService;
    }

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

    private Validator getValidator() {
        final Validator validator = new Validator("paymentObligation");
        // the status is not validated because it is set in at insert
        validator.property("buyer").required().key("paymentObligation.buyerUsername");
        validator.property("seller").required().key("paymentObligation.sellerUsername");
        validator.property("expirationDate").required().futureOrToday();
        validator.property("maxPublishDate").required().futureOrToday();
        validator.property("amount").required().positiveNonZero();
        validator.property("currency").required();
        validator.property("description").required();
        validator.general(new MaxPublicationDateBeforeExpirationDateValidation());
        return validator;
    }

    private void initialize(final PaymentObligation paymentObligation) {
        paymentObligation.setStatus(PaymentObligation.Status.REGISTERED);
        paymentObligation.setRegistrationDate(Calendar.getInstance());
    }

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