/*******************************************************************************
 * 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.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

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

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.InvoiceStub;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Statement;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.StatementInvoiceStubInterface;
import lu.tudor.santec.gecamed.billing.utils.InvoiceWorkflow;
import lu.tudor.santec.gecamed.billing.utils.StatementWorkflow;
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 lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalisationClass;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Insurance;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;

import org.apache.log4j.Level;

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

/**
 * The StatementInvoiceStubBean defines functionality to manage invoices that
 * will be put on third party statements. 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 (StatementInvoiceStubInterface.class)
public class StatementInvoiceStubBean extends GECAMedSessionBean implements StatementInvoiceStubInterface
	{
//	private static final long serialVersionUID = 1L
	
	/** the logger Object for this class */
	private static org.apache.log4j.Logger m_Logger = org.apache.log4j.Logger.getLogger(StatementInvoiceStubBean.class.getName());;

	@EJB 
	LoginInterface 				m_LoginBean;

	private Query				m_StatementQuery;
	private Integer				m_FirstInvoiceStub;
	private Integer				m_NumberOfInvoiceStubs;

	private static Hashtable 	<String,Insurance>	m_ThirdPartyPayers = null;
	
	private static Insurance			m_UCM;
	private static HospitalisationClass	m_Ambulatory;

//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************

	private static final int	c_TransactionTimeout	= 900; //15 Minutes
	
	private static final int	c_MinimumLengthOfStay	 = 4;
	private static final double	c_MinimumAmount		 	 = 100;
	
	private static final String	c_UCM					 = "UCM";
//	private static final String	c_Ambulatory			 = "A";
	
//---------------------------------------------------------------------------
//***************************************************************************
//* 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 ()
	{
	Collection <Insurance> l_ThirdPartyPayers;
	Iterator <Insurance>   l_ThirdPartyIterator;
	Insurance			   l_ThirdPartyPayer;
	
//	super.postConstruct();
	
	this.setTransactionTimeout (c_TransactionTimeout);
	
	m_StatementQuery 		= null;
	m_FirstInvoiceStub   	= null;
	m_NumberOfInvoiceStubs 	= null;
	
	if (m_ThirdPartyPayers == null)
		{
		try	{
			//====================================================================
			//= Pretch List of available Third Party Payers from GECAMed database
			//====================================================================
			
			m_ThirdPartyPayers = new Hashtable <String,Insurance> ();
			l_ThirdPartyPayers = this.getAllThirdPartyPayers();
			if (l_ThirdPartyPayers != null)
				{
				l_ThirdPartyIterator = l_ThirdPartyPayers.iterator();
				while (l_ThirdPartyIterator.hasNext())
					{
					l_ThirdPartyPayer = l_ThirdPartyIterator.next();
					m_ThirdPartyPayers.put (l_ThirdPartyPayer.getAcronym(),l_ThirdPartyPayer);
					}
				if (m_UCM == null) m_UCM = m_ThirdPartyPayers.get(c_UCM);
				}
			}
		catch (Exception p_Exception)
			{
			m_Logger.error("Failed to prefetch required Third Party Paying Insurances!",p_Exception);
			}
		}		

		if (m_Ambulatory == null)
		{
		try	{
			//====================================================================
			//= Pretch List of available Third Party Payers from GECAMed database
			//====================================================================
			
			m_Ambulatory = this.getHospitalisationClass(HospitalisationClass.c_Ambulant);
			}
		catch (Exception p_Exception)
			{
			m_Logger.error("Failed to prefetch required Ambulatory Hospitalisation Class!",p_Exception);
			}
		}		
	}
	
//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Returns all third party paying insurances defined in the databas.
 * @return a collection holding all third party paying insurances defined
 * in the database, <code>null</code> if none can be found.
 */
//---------------------------------------------------------------------------

