/*******************************************************************************
 * 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.session.beans;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Vector;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.Query;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.InvoiceStub;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.ReminderInvoiceStubInterface;
import lu.tudor.santec.gecamed.billing.utils.InvoiceWorkflow;
import lu.tudor.santec.gecamed.core.ejb.session.beans.GECAMedSessionBean;
import lu.tudor.santec.gecamed.core.utils.querybuilder.Condition;
import lu.tudor.santec.gecamed.core.utils.querybuilder.Group;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateCondition;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateList;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateOperator;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateQueryFactory;
import lu.tudor.santec.gecamed.core.utils.querybuilder.WhereClause;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;

import org.apache.log4j.Level;

//***************************************************************************
//* Interface Definition and Members                                        *
//***************************************************************************

/**
 * The ReminderInvoiceStubBean defines functionality to manage invoices that
 * have not been settled yet and those that will or have already received
 * notices. The bean relies on Invoice stubs to improve performance of
 * transfering large numbers of invoices.
 * @author nico.mack@tudor.lu
 */

@Stateful
@TransactionManagement (TransactionManagementType.BEAN)
@Remote (ReminderInvoiceStubInterface.class)
public class ReminderInvoiceStubBean extends GECAMedSessionBean implements ReminderInvoiceStubInterface
	{
//	private static final long serialVersionUID = 1L;
	
	private Query						m_InvoiceStubQuery;
	private Physician					m_Physician;
	private Integer						m_FirstInvoiceStub;
	private Integer						m_NumberOfInvoiceStubs;
	private Integer						m_Ceiling;
	private Boolean						m_OnlyExpired;
	
	private WhereClause					m_WhereClause;
	
//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************
	
	public static final int	c_None							= 0;
	public static final int	c_OpenInvoicesQuery				= 1;
	public static final int	c_UnpaidInvoicesQuery			= 2;
	public static final int	c_FirstNoticeInvoicesQuery		= 3;
	public static final int	c_SecondNoticeInvoicesQuery		= 4;
	public static final int	c_DemandOfPaymentInvoicesQuery	= 5;
	
	
	private String	m_OrderBy					= "ORDER BY o.id";
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor		                                                        *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The postConstruct method is part of the bean lifecycle interface and its
 * being called just after bean has been created.
 */
//---------------------------------------------------------------------------

@PostConstruct
public void postConstruct ()
	{
//	super.postConstruct();
	
	m_InvoiceStubQuery		= null;
	m_Physician 			= null;
	m_WhereClause			= null;
	m_FirstInvoiceStub 		= 0;
	m_NumberOfInvoiceStubs  = 10;
	m_Ceiling				= 0;
	m_OnlyExpired			= false;
	}
	
//---------------------------------------------------------------------------
/**
 * The preDestroy method is part of the bean lifecycle interface and its
 * being called just before the bean is being destroyed.
 */
//---------------------------------------------------------------------------

@PreDestroy
public void preDestroy ()
	{
//	this.closeTransaction(false);
	}

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

private Group getSubSetGroup (Collection <Integer> p_SubSetIds)
	{
	Group					l_SubSetGroup;
	HibernateCondition		l_Condition;

	if ((p_SubSetIds == null) || (p_SubSetIds.isEmpty())) return null;		
		
	l_SubSetGroup = new Group ();
		
	l_Condition   = new HibernateCondition ("id",
			    							HibernateOperator.c_InOperator,
			    							new HibernateList (p_SubSetIds));
	
	l_SubSetGroup.addCondition(l_Condition);
	return l_SubSetGroup;
	}

//---------------------------------------------------------------------------
/**
 * The getUnsettledGroup builds and returns a group of conditions to be used
 * in where clauses that identifies invoices that have not been fully settled
 * yet.
 * @return an initialized group of conditions to identify invoices that have
 * not been fully settled.
 */
//---------------------------------------------------------------------------

private Group	getUnsettledGroup ()
	{
	Group					l_UnpaidGroup;
	HibernateCondition		l_Condition;
	
	l_UnpaidGroup = new Group ();
	l_UnpaidGroup.setOperator(HibernateOperator.c_AndOperator);
	
	l_Condition   = new HibernateCondition ("state",
					  					    HibernateOperator.c_GreaterOrEqualOperator,
					  					    InvoiceWorkflow.c_PrintedState);
		
	l_UnpaidGroup.addCondition (l_Condition);

	l_Condition   = new HibernateCondition ("balance",
				  					    	HibernateOperator.c_GreaterThanOperator,
				  					    	Double.valueOf(0d));
	
	l_UnpaidGroup.addCondition (l_Condition);

	return l_UnpaidGroup;
	}

//---------------------------------------------------------------------------
/**
 * The getNonThirdPartyGroup builds and returns a group of conditions to be used
 * in where clauses that identifies invoices that are not taken in charge
 * by a third party paying insurance.
 * @return an initialized group of conditions to identify invoices that are
 * taken in charge by a third party paying insurance.
 */
//---------------------------------------------------------------------------

private Group getNonThirdPartyGroup ()
	{
	Group					l_NonThirdPartyGroup;	
	HibernateCondition		l_Condition;

	l_NonThirdPartyGroup = new Group ();
	l_NonThirdPartyGroup.setOperator(HibernateOperator.c_OrOperator);
	
	l_Condition   = new HibernateCondition ("thirdPartyPayerId",
				  					    	HibernateOperator.c_IsOperator,
				  					    	Condition.c_Null);
	
	l_NonThirdPartyGroup.addCondition(l_Condition);
	
	l_Condition   = new HibernateCondition ("thirdPartyPayerId",
				  					    	HibernateOperator.c_EqualOperator,
				  					    	Integer.valueOf(0));
	
	l_NonThirdPartyGroup.addCondition(l_Condition);

	return l_NonThirdPartyGroup;
	}


//---------------------------------------------------------------------------
/**
 * builds a query for the specified query mode and using conditions from the
 * specified where clause. Depending on the query mode and the current state
 * of the mean, the methods adds additional conditions to the specified where
 * clause.
 * @param p_QueryString specifies the stem, i.e. the actually select statement
 * of the query to be build.
 * @param p_QueryMode specifies which kind of query to build. Possible values
 * for query mode are:
 * <ul>
 * <li>c_OpenInvoicesQuery				: Query for all still open invoices.</li>
 * <li>c_UnpaidInvoicesQuery			: Query for all unsettled invoices.</li>
 * <li>c_FirstNoticeInvoicesQuery		: Query for all invoices having received
 * a first notice already. </li>
 * <li>c_SecondNoticeInvoicesQuery		: Query for all invoices having received
 * a second notice already.</li>
 * <li>c_DemandOfPaymnetInvoicesQuery	: Query for all invoices having received
 * a demand of payment already.</li>
 * </ul>
 * @param p_Clause specifies the inital where clause to be integrated in
 * the built query.
 * @return an initialized query ready to be executed.
 */
//---------------------------------------------------------------------------

private Query buildQuery (String p_QueryString, int p_QueryMode, WhereClause p_Clause, String p_OrderByClause) throws Exception 
	{
	Group					l_ReminderGroup;
	HibernateCondition		l_Condition;
	
	l_ReminderGroup = new Group ();
	l_ReminderGroup.setOperator(HibernateOperator.c_AndOperator);

	
	if (p_QueryMode == c_OpenInvoicesQuery)
		// always get all open invoices (no reason to hide the rest)
		setOnlyExpired(Boolean.FALSE);

	switch (p_QueryMode)
	{
	case ReminderInvoiceStubBean.c_OpenInvoicesQuery:
		
		l_Condition   = new HibernateCondition ("state",
					  					    	HibernateOperator.c_LowerThanOperator,
					  					    	InvoiceWorkflow.c_PrintedState);
		
		l_ReminderGroup.addCondition (l_Condition);
		break;
		
	case ReminderInvoiceStubBean.c_UnpaidInvoicesQuery:

		l_ReminderGroup = this.getUnsettledGroup();
		
		l_Condition   = new HibernateCondition ("numberOfReminders",
					  					    	HibernateOperator.c_EqualOperator,
					  					    	Integer.valueOf(0));
		
		l_ReminderGroup.addCondition (l_Condition);
		
		break;
		
	case ReminderInvoiceStubBean.c_FirstNoticeInvoicesQuery:

		l_ReminderGroup = this.getUnsettledGroup();
		
		l_Condition   = new HibernateCondition ("numberOfReminders",
					  					    	HibernateOperator.c_EqualOperator,
					  					    	Integer.valueOf(1));
		
		l_ReminderGroup.addCondition (l_Condition);

		break;
		
	case ReminderInvoiceStubBean.c_SecondNoticeInvoicesQuery:

		l_ReminderGroup = this.getUnsettledGroup();
		
		l_Condition   = new HibernateCondition ("numberOfReminders",
					  					    	HibernateOperator.c_EqualOperator,
					  					    	Integer.valueOf(2));
		
		l_ReminderGroup.addCondition (l_Condition);

		break;
		
	case ReminderInvoiceStubBean.c_DemandOfPaymentInvoicesQuery:

		l_ReminderGroup = this.getUnsettledGroup();
		
		l_Condition   = new HibernateCondition ("numberOfReminders",
					  					    	HibernateOperator.c_EqualOperator,
					  					    	Integer.valueOf(3));
		
		l_ReminderGroup.addCondition (l_Condition);
		
		break;
	}
	
	l_ReminderGroup.addGroup (this.getNonThirdPartyGroup());
	
	if (m_Physician != null)
		{
		l_Condition   = new HibernateCondition ("physicianId",
				  					    		HibernateOperator.c_EqualOperator,
				  					    		m_Physician.getId());
		
		l_ReminderGroup.addCondition (l_Condition);		
		}
	
	if (m_OnlyExpired)
		{
		l_Condition   = new HibernateCondition ("dueDate",
				  					    		HibernateOperator.c_LowerOrEqualOperator,
				  					    		"today",
				  					    		InvoiceStub.stripTime (new Date ()));
		
		l_ReminderGroup.addCondition (l_Condition);
		}
	
	if (p_Clause == null) p_Clause = new WhereClause ();
	p_Clause.setOperator (HibernateOperator.c_AndOperator);
	
	p_Clause.addGroup (l_ReminderGroup);
	
	return HibernateQueryFactory.buildQueryFromWhereClause (m_EntityManager, p_QueryString, p_Clause, p_OrderByClause);
	}

//---------------------------------------------------------------------------
/**
 * returns the number of invoice for the specified query mode. Please note
 * that the method also considers the where clause specified by calling the
 * setWhereClause () method. The method assumes that a transaction has been
 * previously opened.
 * @param p_QueryMode specifies the query mode to use.
 * @return the number of matching invoices for the specified query mode.
 * @see #buildQuery () for more information about possible values for
 * query mode.
 */
//---------------------------------------------------------------------------

private Long getNumberOfInvoiceStubs (int p_QueryMode) throws Exception
	{
	Query				l_Query;
	String				l_QueryString;
	Long				l_InvoiceCount;
	
	l_QueryString = "SELECT COUNT (o) FROM InvoiceStub o ";
		
	l_Query = this.buildQuery(l_QueryString, p_QueryMode, m_WhereClause, null);
	l_InvoiceCount = (Long) l_Query.getSingleResult();
	
	return l_InvoiceCount;
	}

//---------------------------------------------------------------------------
/**
 * returns the new query for the specified query mode. Please note
 * that the method also considers the where clause specified by calling the
 * setWhereClause () method. 
 * @param p_QueryMode specifies the query mode to use.
 * @return an initialized query ready to be executed.
 * @see #buildQuery () for more information about possible values for
 * query mode.
 */
//---------------------------------------------------------------------------

private Query newInvoiceStubQuery (int p_QueryMode) throws Exception
	{
	Query					l_Query;
	String					l_QueryString;
	
	l_QueryString = "SELECT OBJECT(o) FROM InvoiceStub o ";
	
	l_Query = this.buildQuery(l_QueryString, p_QueryMode, m_WhereClause, m_OrderBy);	
	
	return l_Query;
	}

//---------------------------------------------------------------------------
/**
 * returns the size of the next chunk of invoices to fetch. Chunk size is normally 
 * defined by the number of invoices property. However, if number of remaining
 * invoices to fetch is small than number of invoices, then the number of remaining
 * invoices will be returned.
 * @return the number of invoices to fetch the next time.
 */
//---------------------------------------------------------------------------

private Integer getChunkSize ()
	{
	int	l_Current;
	int	l_Remaining;
	
	if (m_FirstInvoiceStub == null) return 0;
	if (m_NumberOfInvoiceStubs == null) return 0;
		
	l_Current   = m_FirstInvoiceStub;
	
	if ((m_Ceiling != null) && (m_Ceiling > 0))
		 l_Remaining = m_Ceiling - l_Current;
	else l_Remaining = m_NumberOfInvoiceStubs;
	
	l_Remaining = (l_Remaining > 0) ? l_Remaining : 0;
	
	return Math.min (l_Remaining, m_NumberOfInvoiceStubs);
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Sets the ceiling, i.e. the abslute maximum number of invoices to fetch.
 * The bean will never fetch more invoices than specified by ceiling. Set
 * ceiling to <code>null</code> (default) to disable ceiling.
 * @param p_Ceiling specifies the absolute maximum number of invoices to
 * fetch. Set <code>null</code> to disable.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void setCeiling (Integer p_Ceiling)
	{
	m_Ceiling = p_Ceiling;
	}

//---------------------------------------------------------------------------
/**
 * Sets the physician to be used for subsequent queries. Returned invoices and
 * invoice counts will be limited to specifies physician.
 * @param p_Physician specifies the physician to limit returned invoices and
 * invoice counts to. Set p_Physician to <code>null</code> (default) to disable
 * filtering by physician.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void setPhysician (Physician p_Physician)
	{
	m_Physician = p_Physician;
	}

//---------------------------------------------------------------------------
/**
 * Sets the flag specifying whether subsequent queries should only consider
 * already expired invoices or all of them.
 * @param p_OnlyExpired specifies new state of flag. <code>true</code> will
 * limit returned invoices and invoice counts to already expired invoices,
 * <code>false</code> will disable expiry filter for subsequent queries.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void setOnlyExpired (Boolean p_OnlyExpired)
	{
	m_OnlyExpired = p_OnlyExpired;
	}

//---------------------------------------------------------------------------
/**
 * Sets an extra where clause to be integrated into subsequent queries.
 * @param p_WhereClause specifies the extra where clause to be integrated into
 * subsequent queries. Specify <code>null</code> to disable extra where
 * clause.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void	setWhereClause	(WhereClause p_WhereClause)
	{
	m_WhereClause = p_WhereClause;
	}

@RolesAllowed("gecam")

public void	setOrderBy	(String p_OrderBy)
	{
	m_OrderBy = p_OrderBy;
	}

//---------------------------------------------------------------------------
/**
 * Returns the number of invoices for the specified query mode. Please note
 * that previously set filters, like only expired, physicians and extra where
 * clauses are taken into account.
 * @param p_QueryMode specifies the query mode to get invoice count for. Possible
 * values are:
 * <ul>
 * <li>c_OpenInvoicesQuery				: Query for all still open invoices.</li>
 * <li>c_UnpaidInvoicesQuery			: Query for all unsettled invoices.</li>
 * <li>c_FirstNoticeInvoicesQuery		: Query for all invoices having received
 * a first notice already.</li>
 * <li>c_SecondNoticeInvoicesQuery		: Query for all invoices having received
 * a second notice already.</li>
 * <li>c_DemandOfPaymnetInvoicesQuery	: Query for all invoices having received
 * a demand of payment already.</li>
 * </ul>
 * @return The number of invoices for the specified query mode and previously
 * set filters.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public Long getInvoiceStubCount (Integer p_QueryMode) throws Exception 
	{
	Long l_InvoiceStubCount = Long.valueOf(0L);
	int	l_QueryMode;
	
	if (p_QueryMode == null) return l_InvoiceStubCount;
	
	l_QueryMode = p_QueryMode.intValue();
	
	if (l_QueryMode == c_OpenInvoicesQuery)
		// always get all open invoices (no reason to hide the rest)
		setOnlyExpired(Boolean.FALSE);
	
	if (	(l_QueryMode >= c_OpenInvoicesQuery)
		 && (l_QueryMode <= c_DemandOfPaymentInvoicesQuery))
		{
		try	{		
			this.openTransaction();
		
			l_InvoiceStubCount = (Long) this.getNumberOfInvoiceStubs (l_QueryMode);
		
			this.closeTransaction (true);
			}
		catch (Exception p_Exception)
			{
			this.log(Level.ERROR, "Failed to get Invoice count", p_Exception);
			this.closeTransaction (false);
			throw p_Exception;
			}
		}
		
	return l_InvoiceStubCount; 
	}

//---------------------------------------------------------------------------
/**
 * Filters the specified set of Invoice IDs by checking properties according
 * to the specified query mode.
 * 
 * @param p_QueryMode specifies the query mode to filter invoice IDs by. Possible
 * values are:
 * <ul>
 * <li>c_OpenInvoicesQuery				: Query for all still open invoices.</li>
 * <li>c_UnpaidInvoicesQuery			: Query for all unsettled invoices.</li>
 * <li>c_FirstNoticeInvoicesQuery		: Query for all invoices having received
 * a first notice already.</li>
 * <li>c_SecondNoticeInvoicesQuery		: Query for all invoices having received
 * a second notice already.</li>
 * <li>c_DemandOfPaymnetInvoicesQuery	: Query for all invoices having received
 * a demand of payment already.</li>
 * </ul>
 * @param p_SubSetIds specifies a collection of Invoice IDs to filter.
 * @return A collection of Invoice IDs, which matched criteria defined for
 * specified query mode and which were in the original subset.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")
@SuppressWarnings("unchecked")
public Collection <Integer>	filterByQueryMode (Integer p_QueryMode, Collection <Integer> p_SubSetIds) throws Exception
	{
	Group					l_SubSetGroup;
	Query					l_Query;
	String					l_QueryString;
	Collection <Integer>	l_FilteredSubSet;	
	
	l_SubSetGroup = this.getSubSetGroup (p_SubSetIds);
	if (l_SubSetGroup == null) return p_SubSetIds;
	
	m_WhereClause = new WhereClause ();

	l_QueryString = "SELECT o.id FROM InvoiceStub o ";
	m_WhereClause.addGroup (l_SubSetGroup);
	
	try	{
		this.openTransaction();
		l_Query = this.buildQuery (l_QueryString, p_QueryMode, m_WhereClause, m_OrderBy);
		l_FilteredSubSet = l_Query.getResultList();
		this.closeTransaction (true);
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Failed to filter sub set by query mode!", p_Exception);
		this.closeTransaction (false);
		throw p_Exception;
		}
	
	return l_FilteredSubSet;
	}

//---------------------------------------------------------------------------

@RolesAllowed("gecam")
@SuppressWarnings("unchecked")
public Collection <InvoiceStub>	getInvoiceStubsByIds (Collection <Integer> p_InvoiceIds) throws Exception
	{
	Group						l_SubSetGroup;
	WhereClause					l_Clause;
	Query						l_Query;
	String						l_QueryString;
	Collection <InvoiceStub>	l_InvoiceStubs;	
	
	l_QueryString = "SELECT OBJECT(o) FROM InvoiceStub o ";

	l_SubSetGroup = this.getSubSetGroup (p_InvoiceIds);
	if (l_SubSetGroup == null) return null;
	
	l_Clause = new WhereClause ();
	l_Clause.addGroup (l_SubSetGroup);
	
	// this.log(Level.INFO, l_Clause.toString());
	
	try	{
		this.openTransaction();
		l_Query = HibernateQueryFactory.buildQueryFromWhereClause (m_EntityManager, l_QueryString, l_Clause);
		l_InvoiceStubs = l_Query.getResultList();
		this.closeTransaction (true);
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Failed to fetch invoice stubs!", p_Exception);
		this.closeTransaction (false);
		throw p_Exception;
		}
	
	return l_InvoiceStubs;
	}

//---------------------------------------------------------------------------
/**
 * Returns the counts of invoices for all query modes, considering only
 * expired invoices first and then all invoices. Content of returned collection
 * is structured as follows:
 * <table>
 * <tr><th>Pos</th> <th>Query Mode</th> <th>Expired</th></tr>
 * <tr><td>0</td><td>c_OpenInvoicesQuery</td><td>true</td></tr>
 * <tr><td>1</td><td>c_OpenInvoicesQuery</td><td>false</td></tr>
 * <tr><td>2</td><td>c_UnpaidInvoicesQuery</td><td>true</td></tr>
 * <tr><td>3</td><td>c_UnpaidInvoicesQuery</td><td>false</td></tr>
 * <tr><td>4</td><td>c_FirstNoticeInvoicesQuery</td><td>true</td></tr>
 * <tr><td>5</td><td>c_FirstNoticeInvoicesQuery</td><td>false</td></tr>
 * <tr><td>6</td><td>c_SecondNoticeInvoicesQuery</td><td>true</td></tr>
 * <tr><td>7</td><td>c_SecondNoticeInvoicesQuery</td><td>false</td></tr>
 * <tr><td>8</td><td>c_DemandOfPaymentInvoicesQuery</td><td>true</td></tr>
 * <tr><td>9</td><td>c_DemandOfPaymentInvoicesQuery</td><td>false</td></tr>
 * </table>
 * Please note that other filter criteria like physician and extra where
 * clause are still taken into account.
 * @return a collection holding the invoice counts as specified above.
 */
//---------------------------------------------------------------------------

public Collection <Long> getAllInvoiceStubCounts () throws Exception
	{
	Long 				l_InvoiceStubCount = 0L;
	int					l_QueryMode;
	Collection <Long>	l_AllCounts;
	
	try	{		
		l_AllCounts = new Vector <Long> ();
		
		this.openTransaction();
		
		for (l_QueryMode = c_OpenInvoicesQuery; l_QueryMode <= c_DemandOfPaymentInvoicesQuery; l_QueryMode++)
			{
			this.setOnlyExpired(true);
			l_InvoiceStubCount = (Long) this.getNumberOfInvoiceStubs (l_QueryMode);
			
			l_AllCounts.add(l_InvoiceStubCount);
			
			this.setOnlyExpired(false);
			l_InvoiceStubCount = (Long) this.getNumberOfInvoiceStubs (l_QueryMode);
			
			l_AllCounts.add(l_InvoiceStubCount);
			}
			
		this.closeTransaction (true);
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Failed to get Invoice count", p_Exception);
		this.closeTransaction (false);
		throw p_Exception;
		}
		
	return l_AllCounts; 
	}

//---------------------------------------------------------------------------
/**
 * The method builds a query for the specified query mode, taking into account
 * previously set filter criteria (physician, expiry and extra where clause).
 * The method also opens a new transaction. The getNextInvoiceStubs () methods
 * assumes the buildInvoiceStubQuery being called first. The get matching invoices
 * for this query you have to repeatedly call the getNextInvoiceStubs () method
 * until it returns <code>null</code>. 
 * @param p_QueryMode specifies the kind of query to build and initiate.
 * @see #setFirstInvoiceStub ()
 * @see #setNumberOfInvoiceStubs ()
 * @see #getNextInvoiceStubs ()
 * @see #close ()
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void buildInvoiceStubQuery (Integer p_QueryMode) throws Exception
	{
	int	l_QueryMode;
	
	if (p_QueryMode == null) return;
	
	l_QueryMode = p_QueryMode.intValue();
	
		if (	(l_QueryMode >= c_OpenInvoicesQuery) && (l_QueryMode <= c_DemandOfPaymentInvoicesQuery)) {
			this.openTransaction();
			this.setFirstInvoiceStub (0);
			m_InvoiceStubQuery = this.newInvoiceStubQuery (l_QueryMode);
		}
	}

//---------------------------------------------------------------------------
/**
 * Sets the position in query result row set to start returning invoices from
 * @param p_FirstInvoiceStub specifies the position in result row set to start
 * return invoices from.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void setFirstInvoiceStub (Integer p_FirstInvoiceStub)
	{
	m_FirstInvoiceStub = p_FirstInvoiceStub;
	}

//---------------------------------------------------------------------------
/**
 * Specifies the maximum number of invoices to be returned every time the
 * getNextInvoiceStubs () method is being called.
 * @param p_NumberOfInvoiceStubs specifies the number of invoices to be fetched
 * every time getNextInvoiceStubs () is being called.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void setNumberOfInvoiceStubs (Integer p_NumberOfInvoiceStubs)
	{
	m_NumberOfInvoiceStubs = p_NumberOfInvoiceStubs;
	}

//---------------------------------------------------------------------------
/**
 * Returns the next chunk of invoices. The method assumes that the
 * buildInvoiceStubQuery has been called first. The method should be called
 * repeatedly until no more invoices are returned. After all invoices have
 * been returned, the method closes previously opened transaction.
 * @return A collection holding a maximum number, specified by calling
 * setNumberOfInvoices () method, of invoices. Method returns <code>null</code>
 * if no more invoices are available.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")
@SuppressWarnings("unchecked")

public Collection <InvoiceStub> getNextInvoiceStubs () throws Exception
	{
	ArrayList <InvoiceStub>		l_OrderedInvoices = null;
	Collection <InvoiceStub>	l_Invoices = null;
	int							l_FirstResult;
	int							l_MaxResults;
	
	try	{
		if (m_InvoiceStubQuery != null)
			{
			
			if (m_FirstInvoiceStub != null) 
				l_FirstResult	= m_FirstInvoiceStub;
			else
				l_FirstResult	= 0;
			l_MaxResults	= this.getChunkSize();
			
			m_InvoiceStubQuery.setFirstResult(l_FirstResult);
			m_InvoiceStubQuery.setMaxResults (l_MaxResults);
			
//			System.out.println("QUERY "+l_MaxResults+" INVOICES, FROM: "+l_FirstResult+" TO: "+(l_FirstResult+l_MaxResults));
			
			l_Invoices = m_InvoiceStubQuery.getResultList();
			
			if ((l_Invoices != null) && (l_Invoices.size() > 0))
				{
				l_OrderedInvoices = new ArrayList<InvoiceStub> (l_Invoices);
	
				m_FirstInvoiceStub += l_Invoices.size();
				}
			}
		
		if ((l_OrderedInvoices == null) || (l_OrderedInvoices.size() < m_NumberOfInvoiceStubs))
			{
			this.closeTransaction(true);
			}
		}
	catch (Exception p_Exception)
		{
		this.log(Level.ERROR, "Failed to get chunk of Invoice Stubs", p_Exception);
		this.closeTransaction (false);
		throw p_Exception;
		}
	
	return l_OrderedInvoices;
	}

//---------------------------------------------------------------------------
/**
 * Rollback of previously opened transaction. Needs only to be called in case
 * of an error.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")

public void close ()
	{
	this.closeTransaction(false);
	m_InvoiceStubQuery 	= null;
	}

//---------------------------------------------------------------------------
/**
 * Releases this stateful session bean.
 */
//---------------------------------------------------------------------------

@Remove
public void remove ()
	{	
	}

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