//
// Copyright (c) 2011 Linkeos.
//
// This file is part of Elveos.org.
// Elveos.org 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 3 of the License, or (at your
// option) any later version.
//
// Elveos.org 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 Elveos.org. If not, see http://www.gnu.org/licenses/.
//
package com.bloatit.data;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import org.hibernate.HibernateException;
import org.hibernate.annotations.NamedQueries;
import org.hibernate.annotations.NamedQuery;
import com.bloatit.common.Log;
import com.bloatit.data.exceptions.NotEnoughMoneyException;
import com.bloatit.framework.exceptions.lowlevel.NonOptionalParameterException;
/**
* A DaoBankTransaction represent a transaction with a real bank. It keep some
* informations on the transaction.
*/
@Entity
//@formatter:off
@NamedQueries(value = {
@NamedQuery(
name = "bankTransaction.byToken",
query = "from DaoBankTransaction where token = :token"),
@NamedQuery(
name = "bankTransaction.sumByDates",
query = "SELECT count(*), coalesce(sum(value), 0), coalesce(sum(valuePaid), 0) " +
"FROM DaoBankTransaction " +
"WHERE modificationDate > :from " +
"AND modificationDate <= :to " +
"AND state = :state "),
}
)
// @formatter:on
public class DaoBankTransaction extends DaoIdentifiable {
/**
* Enumerate the different state a BankTranscation can be in. After being
* <code>AUTHORIZED</code> a transaction must be <code>AUTHORIZED</code>.
*/
public enum State {
/**
* A <code>PENDING</code> BankTransaction is in an not known yet state.
* We are waiting for more informations to know in which state it will
* be.
*/
PENDING,
/**
* A transaction is <code>AUTHORIZED</code> when you give the right card
* code and the bank says that you can do the transaction.
*/
AUTHORIZED,
/**
* A refused transaction is a transaction that went wrong (error in the
* CB code, bug, not money in the account.
*/
REFUSED,
/**
* When the transaction is actually performed it pass in
* <code>VALIDATE</code> state.
*/
VALIDATED
}
private static final int DEFAULT_STRING_LENGTH = 64;
/**
* When doing automatic transaction with a bank, we can received a message
* (mostly error messages). Use this property to store them.
*/
@Column(columnDefinition = "TEXT", nullable = true)
private String message;
/**
* A token is used to identify a BankTransaction. It is unique for each
* DaoBankTransaction. Most of the time the token is created by the Bank,
* and the bank will ask for it during the process, to identify the
* different requests. ( {@value #DEFAULT_STRING_LENGTH} length)
*/
@Basic(optional = true)
@Column(unique = true, updatable = false, length = DEFAULT_STRING_LENGTH)
private String token;
/**
* This field is a spare data field. You can store whatever you want in it (
* {@value #DEFAULT_STRING_LENGTH} length)
*/
@Basic(optional = true)
@Column(updatable = true, length = DEFAULT_STRING_LENGTH)
private String processInformations;
@ManyToOne(optional = false, fetch = FetchType.LAZY)
private DaoActor author;
@Basic(optional = false)
@Column(updatable = false)
private Date creationDate;
@Basic(optional = false)
private Date modificationDate;
@Column(nullable = false, updatable = false)
private BigDecimal value;
@Column(nullable = false, updatable = false)
private BigDecimal valuePaid;
@Enumerated
@Column(nullable = false)
private State state;
/**
* This is the order reference. It must be unique and it is often used by
* the bank to make sure an order is not taken twice.
*/
@Column(nullable = false, updatable = false, unique = true)
private String reference;
@OneToOne(optional = true)
private DaoInvoice invoice;
// ======================================================================
// Static HQL queries.
// ======================================================================
/**
* Gets a bank transaction by token.
*
* @param token the token we are looking for
* @return the <code>DaoBankTransaction</code> with this <code>token</code>.
* Return null if not found.
*/
public static DaoBankTransaction getByToken(final String token) {
return (DaoBankTransaction) SessionManager.getNamedQuery("bankTransaction.byToken").setString("token", token).uniqueResult();
}
public static class DaoBankTransactionSum {
public final Long count;
public final BigDecimal chargedValueSum;
public final BigDecimal paidValueSum;
public DaoBankTransactionSum(final Long count, final BigDecimal chargedValueSum, final BigDecimal paidValueSum) {
super();
this.count = count;
this.chargedValueSum = chargedValueSum;
this.paidValueSum = paidValueSum;
}
}
public static DaoBankTransactionSum getBankTransactionSum(final Date from, final Date to) {
final Object[] result = (Object[]) SessionManager.getNamedQuery("bankTransaction.sumByDates")
.setDate("from", from)
.setDate("to", to)
.setParameter("state", State.VALIDATED)
.uniqueResult();
return new DaoBankTransactionSum((Long) result[0], (BigDecimal) result[1], (BigDecimal) result[2]);
}
public static Long getCount(final Date from, final Date to) {
return (Long) SessionManager.getNamedQuery("bankTransaction.countByDates")
.setDate("from", from)
.setDate("to", to)
.setParameter("state", State.VALIDATED)
.uniqueResult();
}
// ======================================================================
// Construction
// ======================================================================
/**
* Creates a bank transaction and persist it.
*
* @param message the message given by the bank during this transaction
* @param token the token to identify this bank transaction
* @param author the author of the transaction
* @param value the value the user will have on its internal account
* @param valuePayed the value payed by the user
* @param orderReference the order reference a unique reference of the order
* @return the dao bank transaction
*/
public static DaoBankTransaction createAndPersist(final String message,
final String token,
final DaoActor author,
final BigDecimal value,
final BigDecimal valuePayed,
final String orderReference) {
final DaoBankTransaction bankTransaction = new DaoBankTransaction(message, token, author, value, valuePayed, orderReference);
try {
SessionManager.getSessionFactory().getCurrentSession().save(bankTransaction);
} catch (final HibernateException e) {
SessionManager.getSessionFactory().getCurrentSession().getTransaction().rollback();
throw e;
}
return bankTransaction;
}
/**
* Creates a bank transaction and persist it.
*
* @param author the author of the transaction
* @param value the value the user will have on its internal account
* @param valuePayed the value payed by the user
* @param orderReference the order reference a unique reference of the order
* @return the dao bank transaction
*/
public static DaoBankTransaction createAndPersist(final DaoActor author,
final BigDecimal value,
final BigDecimal valuePayed,
final String orderReference) {
final DaoBankTransaction bankTransaction = new DaoBankTransaction(author, value, valuePayed, orderReference);
try {
SessionManager.getSessionFactory().getCurrentSession().save(bankTransaction);
} catch (final HibernateException e) {
SessionManager.getSessionFactory().getCurrentSession().getTransaction().rollback();
throw e;
}
return bankTransaction;
}
/**
* throw a {@link NonOptionalParameterException} if any of the parameters is
* null (or string isEmpty).
*/
private DaoBankTransaction(final String message,
final String token,
final DaoActor author,
final BigDecimal value,
final BigDecimal valuePayed,
final String orderReference) {
super();
checkOptionnal(message, token, author, value, valuePayed, orderReference);
this.message = message;
this.token = token;
this.author = author;
this.value = value;
this.valuePaid = valuePayed;
this.state = State.PENDING;
this.reference = orderReference;
this.creationDate = new Date();
this.modificationDate = (Date) this.creationDate.clone();
}
/**
* throw a {@link NonOptionalParameterException} if any of the parameters is
* null (or string isEmpty).
*/
private DaoBankTransaction(final DaoActor author, final BigDecimal value, final BigDecimal valuePayed, final String orderReference) {
super();
checkOptionnal(author, value, valuePayed, orderReference);
this.author = author;
this.value = value;
this.valuePaid = valuePayed;
this.state = State.PENDING;
this.reference = orderReference;
this.creationDate = new Date();
this.modificationDate = (Date) this.creationDate.clone();
}
/**
* Set the state to {@link State#AUTHORIZED} if the current state is.
* {@link State#PENDING}. Reset the modification date.
*/
public void setAuthorized() {
if (this.state == State.PENDING) {
this.modificationDate = new Date();
this.state = State.AUTHORIZED;
}
}
/**
* Set the state to validated and create a {@link DaoTransaction} from the
* external to the internal account.
*
* @return true if performed, false otherwise.
*/
public boolean setValidated() {
if (this.state != State.AUTHORIZED) {
Log.data().fatal("Cannot VALIDATE bankTransaction: " + getId() + "; state should be AUTHORIZED but was: " + state);
return false;
}
this.modificationDate = new Date();
try {
final DaoTransaction transaction = DaoTransaction.createAndPersist(this.author.getInternalAccount(),
this.author.getExternalAccount(),
getValue().negate());
this.state = State.VALIDATED;
Log.data().info("Banktransaction: " + getId() + " VALIDATED. transaction_id: " + transaction.getId());
return true;
} catch (final NotEnoughMoneyException e) {
Log.data().fatal("Error when trying to validate a bankTransaction.", e);
return false;
}
}
/**
* Set the state to {@link State#REFUSED}.
*/
public void setRefused() {
this.modificationDate = new Date();
this.state = State.REFUSED;
}
/**
* Sets the this field is a spare data field.
*
* @param processInformations the new this field is a spare data field
*/
public void setProcessInformations(final String processInformations) {
this.modificationDate = new Date();
this.processInformations = processInformations;
}
/**
* Sets the invoice.
*
* @param invoice
*/
public void setInvoice(final DaoInvoice invoice) {
this.modificationDate = new Date();
this.invoice = invoice;
}
// ======================================================================
// Getters
// ======================================================================
/**
* When doing automatic transaction with a bank, we can received a message
* (mostly error messages).
*
* @return the when doing automatic transaction with a bank, we can received
* a message (mostly error messages)
*/
public String getMessage() {
return this.message;
}
/**
* A token is used to identify a BankTransaction.
*
* @return the a token is used to identify a BankTransaction
*/
public String getToken() {
return this.token;
}
/**
* Gets the author.
*
* @return the author
*/
public DaoActor getAuthor() {
return this.author;
}
/**
* Gets the value paid.
*
* @return the value paid
*/
public BigDecimal getValuePaid() {
return this.valuePaid;
}
/**
* Gets the value.
*
* @return the value
*/
public BigDecimal getValue() {
return this.value;
}
/**
* Gets the state.
*
* @return the state
*/
public State getState() {
return this.state;
}
/**
* Gets the invoice.
*
* @return the state
*/
public DaoInvoice getInvoice() {
return this.invoice;
}
/**
* Gets the creation date.
*
* @return a clone of the creationDate
*/
public Date getCreationDate() {
return (Date) this.creationDate.clone();
}
/**
* Gets the modification date.
*
* @return a clone of the creationDate
*/
public Date getModificationDate() {
return (Date) this.modificationDate.clone();
}
/**
* Gets the this is the order reference.
*
* @return the this is the order reference
*/
public String getReference() {
return this.reference;
}
/**
* Gets the this field is a spare data field.
*
* @return the this field is a spare data field
*/
public String getProcessInformations() {
return this.processInformations;
}
// ======================================================================
// Visitor.
// ======================================================================
/*
* (non-Javadoc)
* @see
* com.bloatit.data.DaoIdentifiable#accept(com.bloatit.data.DataClassVisitor
* )
*/
@Override
public <ReturnType> ReturnType accept(final DataClassVisitor<ReturnType> visitor) {
return visitor.visit(this);
}
// ======================================================================
// For hibernate mapping.
// ======================================================================
/**
* Instantiates a new dao bank transaction.
*/
protected DaoBankTransaction() {
super();
}
// ======================================================================
// equals and hashCode.
// ======================================================================
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.author == null) ? 0 : this.author.hashCode());
result = prime * result + ((this.creationDate == null) ? 0 : this.creationDate.hashCode());
result = prime * result + ((this.token == null) ? 0 : this.token.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DaoBankTransaction)) {
return false;
}
final DaoBankTransaction other = (DaoBankTransaction) obj;
if (this.author == null) {
if (other.author != null) {
return false;
}
} else if (!this.author.equals(other.author)) {
return false;
}
if (this.creationDate == null) {
if (other.creationDate != null) {
return false;
}
} else if (!this.creationDate.equals(other.creationDate)) {
return false;
}
if (this.token == null) {
if (other.token != null) {
return false;
}
} else if (!this.token.equals(other.token)) {
return false;
}
return true;
}
}