@SuppressWarnings("unchecked")
private Collection <Insurance> getAllThirdPartyPayers () throws Exception
	{
	Collection	l_Insurances;
	
	try	{	
		this.openTransaction();
		l_Insurances = m_EntityManager.createNamedQuery (Insurance.c_ThirdPartyPayingInsurances)
									  .getResultList();
		this.closeTransaction(true);
		}
	catch (NoResultException p_Exception)
		{
		this.closeTransaction(true);
		l_Insurances = null;
		}
	catch (Exception p_Exception)
		{
		l_Insurances = null;
		this.closeTransaction(false);
		throw (p_Exception);
		}
	
	return l_Insurances;
	}

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

private HospitalisationClass getHospitalisationClass (String p_Acronym) throws Exception
	{
	HospitalisationClass l_Class;
		
	try	{	
		this.openTransaction();
		l_Class = (HospitalisationClass) m_EntityManager.createNamedQuery (HospitalisationClass.c_ClassByAcronym)
														.setParameter("acronym", p_Acronym)
														.getSingleResult();
		this.closeTransaction(true);
		}
	catch (NoResultException p_Exception)
		{
		this.closeTransaction(true);
		l_Class = null;
		}
	catch (Exception p_Exception)
		{
		l_Class = null;
		this.closeTransaction(false);
		throw (p_Exception);
		}
	
	return l_Class;
	}

//---------------------------------------------------------------------------
/**
 * The getHospitalizedGroup builds and returns a group of conditions to be used
 * in where clauses that identifies hospitalized invoices eligible for the
 * specified third party statement.
 * @param p_Statement specifies the statement to get hospitalized group for.
 * @return an initialized group of conditions to identify hospitalized invoices 
 * eligible for specified statement.
 */
//---------------------------------------------------------------------------

private Group getHospitalizedGroup (Statement p_Statement)
	{
	Group				l_HospitalizedGroup;	
	HibernateCondition	l_Condition;
	GregorianCalendar	l_EligibleDate;
	
	if (p_Statement == null) return null; 
		
	l_EligibleDate = new GregorianCalendar ();
	l_EligibleDate.setTime(p_Statement.getEndDate());
	l_EligibleDate.add (Calendar.DAY_OF_MONTH, - c_MinimumLengthOfStay);

	l_HospitalizedGroup = new Group ();
	l_HospitalizedGroup.setOperator(HibernateOperator.c_OrOperator);
	
	l_Condition   = new HibernateCondition ("invoiceDate",
				  					    	HibernateOperator.c_LowerOrEqualOperator,
				  					    	"eligibleDate",
				  					    	l_EligibleDate.getTime());
	
	l_HospitalizedGroup.addCondition(l_Condition);
	
	l_Condition   = new HibernateCondition ("amount",
				  					    	HibernateOperator.c_GreaterOrEqualOperator,
				  					    	"eligibleAmount",
				  					    	Double.valueOf(c_MinimumAmount));
	
	l_HospitalizedGroup.addCondition(l_Condition);

	if (m_UCM != null)
		{
		l_Condition = new HibernateCondition ("thirdPartyPayerId",
											  HibernateOperator.c_NotEqualOperator,
											  m_UCM.getId()); 
	
		l_HospitalizedGroup.addCondition(l_Condition);
		}

	if (m_Ambulatory != null)
		{
		l_Condition = new HibernateCondition ("hospitalisationClassId",
											  HibernateOperator.c_EqualOperator,
											  m_Ambulatory.getId()); 
	
		l_HospitalizedGroup.addCondition(l_Condition);
		}

	return l_HospitalizedGroup;
	
	}

//---------------------------------------------------------------------------
/**
 * Assembles the where clause for the specified third party statement.
 * @param p_Statement specifies the third party paying statement to assemble
 * where clause for.
 * @return The where clause required to build a query.
 */
//---------------------------------------------------------------------------

