/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed 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 (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.billing.ejb.entity.beans;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.patient.utils.PatientNameFormatter;

//***************************************************************************
//* Class Definition and Members                                            *
//***************************************************************************

/**
 * The Transaction Bean is a composite view of ledger data, containing also
 * data about the patient and the invoice at the origin of this transaction.
 * Transaction Beans are mainly intended for statistics or listings. 
 * @author nico.mack@tudor.lu
 * @since 07/08/20
 */

@Entity
@Table(name = "transaction", schema = "billing")

@javax.persistence.NamedQueries 
	({
	@NamedQuery(name = Transaction.c_TransactionsForOffice, 			query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) ORDER BY o.settlementId, o.settlementDate, o.transactionDate"),
		
	@NamedQuery(name = Transaction.c_TransactionsForOfficeByCashier, 	query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.cashierId = :cashierId ORDER BY o.settlementId, o.settlementDate, o.transactionDate"),
	
	@NamedQuery(name = Transaction.c_TransactionsForPhysician, 			query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.physicianId = :physicianId ORDER BY o.settlementId, o.settlementDate, o.transactionDate"),
	
	@NamedQuery(name = Transaction.c_TransactionsForPhysicianByCashier, query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.physicianId = :physicianId AND o.cashierId = :cashierId ORDER BY o.settlementId, o.settlementDate, o.transactionDate")

//	@NamedQuery(name = Transaction.c_EffectiveTransactionsForOffice,    		 query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.id IN " +
//															  							 "(SELECT MAX(t1.id) FROM Transaction t1 WHERE t1.settlementDate = o.settlementDate AND t1.credit = o.debit AND t1.debit = o.credit AND t1.invoiceId = o.invoiceId AND t1.settlementId = o.settlementId group by t1.invoiceId,t1.settlementId,t1.settlementDate  having (count(t1.id) > (COUNT(t1.id) - MOD (COUNT(t1.id),2)))) " +
//																						 "ORDER BY o.settlementId, o.settlementDate"),
//
//	@NamedQuery(name = Transaction.c_EffectiveTransactionsForOfficeByCashier,    query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.cashierId = :cashierId AND o.id IN " +
//				 																		 "(SELECT MAX(t1.id) FROM Transaction t1 WHERE (t1.settlementDate BETWEEN :fromDate AND :untilDate) AND t1.invoiceId IS NOT NULL AND t1.settlementId IN (:settlementIds) AND t1.cashierId = :cashierId group by t1.invoiceId,t1.settlementId,t1.settlementDate,t1.cashierId  having (count(t1.id) > (COUNT(t1.id) - MOD (COUNT(t1.id),2)))) " +
//																						 "ORDER BY o.settlementId, o.settlementDate"),
//																				
//	@NamedQuery(name = Transaction.c_EffectiveTransactionsForPhysician, 		 query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.physicianId = :physicianId AND o.id IN " +
//			 																			 "(SELECT MAX(t1.id) FROM Transaction t1 WHERE (t1.settlementDate BETWEEN :fromDate AND :untilDate) AND t1.invoiceId IS NOT NULL AND t1.settlementId IN (:settlementIds) AND t1.physicianId = :physicianId group by t1.invoiceId,t1.settlementId,t1.settlementDate,t1.physicianId  having (count(t1.id) > (COUNT(t1.id) - MOD (COUNT(t1.id),2)))) " +
//															  							 "ORDER BY o.settlementId, o.settlementDate"),
//	
//	@NamedQuery(name = Transaction.c_EffectiveTransactionsForPhysicianByCashier, query = "SELECT OBJECT(o) FROM Transaction o WHERE (o.settlementDate BETWEEN :fromDate AND :untilDate) AND o.invoiceId IS NOT NULL AND o.settlementId IN (:settlementIds) AND o.physicianId = :physicianId AND o.cashierId = :cashierId AND o.id IN " +
//			 																			 "(SELECT MAX(t1.id) FROM Transaction t1 WHERE (t1.settlementDate BETWEEN :fromDate AND :untilDate) AND t1.invoiceId IS NOT NULL AND t1.settlementId IN (:settlementIds) AND t1.physicianId = :physicianId AND t1.cashierId = :cashierId group by t1.invoiceId,t1.settlementId,t1.settlementDate,t1.physicianId,t1.cashierId  having (count(t1.id) > (COUNT(t1.id) - MOD (COUNT(t1.id),2)))) " +
//																	  					 "ORDER BY o.settlementId, o.settlementDate")	
	})

public class Transaction extends GECAMedEntityBean implements Serializable
	{
	private static final long serialVersionUID = 1L;

	private Integer					m_InvoiceId;
	private Integer					m_PhysicianId;
	private Integer					m_PatientId;
	private String  				m_PatientFirstName;
	private String					m_PatientMaidenName;
	private String  				m_PatientName;
	private String					m_PatientSSN;
	private Date 					m_InvoiceDate;
	private Integer					m_SettlementId;
	private Date					m_SettlementDate;
	private Date					m_TransactionDate;
	private Integer					m_CashierId;
	private Double					m_Debit;
	private Double					m_Credit;

	private static PatientNameFormatter m_Formatter = new PatientNameFormatter ();
	
//***************************************************************************
//* Named Queries                                                           *
//***************************************************************************

//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************
	
	public static final String c_TransactionsForOffice    			= "getAllTransactionsForOffice";
	public static final String c_TransactionsForOfficeByCashier    	= "getTransactionsForOfficeByCashier";
	public static final String c_TransactionsForPhysician 			= "getAllTransactionsForPhysician";
	public static final String c_TransactionsForPhysicianByCashier 	= "getTransactionsForPhysicianByCashier";
	
	public static final String c_EffectiveTransactionsForOffice    			= "getAllEffectiveTransactionsForOffice";
	public static final String c_EffectiveTransactionsForOfficeByCashier    = "getEffectiveTransactionsForOfficeByCashier";
	public static final String c_EffectiveTransactionsForPhysician 			= "getAllEffectiveTransactionsForPhysician";
	public static final String c_EffectiveTransactionsForPhysicianByCashier = "getEffectiveTransactionsForPhysicianByCashier";
	
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************	

public Transaction ()
	{
	m_Debit						= Double.valueOf (0);
	m_Credit					= Double.valueOf (0);
	}	

//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives                                       						*
//***************************************************************************
//---------------------------------------------------------------------------

@Transient
public static void setMaidenNameIsMarriedName (Boolean p_MaidenNameIsMarriedName)
	{
	m_Formatter.setMaidenNameIsMarriedName(p_MaidenNameIsMarriedName);
	}

//---------------------------------------------------------------------------
/**
 * returns the full name of the patient.
 * @return full name of patient.
 */
//---------------------------------------------------------------------------

@Transient
public String getPatientFullName() 
	{
	return m_Formatter.getPatientFullName (m_PatientFirstName, m_PatientMaidenName, m_PatientName);
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The canceledBy method checks whether the specified transaction cancels
 * this one. A Transaction is said to cancel another one if all of the following
 * conditions are met:
 * 1.) They must refer to the same invoice
 * 2.) They must specify the same settlement method
 * 3.) Settlement date must be the same
 * 4.) Debited and Credited amount must match
 * @param p_Candidate specifies the transaction to check this transaction against
 * @retun return <code>true</code> if the above mentioned conditions are
 * met by the specified transaction, <code>false</code> otherwise.
 */
//---------------------------------------------------------------------------

@Transient
public boolean canceledBy (Transaction p_Candidate)
	{
	boolean l_Cancels = true;
	
	if (p_Candidate == null) return false;
	
	l_Cancels &= (p_Candidate.getInvoiceId() != null) && (p_Candidate.getInvoiceId().equals (this.getInvoiceId()));
	
	if (!l_Cancels) return false;
	
	l_Cancels &= (p_Candidate.getSettlementId() != null) && (p_Candidate.getSettlementId().equals (this.getSettlementId()));
	
	if (!l_Cancels) return false;

	l_Cancels &= (p_Candidate.getSettlementDate() != null) && (p_Candidate.getSettlementDate().equals (this.getSettlementDate()));
	
	if (!l_Cancels) return false;

	l_Cancels &= (p_Candidate.getDebit() != null) && (p_Candidate.getDebit().equals (this.getCredit()));
	
	if (!l_Cancels) return false;
	
	l_Cancels &= (p_Candidate.getCredit() != null) && (p_Candidate.getCredit().equals (this.getDebit()));
	
	return l_Cancels;	
	}

//---------------------------------------------------------------------------
/**
 * Returns the ID of the invoice this transaction applies to.
 * @return The ID of the invoice this transaction applies to.
 */
//---------------------------------------------------------------------------

@Column(name = "invoice_id", insertable =false, updatable = false)

public Integer getInvoiceId() 
	{
	return m_InvoiceId;
	}

//---------------------------------------------------------------------------
/**
 * Associates the specified invoice Id with this transaction
 * @param p_InvoiceId specifies the invoice to be associated with this
 * transaction
 */
//---------------------------------------------------------------------------

public void setInvoiceId (Integer p_InvoiceId) 
	{
	m_InvoiceId = p_InvoiceId;
	}

//---------------------------------------------------------------------------
/**
 * Returns the ID of the physician this transaction applies to.
 * @return The ID of the physician this transaction applies to.
 */
//---------------------------------------------------------------------------

@Column(name = "physician_id", insertable =false, updatable = false)

public Integer getPhysicianId() 
	{
	return m_PhysicianId;
	}

//---------------------------------------------------------------------------
/**
 * Associates the specified physician with this transaction
 * @param p_PhysicianId specifies the physician to be associated with this
 * transaction
 */
//---------------------------------------------------------------------------

public void setPhysicianId (Integer p_PhysicianId) 
	{
	m_PhysicianId = p_PhysicianId;
	}

//---------------------------------------------------------------------------
/**
 * Returns the ID of the patient associated with this transaction.
 * @return The ID of the patient associated with this transaction.
 */
//---------------------------------------------------------------------------

@Column(name = "patient_id", insertable =false, updatable = false)

public Integer getPatientId() 
	{
	return m_PatientId;
	}

//---------------------------------------------------------------------------
/**
 * Associates the specified patient with this transaction
 * @param p_PatientId specifies the ID of the patient to be associated with this
 * transaction
 */
//---------------------------------------------------------------------------

public void setPatientId (Integer p_PatientId) 
	{
	m_PatientId = p_PatientId;
	}

//---------------------------------------------------------------------------
/**
 * Returns the first name of the patient this transaction was performed for
 * @return first name of patient this transaction was performed for
 */
//---------------------------------------------------------------------------

@Column(name = "patient_first_name", insertable =false, updatable = false)

public String getPatientFirstName ()
	{
	return m_PatientFirstName;
	}

//---------------------------------------------------------------------------
/**
 * Sets the first name of the patient this transaction was performed for
 * @param p_PatientFirstName specifies the first name of the patient
 */
//---------------------------------------------------------------------------

public void setPatientFirstName (String p_PatientFirstName)
	{
	m_PatientFirstName = p_PatientFirstName;
	}

//---------------------------------------------------------------------------
/**
 * Returns the maiden/married name of the patient associated with this 
 * transaction 
 * @return maiden/married name of patient this transaction is associated with
 */
//---------------------------------------------------------------------------

@Column(name = "patient_maiden_name", insertable =false, updatable = false)

public String getPatientMaidenName ()
	{
	return m_PatientMaidenName;
	}

//---------------------------------------------------------------------------
/**
 * Sets the maiden/married name of the patient associated with this transaction
 * @param p_PatientMaidenName specifies the maiden/married name of the patient 
 * this transaction is associated with
 */
//---------------------------------------------------------------------------

public void setPatientMaidenName (String p_PatientMaidenName)
	{
	m_PatientMaidenName = p_PatientMaidenName;
	}

//---------------------------------------------------------------------------
/**
 * Returns the name of the patient this transaction was performed for
 * @return name of patient this transaction was performed for
 */
//---------------------------------------------------------------------------

@Column(name = "patient_name", insertable =false, updatable = false)

public String getPatientName ()
	{
	return m_PatientName;
	}

//---------------------------------------------------------------------------
/**
 * Sets the name of the patient this transaction was performed for
 * @param p_PatientName specifies the name of the patient
 */
//---------------------------------------------------------------------------

public void setPatientName (String p_PatientName)
	{
	m_PatientName = p_PatientName;
	}

//---------------------------------------------------------------------------
/**
 * Returns the social security number of the patient this transaction was 
 * performed for
 * @return social security number of patient this transaction was performed for
 */
//---------------------------------------------------------------------------

@Column(name = "patient_ssn", insertable =false, updatable = false)

public String getPatientSSN ()
	{
	return m_PatientSSN;
	}

//---------------------------------------------------------------------------
/**
 * Sets the social security number of the patient this transaction was performed 
 * for
 * @param p_PatientSSN specifies the social security number of the patient
 */
//---------------------------------------------------------------------------

public void setPatientSSN (String p_PatientSSN)
	{
	m_PatientSSN = p_PatientSSN;
	}

//---------------------------------------------------------------------------
/**
 * Returns the creation date of the invoice this transcation was performed on
 * @return Invoice creation date
 */
//---------------------------------------------------------------------------

@Column(name = "created", insertable =false, updatable = false)

public Date getInvoiceDate() 
	{
	return m_InvoiceDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified date as the creation date of the invoice this transcation 
 * was performed on
 * @param p_InvoiceDate specifies the new invoice creation date
 */
//---------------------------------------------------------------------------

public void setInvoiceDate (Date p_InvoiceDate) 
	{
	m_InvoiceDate = (p_InvoiceDate != null) ? new Date (p_InvoiceDate.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the Id of the settlement method used for this transaction
 * @return The Id of the settlement method
 */
//---------------------------------------------------------------------------

@Column(name = "settlement_id", insertable =false, updatable = false)

public Integer getSettlementId() 
	{
	return m_SettlementId;
	}

//---------------------------------------------------------------------------
/**
 * Sets the Id of the settlement method used for this transaction
 * @param p_SettlementId specifies the new Id of the settlement method used
 * for this transaction
 */
//---------------------------------------------------------------------------

public void setSettlementId (Integer p_SettlementId) 
	{
	m_SettlementId = p_SettlementId;
	}

//---------------------------------------------------------------------------
/**
 * Returns the settlement date recorded for this transaction.
 * @return The settlement date recorded for this transaction.
 */
//---------------------------------------------------------------------------

@Column (name ="settlement_date")

public Date getSettlementDate() 
	{
	return m_SettlementDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the settlement date for this transaction
 * @param p_SettlementDate specifies the new settlement date
 */
//---------------------------------------------------------------------------

public void setSettlementDate (Date p_SettlementDate) 
	{
	m_SettlementDate = (p_SettlementDate != null) ? new Date (p_SettlementDate.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the date this transaction was executed
 * @return Execution date of transaction
 */
//---------------------------------------------------------------------------

@Column(name = "transaction_date", insertable =false, updatable = false)

public Date getTransactionDate() 
	{
	return m_TransactionDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the execution date for this transaction
 * @param p_TransactionDate specifies the new execution date
 */
//---------------------------------------------------------------------------

public void setTransactionDate (Date p_TransactionDate) 
	{
	m_TransactionDate = (p_TransactionDate != null) ? new Date (p_TransactionDate.getTime()) : null;
	}

//---------------------------------------------------------------------------
/**
 * Returns the id of the user who executed the transaction
 * @return The id of the user
 */
//---------------------------------------------------------------------------

@Column(name = "cashier_id", insertable =false, updatable = false)

public Integer getCashierId() 
	{
	return m_CashierId;
	}

//---------------------------------------------------------------------------
/**
 * Sets the specified user as the one who executed this transaction
 * @param p_CashierId specifies the new user
 */
//---------------------------------------------------------------------------

public void setCashierId (Integer p_CashierId) 
	{
	m_CashierId = p_CashierId;
	}

//---------------------------------------------------------------------------
/**
 * Returns the amount debited by this transaction
 * @return The debited amount
 */
//---------------------------------------------------------------------------

@Column(name = "debit", insertable =false, updatable =false)

public Double getDebit() 
	{
	return m_Debit;
	}

//---------------------------------------------------------------------------
/**
 * Sets the amount debited by this transaction
 * @param p_Debit specifies the debited amount
 */
//---------------------------------------------------------------------------

public void setDebit (Double p_Debit) 
	{
	m_Debit = p_Debit;
	}

//---------------------------------------------------------------------------
/**
 * Returns the amount credited by this transaction
 * @return The credited amount
 */
//---------------------------------------------------------------------------

@Column(name = "credit", insertable =false, updatable =false)

public Double getCredit() 
	{
	return m_Credit;
	}

//---------------------------------------------------------------------------
/**
 * Sets the amount credited by this transaction
 * @param p_Credit specifies the credited amount
 */
//---------------------------------------------------------------------------

public void setCredit (Double p_Credit) 
	{
	m_Credit = p_Credit;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* End of Class															*
//***************************************************************************
}
