/* 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.dao.members; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import nl.strohalm.cyclos.dao.BaseDAOImpl; import nl.strohalm.cyclos.entities.Relationship; import nl.strohalm.cyclos.entities.accounts.transactions.PaymentAwaitingFeedbackDTO; import nl.strohalm.cyclos.entities.accounts.transactions.TransferType; import nl.strohalm.cyclos.entities.groups.MemberGroup; import nl.strohalm.cyclos.entities.members.GeneralReference; import nl.strohalm.cyclos.entities.members.Member; import nl.strohalm.cyclos.entities.members.PaymentsAwaitingFeedbackQuery; import nl.strohalm.cyclos.entities.members.Reference; import nl.strohalm.cyclos.entities.members.Reference.Level; import nl.strohalm.cyclos.entities.members.Reference.Nature; import nl.strohalm.cyclos.entities.members.ReferenceQuery; import nl.strohalm.cyclos.entities.members.TransactionFeedback; import nl.strohalm.cyclos.utils.DataIteratorHelper; import nl.strohalm.cyclos.utils.IteratorListImpl; import nl.strohalm.cyclos.utils.Period; import nl.strohalm.cyclos.utils.ScrollableResultsIterator; import nl.strohalm.cyclos.utils.conversion.Transformer; import nl.strohalm.cyclos.utils.hibernate.HibernateHelper; import nl.strohalm.cyclos.utils.query.PageHelper; import nl.strohalm.cyclos.utils.query.PageImpl; import nl.strohalm.cyclos.utils.query.PageParameters; import nl.strohalm.cyclos.utils.query.QueryParameters.ResultType; import org.apache.commons.collections.CollectionUtils; import org.hibernate.SQLQuery; import org.hibernate.type.StandardBasicTypes; /** * Implementation class for reference DAO * @author rafael */ public class ReferenceDAOImpl extends BaseDAOImpl<Reference> implements ReferenceDAO { public ReferenceDAOImpl() { super(Reference.class); } @Override public Map<Level, Integer> countGivenReferencesByLevel(final Reference.Nature nature, final Collection<MemberGroup> memberGroups) { return countReferencesByLevel(nature, null, null, memberGroups, false); } @Override public Map<Level, Integer> countReferencesByLevel(final Reference.Nature nature, final Period period, final Member member, final boolean received) { return countReferencesByLevel(nature, period, member, null, received); } public Map<Level, Integer> countReferencesByLevel(final Reference.Nature nature, final Period period, final Member member, final Collection<MemberGroup> memberGroups, final boolean received) { final Map<Level, Integer> countGivenReferences = new EnumMap<Level, Integer>(Level.class); for (final Level level : Level.values()) { countGivenReferences.put(level, 0); } final Map<String, Object> namedParameters = new HashMap<String, Object>(); final Class<? extends Reference> type = typeForNature(nature); final StringBuilder hql = new StringBuilder("select r.level, count(r.id) from ").append(type.getName()).append(" r where 1=1 "); HibernateHelper.addParameterToQuery(hql, namedParameters, (received ? "r.to" : "r.from"), member); if (memberGroups != null && !memberGroups.isEmpty()) { hql.append(" and " + (received ? "r.to" : "r.from") + ".group in (:memberGroups) "); namedParameters.put("memberGroups", memberGroups); } HibernateHelper.addPeriodParameterToQuery(hql, namedParameters, "r.date", period); hql.append(" group by r.level order by r.level"); final List<Object[]> rows = list(hql.toString(), namedParameters); for (final Object[] row : rows) { countGivenReferences.put((Level) row[0], (Integer) row[1]); } return countGivenReferences; } @Override public List<? extends Reference> search(final ReferenceQuery query) { final Map<String, Object> namedParameters = new HashMap<String, Object>(); final Set<Relationship> fetch = query.getFetch(); final Nature nature = query.getNature(); final Class<? extends Reference> type = typeForNature(nature); final StringBuilder hql = HibernateHelper.getInitialQuery(type, "r", fetch); HibernateHelper.addParameterToQuery(hql, namedParameters, "r.from", query.getFrom()); HibernateHelper.addParameterToQuery(hql, namedParameters, "r.to", query.getTo()); HibernateHelper.addPeriodParameterToQuery(hql, namedParameters, "r.date", query.getPeriod()); if (nature == Nature.TRANSACTION) { HibernateHelper.addParameterToQuery(hql, namedParameters, "r.transfer", query.getTransfer()); HibernateHelper.addParameterToQuery(hql, namedParameters, "r.scheduledPayment", query.getScheduledPayment()); } if (query.getGroups() != null) { hql.append(" and (r.from.group in (:groups) or r.to.group in (:groups)) "); namedParameters.put("groups", query.getGroups()); } HibernateHelper.appendOrder(hql, "r.id desc"); return list(query, hql.toString(), namedParameters); } @Override public List<PaymentAwaitingFeedbackDTO> searchPaymentsAwaitingFeedback(final PaymentsAwaitingFeedbackQuery query) { final ResultType resultType = query.getResultType(); final PageParameters pageParameters = query.getPageParameters(); final boolean countOnly = resultType == ResultType.PAGE && pageParameters != null && pageParameters.getMaxResults() == 0; // There are 2 tables which contains payments that can have feedback: transfers and scheduled payments // As we need an union, we need a native SQL final Member member = query.getMember(); Boolean expired = query.getExpired(); final StringBuilder sql = new StringBuilder(); sql.append(" select "); if (countOnly) { sql.append(" count(*) as row_count"); } else { sql.append(" * "); } sql.append(" from ( "); { sql.append(" select t.id, t.type_id as transferTypeId, false as scheduled, t.date, t.amount, tm.id as memberId, tm.name as memberName, ta.owner_name as memberUsername"); sql.append(" from transfers t inner join transfer_types tt on t.type_id = tt.id inner join accounts ta on t.to_account_id = ta.id inner join members tm on ta.member_id = tm.id"); if (member != null) { sql.append(" inner join accounts a on t.from_account_id = a.id"); } sql.append(" left join refs tf on tf.transfer_id = t.id"); sql.append(" where tt.requires_feedback = true"); sql.append(" and t.date >= tt.feedback_enabled_since"); sql.append(" and t.parent_id is null"); sql.append(" and t.chargeback_of_id is null"); sql.append(" and t.scheduled_payment_id is null"); sql.append(" and t.process_date is not null"); if (expired != null) { sql.append(" and t.feedback_deadline " + (expired ? "<" : ">=") + " now()"); } sql.append(" and tf.id is null"); if (member != null) { sql.append(" and a.member_id = :memberId"); } sql.append(" union "); sql.append(" select sp.id, sp.type_id, true, sp.date, sp.amount, tm.id, tm.name, ta.owner_name"); sql.append(" from scheduled_payments sp inner join transfer_types tt on sp.type_id = tt.id inner join accounts ta on sp.to_account_id = ta.id inner join members tm on ta.member_id = tm.id"); if (member != null) { sql.append(" inner join accounts a on sp.from_account_id = a.id"); } sql.append(" left join refs tf on tf.scheduled_payment_id = sp.id"); sql.append(" where tt.requires_feedback = true"); if (expired != null) { sql.append(" and sp.feedback_deadline " + (expired ? "<" : ">=") + " now()"); } sql.append(" and sp.date >= tt.feedback_enabled_since"); sql.append(" and tf.id is null"); if (member != null) { sql.append(" and a.member_id = :memberId"); } } sql.append(") as awaiting "); if (!countOnly) { sql.append("order by date"); } SQLQuery sqlQuery = getSession().createSQLQuery(sql.toString()); if (member != null) { sqlQuery.setLong("memberId", member.getId()); } if (countOnly) { // Handle the special case for count only sqlQuery.addScalar("row_count", StandardBasicTypes.INTEGER); int count = ((Number) sqlQuery.uniqueResult()).intValue(); return new PageImpl<PaymentAwaitingFeedbackDTO>(pageParameters, count, Collections.<PaymentAwaitingFeedbackDTO> emptyList()); } else { // Execute the search sqlQuery.addScalar("id", StandardBasicTypes.LONG); sqlQuery.addScalar("transferTypeId", StandardBasicTypes.LONG); sqlQuery.addScalar("scheduled", StandardBasicTypes.BOOLEAN); sqlQuery.addScalar("date", StandardBasicTypes.CALENDAR); sqlQuery.addScalar("amount", StandardBasicTypes.BIG_DECIMAL); sqlQuery.addScalar("memberId", StandardBasicTypes.LONG); sqlQuery.addScalar("memberName", StandardBasicTypes.STRING); sqlQuery.addScalar("memberUsername", StandardBasicTypes.STRING); getHibernateQueryHandler().applyPageParameters(pageParameters, sqlQuery); // We'll always use an iterator, even if it is for later adding it to a list Iterator<PaymentAwaitingFeedbackDTO> iterator = new ScrollableResultsIterator<PaymentAwaitingFeedbackDTO>(sqlQuery, new Transformer<Object[], PaymentAwaitingFeedbackDTO>() { @Override public PaymentAwaitingFeedbackDTO transform(final Object[] input) { PaymentAwaitingFeedbackDTO dto = new PaymentAwaitingFeedbackDTO(); dto.setId((Long) input[0]); dto.setTransferTypeId((Long) input[1]); dto.setScheduled(Boolean.TRUE.equals(input[2])); dto.setDate((Calendar) input[3]); dto.setAmount((BigDecimal) input[4]); dto.setMemberId((Long) input[5]); dto.setMemberName((String) input[6]); dto.setMemberUsername((String) input[7]); TransferType transferType = (TransferType) getSession().load(TransferType.class, dto.getTransferTypeId()); dto.setCurrency(getFetchDao().fetch(transferType.getCurrency())); return dto; } }); if (resultType == ResultType.ITERATOR) { return new IteratorListImpl<PaymentAwaitingFeedbackDTO>(iterator); } else { List<PaymentAwaitingFeedbackDTO> list = new ArrayList<PaymentAwaitingFeedbackDTO>(); CollectionUtils.addAll(list, iterator); DataIteratorHelper.close(iterator); if (resultType == ResultType.PAGE) { // For page, we need another search for the total count query.setPageForCount(); int totalCount = PageHelper.getTotalCount(searchPaymentsAwaitingFeedback(query)); return new PageImpl<PaymentAwaitingFeedbackDTO>(pageParameters, totalCount, list); } else { return list; } } } } private Class<? extends Reference> typeForNature(final Nature nature) { Class<? extends Reference> type; if (nature == Reference.Nature.TRANSACTION) { type = TransactionFeedback.class; } else if (nature == Reference.Nature.GENERAL) { type = GeneralReference.class; } else { type = Reference.class; } return type; } }