private WhereClause assembleWhereClause (Statement p_Statement)
	{
	WhereClause 		l_WhereClause = null;
	HibernateCondition	l_Condition   = null;
	HibernateList		l_List		  = null;
	
	Set <Insurance>		l_ThirdPartyPayers;	
	Set <Physician>		l_Physicians;
	
	if (p_Statement == null) return l_WhereClause; 
	
	l_WhereClause = new WhereClause ();
	l_WhereClause.setOperator (HibernateOperator.c_AndOperator);
	
	//========================================================================
	//= Specify which third party paying insurances we're looking for
	//========================================================================
	
	l_ThirdPartyPayers = p_Statement.getThirdPartyPayers();
	if (l_ThirdPartyPayers.size() > 0) {
		l_List = new HibernateList ("id",Insurance.class,l_ThirdPartyPayers);
		
		l_Condition = new HibernateCondition ("thirdPartyPayerId",
											  HibernateOperator.c_InOperator,
											  l_List); 
		
		l_WhereClause.addCondition(l_Condition);
	} else {
		l_Condition = new HibernateCondition ("thirdPartyPayerId", HibernateOperator.c_GreaterThanOperator, new Integer(0)); 
		l_WhereClause.addCondition(l_Condition);
	}
		
	//========================================================================
	//= If statement is not for all physicians, specify which physicians
	//= we're looking for
	//========================================================================

	if (p_Statement.getAllPhysicians() == false)
		{
		l_Physicians = p_Statement.getPhysicians();
		if (l_Physicians.size() > 0) 
			{
			l_List = new HibernateList ("id",Physician.class,l_Physicians);
			
			l_Condition = new HibernateCondition ("physicianId",
												  HibernateOperator.c_InOperator,
												  l_List); 
			
			l_WhereClause.addCondition(l_Condition);
			}
		}
	
	//========================================================================
	//= Invoice date must fall between start and end date of third party 
	//= statement to be eligible.
	//========================================================================

	l_Condition = new HibernateCondition ("invoiceDate",
										  HibernateOperator.c_GreaterOrEqualOperator,
										  "startDate",
										  p_Statement.getStartDate()); 

	l_WhereClause.addCondition(l_Condition);
	
	l_Condition = new HibernateCondition ("invoiceDate",
										  HibernateOperator.c_LowerOrEqualOperator,
										  "endDate",
										  p_Statement.getEndDate()); 
	
	l_WhereClause.addCondition(l_Condition);
	
	//========================================================================
	//= Eligible invoices are not yet on a third party statement / or on this statement
	//========================================================================

	Group l_StatementIdGroup = new Group(HibernateOperator.c_OrOperator);	
	
	l_Condition = new HibernateCondition ("statementId",
										  HibernateOperator.c_IsOperator,
										  Condition.c_Null);
	l_StatementIdGroup.addCondition(l_Condition);
	
	l_Condition = new HibernateCondition ("statementId",
										  HibernateOperator.c_EqualOperator,
										  p_Statement.getId()); 
	l_StatementIdGroup.addCondition(l_Condition);
	
	
	l_WhereClause.addGroup(l_StatementIdGroup);
	
	//========================================================================
	//= Eligible invoices are not settled yet.
	//========================================================================

//	l_Condition = new HibernateCondition ("balance",
//										  HibernateOperator.c_GreaterThanOperator,
//										  Double.valueOf(0d)); 
//	
//	l_WhereClause.addCondition(l_Condition);
//
//	l_Condition = new HibernateCondition ("state",
//										  HibernateOperator.c_LowerOrEqualOperator,
//										  InvoiceWorkflow.c_RemindedState); 
//	
//	l_WhereClause.addCondition(l_Condition);
	
	//========================================================================
	//= Add special considerations for hospitalized invoices.
	//========================================================================

	l_WhereClause.addGroup(this.getHospitalizedGroup(p_Statement));	
	
	this.log(Level.DEBUG, l_WhereClause.toString());
	
	return l_WhereClause;
	}

//---------------------------------------------------------------------------
/**
 * Returns the number of invoices eligible for the specified third party
 * statement.
 * @param p_Statement specifies the third party statement to get invoice
 * count for.
 * @return the number of invoices eligible for the specified third statement.
 */
//---------------------------------------------------------------------------

private Long countInvoiceStubsForStatement (Statement p_Statement) throws Exception 
	{
	Query				l_Query;
	String				l_QueryString;
	Long				l_InvoiceCount;
	
	if (p_Statement == null) return null; 
	
	l_QueryString = "SELECT COUNT (o) FROM InvoiceStub o";
	
	l_Query = HibernateQueryFactory.buildQueryFromWhereClause (m_EntityManager, 
															   l_QueryString, 
															   this.assembleWhereClause(p_Statement));
	
	l_InvoiceCount = (Long) l_Query.getSingleResult();
	
	return l_InvoiceCount;
	}

//---------------------------------------------------------------------------
/**
 * builds the query required to fetch invoice stubs for the specified
 * third party statement.
 * @param p_Statement specifies the third party statement to build query for.
 * @return An initialized query ready to be executed.
 */
//---------------------------------------------------------------------------

private Query buildQueryForStatement (Statement p_Statement) throws Exception 
	{
	Query					l_Query;
	String					l_QueryString;
	
	if (p_Statement == null) return null; 
	
	l_QueryString = "SELECT OBJECT(o) FROM InvoiceStub o $WHERECLAUSE ORDER BY o.patientName ASC, o.patientFirstName ASC";
 
	l_Query = HibernateQueryFactory.buildQueryFromNestedWhereClause (m_EntityManager, 
															   		 l_QueryString, 
															   		 this.assembleWhereClause(p_Statement));
	return l_Query;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Returns a very specific invoice stub identified by the specified Id.
 * @param p_Id specifies the Id of the invoice stub to get from
 * database.
 * @return The invoice stub with the specified Id, <code>null</code> if
 * no such invoice exists.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")
public InvoiceStub	getInvoiceStubById	(Integer p_Id) throws Exception
	{
	InvoiceStub	l_InvoiceStub;
	
	if (p_Id == null) return null;
	
	this.openTransaction();
	
	l_InvoiceStub = m_EntityManager.find (InvoiceStub.class, p_Id);
	
	this.closeTransaction(true);
	
	return l_InvoiceStub;
	}

//---------------------------------------------------------------------------
/**
 * Returns the number of invoices for the specified third party statement. 
 * Depending on state of third party statement, the method returns either
 * the number of currently eligible invoices (open statement) or the number
 * of invoices actually on the statement (closed statement).
 * @param p_Statement specifies the third party statement to get number of
 * invoices for.
 * @return The number of invoices for the specified third party statement.
 */
//---------------------------------------------------------------------------

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

public Long getInvoiceStubCountForStatement (Statement p_Statement) throws Exception 
	{
	Long l_InvoiceStubCount = 0L;
	
	this.openTransaction();
	
	if (p_Statement.getState() >= StatementWorkflow.c_ClosedState ) {
		l_InvoiceStubCount = (Long) m_EntityManager.createNamedQuery (InvoiceStub.c_InvoiceStubCountByStatementId)
		 		   .setParameter ("statementId",p_Statement.getId())
		 		   .getSingleResult();
		this.closeTransaction (true);
		return l_InvoiceStubCount; 
	} else {
		l_InvoiceStubCount = this.countInvoiceStubsForStatement(p_Statement);
		this.closeTransaction (true);
		return l_InvoiceStubCount;
	}
	
//	l_InvoiceStubCount = (Long) m_EntityManager.createNamedQuery (InvoiceStub.c_InvoiceStubCountByStatementId)
//					 		   .setParameter ("statementId",p_Statement.getId())
//					 		   .getSingleResult();
//	if ((l_InvoiceStubCount != null) && (l_InvoiceStubCount.longValue() > 0L)) 
//		{
//		this.closeTransaction (true);
//		return l_InvoiceStubCount; 
//		}
//	else
//		{
//		l_InvoiceStubCount = this.countInvoiceStubsForStatement(p_Statement);
//		this.closeTransaction (true);
//		return l_InvoiceStubCount;
//		}
	}

//---------------------------------------------------------------------------
/**
 * The method builds a query for the specified third party statement.
 * The method also opens a new transaction. The getNextInvoiceStubs () methods
 * assumes the buildInvoiceStubQueryForStatement being called first. To get 
 * matching invoices for this query you have to repeatedly call the 
 * getNextInvoiceStubs () method until it returns <code>null</code>. 
 * Depending on state of third party statement, the query returns either
 * the currently eligible invoices (open statement) or the invoices actually 
 * on the statement (closed statement).
 * @param p_Statement specifies the third party statement to build query for,
 * @see #setFirstInvoiceStub ()
 * @see #setNumberOfInvoiceStubs ()
 * @see #getNextInvoiceStubs ()
 * @see #close ()
 */
//---------------------------------------------------------------------------

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

public void	buildInvoiceStubQueryForStatement (Statement p_Statement) throws Exception
	{
	if (p_Statement == null) return; 
	
	this.openTransaction();
	
	if (p_Statement.getState() >= StatementWorkflow.c_ClosedState )
		{
		m_StatementQuery = m_EntityManager.createNamedQuery (InvoiceStub.c_InvoiceStubsByStatementId);
		m_StatementQuery.setParameter ("statementId",p_Statement.getId());
		}
	else m_StatementQuery = this.buildQueryForStatement (p_Statement);
	}

//---------------------------------------------------------------------------
/**
 * 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")
@SuppressWarnings("unchecked")

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")
@SuppressWarnings("unchecked")

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

//---------------------------------------------------------------------------
/**
 * Returns the next chunk of invoices. The method assumes that the
 * buildInvoiceStubQueryForStatement 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
	{
	Collection <InvoiceStub>		l_InvoiceStubs = null;
	
	if (m_StatementQuery != null)
		{
		if (m_FirstInvoiceStub != null) m_StatementQuery.setFirstResult(m_FirstInvoiceStub);
		if (m_NumberOfInvoiceStubs != null) m_StatementQuery.setMaxResults(m_NumberOfInvoiceStubs);
		
		l_InvoiceStubs = m_StatementQuery.getResultList();
		
		if ((l_InvoiceStubs != null) && (l_InvoiceStubs.size() > 0))
			{
			m_FirstInvoiceStub += l_InvoiceStubs.size();
			}
		}
	
	if ((l_InvoiceStubs == null) || (l_InvoiceStubs.size() < m_NumberOfInvoiceStubs)) 
		{
		this.closeTransaction(true);
		}
		
	return l_InvoiceStubs;
	}

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

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

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

//---------------------------------------------------------------------------
/**
 * closes specified invoices for the specified third party statement. Closing
 * an invoice sets invoice into the closed state and sets invoice's statement
 * property to the specified third party statement, thus linking invoice to
 * the statement.
 * @param p_Statement specifies the statement to set invoices statement
 * property to.
 * @param p_InvoiceIDs specifies the Ids of the invoices to close.
 */
//---------------------------------------------------------------------------

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

public void	closeInvoicesForStatement (Statement			 p_Statement, 
									   Collection <Integer> p_InvoiceIDs) throws Exception
	{
	Iterator <Integer>	l_InvoiceIDIterator;
	Invoice				l_Invoice;
	Date				l_ClosureDate = new Date ();
	int					l_Action;
	long				 l_Time;
	
	if ((p_Statement == null) || (p_InvoiceIDs == null)) return;
	
	if (p_Statement.isPersistent())
		{
		l_Time = System.currentTimeMillis();
		this.log(Level.INFO, "Closing Invoices for Statement " + p_Statement.formatStatementNumber() + " Invs:" + Arrays.toString(p_InvoiceIDs.toArray()));
		
		this.openTransaction();
		
		StringBuffer sb = new StringBuffer("\n");
		try	{		
			l_InvoiceIDIterator = p_InvoiceIDs.iterator();
			while (l_InvoiceIDIterator.hasNext())
				{
				l_Invoice = (Invoice) m_EntityManager.createNamedQuery(Invoice.c_InvoiceById)
										             .setParameter("id", l_InvoiceIDIterator.next())
										             .getSingleResult();
				
				l_Action = InvoiceWorkflow.changeInvoiceState(l_Invoice, InvoiceWorkflow.c_CloseAction);
				if (l_Action == InvoiceWorkflow.c_DoClose) {
					l_Invoice.setCloser(m_LoginBean.getCurrentUser());
					l_Invoice.setClosureDate (l_ClosureDate);
					l_Invoice.setStatement(p_Statement);
					l_Invoice = m_EntityManager.merge(l_Invoice);
					l_Invoice.updateFirstClassRequired();
					sb.append("\tClosed invoice: " + l_Invoice + "\n");
				} else {
					sb.append("\tNOT Closing invoice: " + l_Invoice + "\n");
				}
				}
			this.closeTransaction(true);
			}
		catch (Exception p_Exception)
			{
			this.closeTransaction(false);
			throw (p_Exception);
			}
			
		this.log(Level.INFO, sb.toString());
		l_Time = System.currentTimeMillis() - l_Time;
		this.log(Level.INFO, "Closing of invoices took " + l_Time + " ms to complete!");
		}
	}

//---------------------------------------------------------------------------
/**
 * opens specified invoices for the specified third party statement. Opening
 * an invoice sets invoice back into the state they had before being closed
 * and resets invoice's statement property, thus unlinking invoice from the
 * statement.
 * @param p_Statement specifies the statement to remove invoices from.
 * @param p_InvoiceIDs specifies the Ids of the invoices to open.
 */
//---------------------------------------------------------------------------

@RolesAllowed("gecam")
public void	openInvoicesForStatement (Statement	 p_Statement, 
						   			  Collection <Integer> p_InvoiceIDs) throws Exception
	{
	Iterator <Integer>	l_InvoiceIDIterator;
	Invoice				l_Invoice;
	int					l_Action;
	long				 l_Time;
	
	if ((p_Statement == null) || (p_InvoiceIDs == null)) return;
	
	if (p_Statement.isPersistent())
		{
		l_Time = System.currentTimeMillis();
		this.log(Level.INFO, "Opening Invoices for Statement " + p_Statement.formatStatementNumber()+ " Invs:" + Arrays.toString(p_InvoiceIDs.toArray()));
		
		this.openTransaction();
		StringBuffer sb = new StringBuffer("\n");
		try	{
			l_InvoiceIDIterator = p_InvoiceIDs.iterator();
			while (l_InvoiceIDIterator.hasNext())
				{
				l_Invoice = (Invoice) m_EntityManager.createNamedQuery(Invoice.c_InvoiceById)
										             .setParameter("id", l_InvoiceIDIterator.next())
										             .getSingleResult();
				
				l_Action = InvoiceWorkflow.changeInvoiceState(l_Invoice, InvoiceWorkflow.c_OpenAction);
				if (l_Action == InvoiceWorkflow.c_DoOpen)
					{
					l_Invoice.setCloser(null);
					l_Invoice.setClosureDate (null);
					l_Invoice.setStatement(null);
					m_EntityManager.merge(l_Invoice);
					sb.append("\tOpened invoice: " + l_Invoice + "\n");
				} else {
					sb.append("\tNOT opening invoice: " + l_Invoice + "\n");
				}
				}
			this.closeTransaction(true);
			}
		catch (Exception p_Exception)
			{
			this.closeTransaction(false);
			throw (p_Exception);
			}
				
		this.log(Level.INFO, sb.toString());
		l_Time = System.currentTimeMillis() - l_Time;
		this.log(Level.INFO, "Opening of invoices took " + l_Time + " ms to complete!");
		}
	}

//---------------------------------------------------------------------------
/**
 * Returns the number of fully settled invoice on the specified third party
 * statement.
 * @param p_Statement specifies the third party statement to get count of
 * fully settled invoice for.
 * @return The number of fully settled invoices on specified third party
 * statement.
 */
//---------------------------------------------------------------------------

public Long settledInvoiceCountForStatement	(Statement p_Statement) throws Exception
	{
	Long	l_Count = Long.valueOf(0);
	
	if ((p_Statement != null) && (p_Statement.isPersistent()))
		{
		this.openTransaction();
		
		try	{
			l_Count = (Long) m_EntityManager.createNamedQuery (InvoiceStub.c_SettledInvoiceStubCountByStatementId)
									  		.setParameter ("statementId",p_Statement.getId())
									  		.getSingleResult();
			}
		catch (Exception p_Exception)
			{
			this.closeTransaction(false);
			throw (p_Exception);
			}
	
		this.closeTransaction(true);		
		}
	
	return l_Count;
	}

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

@Remove
public void remove ()
	{
	}

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