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

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Act;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Rate;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Suffix;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.RuleBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.NomenclatureInterface;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.RuleInterface;
import lu.tudor.santec.gecamed.billing.gui.event.invoice.InvoiceChangeEvent;
import lu.tudor.santec.gecamed.billing.utils.InvoiceWorkflow;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.log.Log;
import lu.tudor.santec.gecamed.core.ejb.entity.beans.log.LogType;
import lu.tudor.santec.gecamed.core.ejb.session.interfaces.LogManager;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Bean;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Order;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Passage;
import lu.tudor.santec.gecamed.hl7import.ejb.entity.beans.HL7Prescription;
import lu.tudor.santec.gecamed.hl7import.ejb.session.interfaces.OrderInterface;
import lu.tudor.santec.gecamed.hl7import.utils.InvoiceChangeTracker;
import lu.tudor.santec.gecamed.office.ejb.entity.beans.Physician;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalPrescription;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalPrescriptionPage;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Hospitalisation;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalisationClass;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.HospitalisationPeriod;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Passage;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.usermanagement.ejb.session.interfaces.LoginInterface;

import org.apache.log4j.Level;
import org.jboss.annotation.ejb.Service;

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

@Service
@Local (OrderInterface.class)

public class OrderBean implements OrderInterface
{
	@PersistenceContext (unitName="gecam")
	EntityManager m_GECAMedEntityManager;
	
    @EJB
    LogManager logManager;
    
	@EJB
	private LoginInterface								m_LoginInterface;

	@EJB
	private RuleInterface								m_RuleInterface;
	
	@EJB
	private NomenclatureInterface						m_NomenclatureInterface;

	@Resource (mappedName=c_BillingUpdateTopic)
    private Topic 										m_BillingUpdateTopic;
    
    @Resource (mappedName="ConnectionFactory")
    private TopicConnectionFactory 								m_ConnectionFactory;
   
 //   private TopicConnection 									m_TopicConnection;

	private static Hashtable <String,HospitalisationClass>		m_HospitalisationClasses = null;
	private static Hashtable <String,Physician>					m_PhysiciansByName		 = null;
	private static Hashtable <String,Physician>					m_PhysiciansByUCMCode	 = null;
	
//***************************************************************************
//* Constants	                                                           *
//***************************************************************************

//	private static final long serialVersionUID = 1L;

	private static final String c_BillingUpdateTopic 	= "topic/GECAMed/billingUpdateTopic";
	
	private static final String	c_Ambulatory			="A";
//	private static final String	c_FirstClass			="1";
//	private static final String	c_SecondClass			="2";
	
	private	static final	int		c_ExpiryPeriod	= 60;
	private static final	boolean c_InvoicePerPhysician = false;

	private static SimpleDateFormat m_DateFormat = new SimpleDateFormat ("d MMMM yyyy");
	
	/** the logger Object for this class */
	private static org.apache.log4j.Logger m_Logger = org.apache.log4j.Logger.getLogger(OrderBean.class.getName());
//	private static final Category m_Logger = Category.getInstance(OrderBean.class.getName());

	private static Pattern 
    
	c_HL7ExaminationCodePattern = Pattern.compile ("^(\\d{1}[A-Z]{1}\\d{1,2}[A-Z]{0,1})$",Pattern.CASE_INSENSITIVE);
	
	private static Pattern 
    
	c_HL7InterpreterPattern = Pattern.compile ("^(\\d*)&(\\w*)[\\s&](.*)",Pattern.CASE_INSENSITIVE);

    private static Pattern 
    
    c_HL7UCMCodePattern = Pattern.compile ("^(\\d{6})(\\d{2})?$",Pattern.CASE_INSENSITIVE);
	
    private static Pattern 
    
    c_AccidentNumberPattern = Pattern.compile ("^U(\\d{4})\\/?(\\d{5})$",Pattern.CASE_INSENSITIVE);

    
//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************

//@PostConstruct
//public void postConstruct ()
public void start ()
    {
	Set 		<Suffix>				l_Suffixes;
	
	Collection	<HospitalisationClass> 	l_HospitalisationClasses;
	Iterator	<HospitalisationClass>	l_HospitalisationClassIterator;
	HospitalisationClass				l_HospitalisationClass;
	
	Collection	<Physician> 			l_Physicians;
	Iterator	<Physician>				l_PhysicianIterator;
	Physician							l_Physician;
	String								l_PhysicianName;

    try	{	
		//====================================================================
		//= Pretch List of available Third Party Payers from GECAMed database
		//====================================================================
		
		l_Suffixes = new HashSet <Suffix> ();
		l_Suffixes.addAll(this.getAllSuffixes());
		
		Act.setAllSuffixes(l_Suffixes);
		Invoice.setExpiryPeriod (this.getExpiryPeriod());
		
		InvoiceWorkflow.setCheckPermissions(false);
		
		//====================================================================
		//= Pretch List of available Hospitalisation Classes from GECAMed database
		//====================================================================

		if (m_HospitalisationClasses == null)
			{
			m_HospitalisationClasses = new Hashtable <String,HospitalisationClass> ();
			l_HospitalisationClasses = this.getAllHospitalisationClasses();
			if (l_HospitalisationClasses != null)
				{
				l_HospitalisationClassIterator = l_HospitalisationClasses.iterator();
				while (l_HospitalisationClassIterator.hasNext())
					{
					l_HospitalisationClass = l_HospitalisationClassIterator.next();
					m_HospitalisationClasses.put (l_HospitalisationClass.getAcronym(),l_HospitalisationClass);
					}		
				}
			}
		//====================================================================
		//= Pretch List of available Physicians from GECAMed database
		//====================================================================
		
		if ((m_PhysiciansByName == null) || (m_PhysiciansByUCMCode == null))
			{
			m_PhysiciansByName 	 = new Hashtable <String,Physician> ();
			m_PhysiciansByUCMCode = new Hashtable <String,Physician> ();
			l_Physicians = this.getAllPhysicians();
			if (l_Physicians != null)
				{
				l_PhysicianIterator = l_Physicians.iterator();
				while (l_PhysicianIterator.hasNext())
					{
					l_Physician = l_PhysicianIterator.next();
					l_PhysicianName = l_Physician.getFirstName() + " " + l_Physician.getName();
	
					m_PhysiciansByName.put (l_PhysicianName.toLowerCase(),l_Physician);
					if (l_Physician.getUcmCode() != null)
						{
						m_PhysiciansByUCMCode.put (l_Physician.getUcmCode(),l_Physician);
						}
					}
				}
			}
    	
		this.log (Level.INFO, "OrderBean ready to accept requests!");
    	}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to prefetch required Objects",p_Exception);
		}
//	m_TopicConnection = null;
	
//	try {
// 		m_TopicConnection = m_ConnectionFactory.createTopicConnection();
//        m_TopicConnection.start();
//        } 
//	catch (Exception p_Exception) 
//		{
//        this.log (Level.FATAL, "Failed to connect to patient topic",p_Exception);
//        }
 	}

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

//@PreDestroy
//public void preDestroy ()
public void stop ()
	{
//    this.log (Level.INFO, "Destroying Orderbean Instance " + Integer.valueOf(m_InstanceCount).toString());
//    m_InstanceCount--;
//		
//	if (m_TopicConnection == null) return;
//	
//	try {
//        m_TopicConnection.close();
//        } 
//	catch (Exception p_Exception) 
//		{
//        this.log (Level.FATAL, "Failed to close patient topic",p_Exception);
//        }
	
	this.log (Level.INFO, "OrderBean stopped!");
	}

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

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

private Integer getExpiryPeriod ()
	{
	Integer l_Period;
	
	l_Period = (Integer)m_LoginInterface.getAdminSettingValue("Billing", "DuePeriod");
	if (l_Period != null) 
		 return l_Period;
	else return c_ExpiryPeriod;
	
//	return c_ExpiryPeriod;
	}

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

private RuleInterface getRuleInterface ()
	{
	if (m_RuleInterface != null) return m_RuleInterface;

	try {
		m_RuleInterface = (RuleInterface) ManagerFactory.getRemote(RuleBean.class);
//		InitialContext l_Context = new InitialContext();
//		m_RuleInterface = (RuleInterface) l_Context.lookup("RuleBean/remote");
//		l_Context.close();
		} 
	catch (Exception p_Exception) 
		{
		this.log (Level.FATAL, "Failed to get RuleInterface",p_Exception);
		}

	return m_RuleInterface;
	}

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

private void log (Level p_Level, String p_Message)
	{
	this.log(p_Level,p_Message,null);
	}

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

private void log (Level p_Level, String p_Message, Exception p_Exception)
	{
	StackTraceElement[] l_StackTrace; 
	String				l_MethodName;
	                  
	l_StackTrace = new Throwable().getStackTrace();
	l_MethodName = l_StackTrace[1].getMethodName();
	
	if (l_MethodName.equals("log")) l_MethodName = l_StackTrace[2].getMethodName();
	p_Message = "\n" + l_MethodName + " => " + p_Message;
	
	if (p_Exception != null) m_Logger.log (p_Level,p_Message,p_Exception);
						else m_Logger.log (p_Level,p_Message);
	}

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

//===========================================================================
//= HL7Interface Context Primitives									    	=
//===========================================================================

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

@SuppressWarnings("unchecked")
private Collection <HL7Passage> getHL7PassagesByMessageId (Integer p_MessageId) throws Exception 
	{	
	Collection l_Passages;

	try	{
		l_Passages = m_GECAMedEntityManager.createNamedQuery ("getHL7PassagesByMessageId")
   									   .setParameter("messageid", p_MessageId)
   									   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Passages = null;
		}

	return l_Passages;
	}

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

@SuppressWarnings("unchecked")
private Collection <HL7Prescription> getHL7PrescriptionsByMessageId (Integer p_MessageId) throws Exception 
	{	
	Collection l_Prescriptions;

	try	{
		l_Prescriptions = m_GECAMedEntityManager.createNamedQuery ("getHL7PrescriptionsByMessageId")
   									   		    .setParameter("messageid", p_MessageId)
   									   		    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Prescriptions = null;
		}

	return l_Prescriptions;
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Order by setting its rejected flag.
 * @param p_Order specifies the HL7Order object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Order quarantineHL7Order (HL7Order p_Order)
	{
	if (p_Order == null) return null;
	
	p_Order.setQuarantined(Boolean.TRUE);
	p_Order = m_GECAMedEntityManager.merge(p_Order);
		
	return p_Order;
	}

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

private void deleteHL7Order (HL7Order p_Order)
	{
	if (p_Order == null) return;
	
	p_Order = m_GECAMedEntityManager.find (HL7Order.class, p_Order.getId());
	if ((p_Order != null) && (p_Order.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Order);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Passage by setting its rejected flag.
 * @param p_Passage specifies the HL7Passage object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Passage quarantineHL7Passage (HL7Passage p_Passage)
	{
	if (p_Passage == null) return null;
	
	p_Passage.setQuarantined(Boolean.TRUE);
	p_Passage = m_GECAMedEntityManager.merge(p_Passage);
		
	return p_Passage;
	}

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

private void deleteHL7Passage (HL7Passage p_Passage)
	{
	if (p_Passage == null) return;
	
	p_Passage = m_GECAMedEntityManager.find (HL7Passage.class, p_Passage.getId());
	if ((p_Passage != null) && (p_Passage.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Passage);
	}

//---------------------------------------------------------------------------
/**
 * Rejects the specified HL7Prescription by setting its rejected flag.
 * @param p_Prescription specifies the HL7Prescription object to be rejected
 */
//---------------------------------------------------------------------------

private HL7Prescription quarantineHL7Prescription (HL7Prescription p_Prescription)
	{
	if (p_Prescription == null) return null;
	
	p_Prescription.setQuarantined(Boolean.TRUE);
	p_Prescription = m_GECAMedEntityManager.merge(p_Prescription);
		
	return p_Prescription;
	}

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

private void deleteHL7Prescription (HL7Prescription p_Prescription)
	{
	if (p_Prescription == null) return;
	
	p_Prescription = m_GECAMedEntityManager.find (HL7Prescription.class, p_Prescription.getId());
	if ((p_Prescription != null) && (p_Prescription.getQuarantined() == false))
		m_GECAMedEntityManager.remove(p_Prescription);
	}

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

@SuppressWarnings("unchecked")
private void deleteReferences (HL7Order p_Order)
	{
	Collection <HL7Passage> 		l_Passages;
	Iterator <HL7Passage> 			l_PassageIterator;
	HL7Passage						l_Passage;

	Collection <HL7Prescription> 	l_Prescriptions;
	Iterator <HL7Prescription> 		l_PrescriptionIterator;
	HL7Prescription					l_Prescription;

	if (p_Order == null) return;

	try	{	
		
		//====================================================================
		//= Delete orphaned HL7Passage objects
		//====================================================================
		
		l_Passages = m_GECAMedEntityManager.createNamedQuery ("getHL7PassagesByMessageId")
										.setParameter("messageid", p_Order.getMessageId())
										.getResultList();
		
		if (l_Passages != null)
			{
			l_PassageIterator = l_Passages.iterator();
			while (l_PassageIterator.hasNext())
				{
				l_Passage = l_PassageIterator.next();
				this.deleteHL7Passage (l_Passage);
				}	
			}
		
		//====================================================================
		//= Delete orphaned HL7Prescription objects
		//====================================================================
		
		l_Prescriptions = m_GECAMedEntityManager.createNamedQuery ("getHL7PrescriptionsByMessageId")
										    .setParameter("messageid", p_Order.getMessageId())
										    .getResultList();
		
		if (l_Prescriptions != null)
			{
			l_PrescriptionIterator = l_Prescriptions.iterator();
			while (l_PrescriptionIterator.hasNext())
				{
				l_Prescription = l_PrescriptionIterator.next();
				this.deleteHL7Prescription (l_Prescription);
				}	
			}
		
		
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to delete objects referenced by Order with Message ID " + p_Order.getMessageId(),p_Exception);
		}
	}

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

@SuppressWarnings("unchecked")
private void quarantineReferences (HL7Order p_Order)
	{
	Collection <HL7Passage> 		l_Passages;
	Iterator <HL7Passage> 			l_PassageIterator;
	HL7Passage						l_Passage;

	Collection <HL7Prescription> 	l_Prescriptions;
	Iterator <HL7Prescription> 		l_PrescriptionIterator;
	HL7Prescription					l_Prescription;

	if (p_Order == null) return;

	try	{	
		
		//====================================================================
		//= Delete orphaned HL7Passage objects
		//====================================================================
		
		l_Passages = m_GECAMedEntityManager.createNamedQuery ("getHL7PassagesByMessageId")
										.setParameter("messageid", p_Order.getMessageId())
										.getResultList();
		
		if (l_Passages != null)
			{
			l_PassageIterator = l_Passages.iterator();
			while (l_PassageIterator.hasNext())
				{
				l_Passage = l_PassageIterator.next();
				this.quarantineHL7Passage (l_Passage);
				}	
			}
		
		//====================================================================
		//= Delete orphaned HL7Prescription objects
		//====================================================================
		
		l_Prescriptions = m_GECAMedEntityManager.createNamedQuery ("getHL7PrescriptionsByMessageId")
										    .setParameter("messageid", p_Order.getMessageId())
										    .getResultList();
		
		if (l_Prescriptions != null)
			{
			l_PrescriptionIterator = l_Prescriptions.iterator();
			while (l_PrescriptionIterator.hasNext())
				{
				l_Prescription = l_PrescriptionIterator.next();
				this.quarantineHL7Prescription (l_Prescription);
				}	
			}	
		}
	catch (Exception p_Exception)
		{
		this.log (Level.FATAL, "Failed to quarantining objects referenced by Order with Message ID " + p_Order.getMessageId(),p_Exception);
		}
	}

//===========================================================================
//= GECAMed Context Primitives												=
//===========================================================================

@SuppressWarnings("unchecked")
private Collection <Suffix> getAllSuffixes () throws Exception
	{
	Collection	l_Suffixes;
	
	try	{	
		l_Suffixes = m_GECAMedEntityManager.createNamedQuery ("getAllSuffixes")
									    		.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Suffixes = null;
		}

	return l_Suffixes;
	}

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

@SuppressWarnings("unchecked")
private Collection <HospitalisationClass> getAllHospitalisationClasses () throws Exception
	{
	Collection	l_Classes;
	
	try	{	
		l_Classes = m_GECAMedEntityManager.createNamedQuery ("getAllHospitalisationClasses")
									    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Classes = null;
		}

	return l_Classes;
	}

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

@SuppressWarnings("unchecked")
private Collection <Physician> getAllPhysicians () throws Exception
	{
	Collection	l_Physicians;
	
	try	{	
		l_Physicians = m_GECAMedEntityManager.createNamedQuery ("findAllPhysician")
									    		.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Physicians = null;
		}

	return l_Physicians;
	}

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

private Patient getPatientByRISid (String p_RISid) throws Exception 
	{	
	Patient l_Patient;

	try	{
		l_Patient = (Patient) m_GECAMedEntityManager.createNamedQuery (Patient.FIND_PATIENT_BY_RISID)
   													.setParameter("risID", p_RISid)
   													.setMaxResults(1)
   													.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Patient = null;
		}

	return l_Patient;
	}

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

private Hospitalisation getHospitalisationByVisitId (String p_VisitId) throws Exception
	{
	Hospitalisation	l_Hospitalisation;
	
	try	{
		l_Hospitalisation = (Hospitalisation) m_GECAMedEntityManager.createNamedQuery ("getHospitalisationByPassageId")
											.setParameter("passageid", p_VisitId)
											.getSingleResult();
  		}
	catch (NoResultException p_Exception)
		{
		l_Hospitalisation = null;
		}
	
	return l_Hospitalisation;
	}

//---------------------------------------------------------------------------
@SuppressWarnings("unchecked")
private HospitalisationPeriod getHospitalisationPeriodById (Integer p_Id) throws Exception
	{
	HospitalisationPeriod l_Period;

	try	{
		l_Period = (HospitalisationPeriod) m_GECAMedEntityManager.find (HospitalisationPeriod.class, p_Id);
  		}
	catch (NoResultException p_Exception)
		{
		l_Period = null;
		}

	return l_Period;
	}

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

@SuppressWarnings("unchecked")
private Collection <Invoice> getInvoicesByHospitalisation (Hospitalisation p_Hospitalisation) throws Exception
	{
	Collection	l_Invoices;
	
	try	{	
		l_Invoices = m_GECAMedEntityManager.createNamedQuery ("getOpenInvoicesByHospitalisation")
											.setParameter("hospitalisation", p_Hospitalisation)
									    	.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoices = null;
		}

	return l_Invoices;
	}

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

@SuppressWarnings("unchecked")
private Collection <Invoice> getInvoicesByAccident (Patient p_Patient, String p_AccidentNumber, Date p_AccidentDate) throws Exception
	{
	Collection	l_Invoices;
	
	try	{	
		l_Invoices = m_GECAMedEntityManager.createNamedQuery ("getInvoicesByAccident")
										   .setParameter("patient", p_Patient)
										   .setParameter("accidentNumber", p_AccidentNumber)
										   .setParameter("accidentDate", p_AccidentDate)
										   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoices = null;
		}
	
	return l_Invoices;
	}

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

@SuppressWarnings("unchecked")
private Collection <Invoice> getInvoicesByAccidentDate (Patient p_Patient, Date p_AccidentDate) throws Exception
	{
	Collection	l_Invoices;
	
	try	{	
		l_Invoices = m_GECAMedEntityManager.createNamedQuery ("getInvoicesByAccidentDate")
										   .setParameter("patient", p_Patient)
										   .setParameter("accidentDate", p_AccidentDate)
										   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoices = null;
		}
	
	return l_Invoices;
	}

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

@SuppressWarnings("unchecked")
private Collection <Invoice> getInvoicesEligibleForThirdParty () throws Exception
	{
	Collection	l_Invoices;
	
	try	{
		l_Invoices = m_GECAMedEntityManager.createNamedQuery ("getInvoicesEligibleForThirdParty")
											.setParameter("ambulatory", m_HospitalisationClasses.get(c_Ambulatory))
											.setParameter("fourDaysAgo", getEarlierDate(new Date(), 4))
										    .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoices = null;
		}
	
	return l_Invoices;	
	}

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

private Invoice getInvoiceByAct (Act p_Act)
	{
	Invoice l_Invoice = null;
	
	if (p_Act == null) return l_Invoice;
	
	try	{
		l_Invoice = m_GECAMedEntityManager.find(Invoice.class, p_Act.getInvoiceId());
		l_Invoice.updateFirstClassRequired();
		}
	catch (NoResultException p_Exception)
		{
		l_Invoice = null;
		}
	
	return l_Invoice;
	}

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

private Passage getPassageByAccessionNumber (String p_AccessionNumber) throws Exception
	{
	Passage l_Passage;
	
	try	{
		l_Passage = (Passage) m_GECAMedEntityManager.createNamedQuery (Passage.FIND_PASSAGE_BY_ACCESSION_NUMBER)
	 												.setParameter("accessionNumber", p_AccessionNumber)
	 												.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Passage = null;
		}
	catch (NonUniqueResultException p_Exception)
		{
		this.log (Level.WARN, "Found more than one passage for Accession Number " + p_AccessionNumber + "! Returning only latest one!");
		l_Passage = this.getLatestPassage (p_AccessionNumber);
		}
	
	return l_Passage;
	}

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

@SuppressWarnings("unchecked")
private Passage getLatestPassage (String p_AccessionNumber) throws Exception
	{
	Collection <Passage> l_Passages;
	Iterator <Passage>	 l_PassageIterator;
	Passage				 l_Current;
	Passage				 l_Latest = null;
	
	try	{
		l_Passages = m_GECAMedEntityManager.createNamedQuery (Passage.FIND_PASSAGE_BY_ACCESSION_NUMBER)
 												.setParameter("accessionNumber", p_AccessionNumber)
 												.getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Passages = null;
		}

	if (l_Passages == null) return null;
	
	l_PassageIterator = l_Passages.iterator();
	while (l_PassageIterator.hasNext())
		{
		l_Current = l_PassageIterator.next();
		
		if ((l_Latest == null) || (l_Current.getId() > l_Latest.getId())) 
			{
			l_Latest = l_Current;
			}
		}
	
	return l_Latest;
	}

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

@SuppressWarnings("unchecked")
private Collection <Passage> getPassagesByPrescription (HospitalPrescription p_Prescription) throws Exception
	{
	Collection<Passage> l_Passages;
	
	if (p_Prescription == null) return null;
	
	try	{
		l_Passages = m_GECAMedEntityManager.createNamedQuery (Passage.FIND_PASSAGE_BY_HOSPITAL_PPRESCRIPTION_ID)
	 									   .setParameter("hospitalPrescriptionId", p_Prescription.getId())
	 									   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Passages = null;
		}

	return l_Passages;	
	}

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

private Rate getRateByCode (String p_Code) throws Exception 
	{
	Rate l_Rate;

	try	{
		l_Rate = (Rate) m_GECAMedEntityManager.createNamedQuery ("getRateByCode")
		   							 		.setParameter("code", p_Code)
		   							 		.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Rate = null;
		}
	
	return l_Rate;
	}

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

private Act getActByAccessionNumber (String p_AccessionNumber) throws Exception
	{
	Act l_Act;
	
	try	{
		l_Act = (Act) m_GECAMedEntityManager.createNamedQuery ("getActByAccessionNumber")
	 										.setParameter("accessionNumber", p_AccessionNumber)
	 										.getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Act = null;
		}
	catch (NonUniqueResultException p_Exception)
		{
		this.log (Level.WARN, "Found more than one act for Accession Number " + p_AccessionNumber + "! Returning only latest one!");
		l_Act = this.getLatestAct(p_AccessionNumber);
		}
	
	return l_Act;
	}

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

@SuppressWarnings("unchecked")
private Act getLatestAct (String p_AccessionNumber) throws Exception
	{
	Collection <Act> l_Acts;
	Iterator <Act>	 l_ActIterator;
	Act				 l_Current;
	Act				 l_Latest = null;
	
	try	{
		l_Acts = m_GECAMedEntityManager.createNamedQuery ("getActByAccessionNumber")
 									   .setParameter("accessionNumber", p_AccessionNumber)
 									   .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Acts = null;
		}

	if (l_Acts == null) return null;
	
	l_ActIterator = l_Acts.iterator();
	while (l_ActIterator.hasNext())
		{
		l_Current = l_ActIterator.next();
		
		if ((l_Latest == null) || (l_Current.getId() > l_Latest.getId())) 
			{
			l_Latest = l_Current;
			}
		}
	
	return l_Latest;
	}

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

private HospitalPrescription getPrescriptionById (Integer p_Id) throws Exception
	{
	HospitalPrescription	l_Prescription;
	
	try	{
		l_Prescription = (HospitalPrescription) m_GECAMedEntityManager.find(HospitalPrescription.class,p_Id);
		}
	catch (NoResultException p_Exception)
		{
		l_Prescription = null;
		}

	return l_Prescription;	
	}

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

private HospitalPrescription getPrescriptionByOrderNumber (String p_OrderNumber) throws Exception
	{
	HospitalPrescription	l_Prescription;
	
	try	{
		l_Prescription = (HospitalPrescription) m_GECAMedEntityManager.createNamedQuery (HospitalPrescription.GET_BY_ORDER_NUMBER)
	 																  .setParameter("orderNumber", p_OrderNumber)
	 																  .setMaxResults(1)
	 																  .getSingleResult();
		}
	catch (NoResultException p_Exception)
		{
		l_Prescription = null;
		}

	return l_Prescription;	
	}

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

private HospitalPrescription savePrescription (HospitalPrescription p_Prescription) throws Exception
	{	
//	Collection <Passage>	l_Passages;
//	Iterator <Passage>		l_PassageIterator;
//	Passage					l_Passage;
	
	if (p_Prescription == null) return null;
		
//	l_Passages = p_Prescription.getPassages();
//	if (l_Passages != null)
//		{
//		l_PassageIterator = l_Passages.iterator();
//		while (l_PassageIterator.hasNext())
//			{
//			l_Passage = l_PassageIterator.next();
//			l_Passage.setHospperiodId (p_Prescription.getHospperiodId());
//			l_Passage.setHospitalPrescriptionId (p_Prescription.getId());
//			l_Passage = this.savePassage(l_Passage);
//			}
//		}
	
	p_Prescription = m_GECAMedEntityManager.merge (p_Prescription);	
	
	return p_Prescription;
	}

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

private Invoice saveInvoice (Invoice p_Invoice) throws Exception
	{	
	if (p_Invoice == null) return null;

	p_Invoice.monetize();
		
	p_Invoice = m_GECAMedEntityManager.merge (p_Invoice);
	p_Invoice.updateFirstClassRequired();
	
	return p_Invoice;
	}

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

private HospitalisationPeriod saveHospitalisationPeriod (HospitalisationPeriod p_Period) throws Exception
	{
	if (p_Period == null) return null;
		
	p_Period = m_GECAMedEntityManager.merge(p_Period);
	
	return p_Period;
	}

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

private Passage savePassage (Passage p_Passage) throws Exception
	{	
	if (p_Passage == null) return null;

	p_Passage = m_GECAMedEntityManager.merge (p_Passage);	
	
	return p_Passage;
	}

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

private void deleteInvoice (Invoice p_Invoice)
	{
	if (p_Invoice == null) return;
	
	p_Invoice = m_GECAMedEntityManager.find (Invoice.class, p_Invoice.getId());
	if (p_Invoice != null) m_GECAMedEntityManager.remove(p_Invoice);
	}

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

private void deletePassage (Passage p_Passage)
	{
	if (p_Passage == null) return;
	
	p_Passage = m_GECAMedEntityManager.find (Passage.class, p_Passage.getId());
	if (p_Passage != null) m_GECAMedEntityManager.remove(p_Passage);
	}

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

private void deleteAct (Act p_Act)
	{
	if (p_Act == null) return;
	
	p_Act = m_GECAMedEntityManager.find (Act.class, p_Act.getId());
	if (p_Act != null) m_GECAMedEntityManager.remove(p_Act);
	}

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

private void deleteHospitalPrescriptionPage (HospitalPrescriptionPage p_Page)
	{
	if (p_Page == null) return;
	
	p_Page = m_GECAMedEntityManager.find (HospitalPrescriptionPage.class, p_Page.getId());
	if (p_Page != null) m_GECAMedEntityManager.remove(p_Page);
	}


//===========================================================================
//= General Primitives												    	=
//===========================================================================

//---------------------------------------------------------------------------
/**
 * Given a Date, the getFirstOfMonth method returns a new Date object dated
 * back to the first of the month specified by the original Date object, e.g.
 * if the orginal date was the 14th of octobre 2006, the method will return
 * a date object set to the 1st of october (at 0:00 hour).
 * @param p_Date specifies the original Date object.
 * @return a new date object dated back to the first of the month specified
 * by p_Date. Returns <b>null</b> if specified p_Date was <b>null</b> to.
 */
//---------------------------------------------------------------------------

private Date getFirstOfMonth (Date p_Date)
	{
	GregorianCalendar	l_FirstOfMonth;
	
	if (p_Date == null) return null;
		
	l_FirstOfMonth = new GregorianCalendar();
	l_FirstOfMonth.setTime (p_Date);
	l_FirstOfMonth.set (Calendar.DAY_OF_MONTH,1);
	l_FirstOfMonth.set (Calendar.HOUR_OF_DAY,0);
	l_FirstOfMonth.set (Calendar.MINUTE,0);
	l_FirstOfMonth.set (Calendar.SECOND,0);
	l_FirstOfMonth.set (Calendar.MILLISECOND,0);
	
	return l_FirstOfMonth.getTime();
	}

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

public static Date getEarlierDate (Date p_OriginalDate, int p_DaysEarlier)
	{
	GregorianCalendar	l_EarlierDate;
	
	if (p_OriginalDate == null) return null;
	
	if (p_DaysEarlier > 0) p_DaysEarlier = 0 - p_DaysEarlier;
	
	l_EarlierDate = new GregorianCalendar ();
	l_EarlierDate.setTime (p_OriginalDate);
	l_EarlierDate.add (Calendar.DAY_OF_MONTH, p_DaysEarlier);
	
	return l_EarlierDate.getTime();
	}

//---------------------------------------------------------------------------
/**
 * Given a collection of invoices, the buildInvoiceLookupTable method creates
 * a hashtable to avoid iterating over invoice collection every time a
 * specific invoice is required.  
 * @param p_Invoices specifies the collection of invoices to build the
 * lookup table for.
 * @return A hashtable using Strings generated by generateInvoiceKey method
 * as keys and invoices specified by p_Invoices as values.
 */
//---------------------------------------------------------------------------

private Hashtable <String,Invoice> buildInvoiceLookupTable (Collection <Invoice> p_Invoices)
	{
	Iterator <Invoice>			l_InvoiceIterator;
	Invoice						l_Invoice;
	Hashtable <String,Invoice>	l_LookupTable;
	
	l_LookupTable = new Hashtable <String,Invoice> ();
	
	if (p_Invoices == null) return l_LookupTable;
	
	l_InvoiceIterator = p_Invoices.iterator();
	while (l_InvoiceIterator.hasNext())
		{
		l_Invoice = l_InvoiceIterator.next();
		l_LookupTable = this.addToInvoiceLookupTable(l_LookupTable, l_Invoice);
		}
	
	return l_LookupTable;
	}

//---------------------------------------------------------------------------
/**
 * the method adds the specified invoice to specified invoice lookup table.
 * @param p_LookupTable the lookup table to add invoice to. The method uses keys 
 * generated by Invoice's generateInvoiceKey method for lookup.
 * @param p_Invoice specifies the invoice to be added.
 * @return the lookup table with the added invoice.
 * @see generateInvoiceKey for more information regarding format of keys.
 */
//---------------------------------------------------------------------------

private Hashtable <String,Invoice> addToInvoiceLookupTable (Hashtable <String,Invoice> p_LookupTable, Invoice p_Invoice)
	{
	String						l_InvoiceKey;

	if ((p_LookupTable == null) && (p_Invoice == null)) return p_LookupTable;
	
	l_InvoiceKey = p_Invoice.generateInvoiceKey(c_InvoicePerPhysician);	
	if (!p_LookupTable.containsKey(l_InvoiceKey))
		{
		p_LookupTable.put(l_InvoiceKey, p_Invoice);
		}
	else this.log(Level.WARN, "Duplicate Invoice Key " + l_InvoiceKey + "! Skipping Duplicate!");
	
	return p_LookupTable;
	}

//---------------------------------------------------------------------------
/**
 * Checks whether the critical properties of two Acts are equal. Critical
 * properties being checked are the accession number, the UCM code and the
 * date and time both acts were performed. If those properties match, then
 * both Acts are considered to be equal.
 * @param p_Original specifies the reference act.
 * @param p_New specifies the act to check against reference.
 * @return <code>TRUE</code> if critical attributes of specified acts do
 * match, <code>FALSE</code> otherwise.
 */
//---------------------------------------------------------------------------

private boolean areSameActs (Act p_Original, Act p_New)
	{
	boolean	l_AreSame = true;
	
	if ((p_Original == null) || (p_New == null)) return false;
	
	l_AreSame &= (p_Original.getAccessionNumber().equals(p_New.getAccessionNumber()));
	l_AreSame &= (p_Original.getCode().equals(p_New.getCode()));
	l_AreSame &= (p_Original.getPerformedDate().equals(p_New.getPerformedDate()));
	
	return l_AreSame;
	}

//---------------------------------------------------------------------------
/**
 * The getFirstInPatientPeriod returns the first real hospitalisation period,
 * i.e. the first hospitalisation period with first or second hospitalisation
 * class. Method assumes that Hospitalisation Periods are sorted in
 * chronolgical order, i.e. earliest period first.
 * @param p_Periods specifies all hospitalisation periods ordered by date.
 * @retun the first In Patient hopitalisation period if available, <b>null</b>
 * otherwise 
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod getFirstInPatientPeriod (Set <HospitalisationPeriod> p_Periods)
	{
	Iterator <HospitalisationPeriod>	l_PeriodIterator			= null;
	
	HospitalisationPeriod				l_Period					= null;
	HospitalisationPeriod				l_FirstInPatientPeriod		= null;
		
	HospitalisationClass				l_Class						= null;
	boolean								l_Found						= false;
	
	if (p_Periods == null) return null;
	
	l_PeriodIterator = p_Periods.iterator();
			
	while (l_PeriodIterator.hasNext() && !l_Found) 
		{
		l_Period 	= l_PeriodIterator.next();
		l_Class  	= l_Period.getHospitalisationClass();
		
		if ((l_Class != null) && !c_Ambulatory.equals (l_Class.getAcronym()))
			{
			l_FirstInPatientPeriod = l_Period;
			l_Found	= true;
			}
		}
	
	return l_FirstInPatientPeriod;
	}

//---------------------------------------------------------------------------
/**
 * The getFirstHospitalisationClass returns the acronym of the first real
 * hospitalisation class, i.e. first or second class, found in the ordererd
 * set of hospitalisation periods specified by p_HospitalisationPeriods.
 */
//---------------------------------------------------------------------------

private String getHospitalisationClass (HospitalisationPeriod p_Period)
	{
	HospitalisationClass				l_Class						= null;
	String								l_FirstHospitalisationClass = "";

	if (p_Period != null)
		{
		l_Class = p_Period.getHospitalisationClass();
		if (l_Class != null) l_FirstHospitalisationClass = l_Class.getAcronym();
		}
	
	return l_FirstHospitalisationClass;
	}
		
//---------------------------------------------------------------------------

private HospitalisationClass getInPatientHospitalisationClass (HospitalisationPeriod p_Period,
															   HospitalisationPeriod p_FirstInPatientPeriod)
	{
	HospitalisationClass	l_Class;
	String					l_InPatientClass;
	
	if (p_Period == null) return null;
	
	l_Class = p_Period.getHospitalisationClass();
	l_InPatientClass = this.getHospitalisationClass(p_FirstInPatientPeriod);
				
	if (	(c_Ambulatory.equals(l_Class.getAcronym()))
		&&  (l_InPatientClass.length() > 0))
		{	
		l_Class = m_HospitalisationClasses.get(l_InPatientClass);	
		}

	return l_Class;
	}

//---------------------------------------------------------------------------
/**
 * Given a set of hospitalisation periods and a date, the method attempts
 * to find the hospitialisaton period enclosing the specified date.
 * @param p_Periods specifies the set of hospitalisation periods to search
 * in
 * @param p_Date specifies the date to find the matching hospitalisation
 * period for.
 * @return the hospitalisation period enclosing the specified date. Right now
 * the method returns the last period if no match could be found. In case 
 * specified set of hospitalisation periods was <b>null</b>, the method will 
 * return<b>null</b> to.
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod getHospitalisationPeriodByDate (Set <HospitalisationPeriod> p_Periods,
															  HospitalisationPeriod		  p_FirstInPatientPeriod,
															  Date						  p_Date)
	{
	Iterator <HospitalisationPeriod>	l_PeriodIterator;
	HospitalisationPeriod				l_Period			= null;
	HospitalisationPeriod				l_PotentialMatch	= null;
	HospitalisationClass				l_Class				= null;
	
	boolean								l_Found				= false;
	
	if (p_Periods == null) return null;
	
	l_PeriodIterator = p_Periods.iterator();
	while (l_PeriodIterator.hasNext() && !l_Found)
		{
		l_Period = l_PeriodIterator.next();
		if (l_Period.getStartDate().compareTo(p_Date) <= 0)
			{
			l_PotentialMatch = l_Period;
			}
		else if (l_PotentialMatch != null) l_Found = true;
		}
	
	//========================================================================
	//= If a matching hospitalisation period could be found and if that period
	//= is of class ambulatory, then we're going to check whether an actual
	//= In Patient period (class 1 or 2) exists with the same date. If such
	//= a period exists, that period will be returned instead of the ambulatory
	//= one.
	//========================================================================
	
	if (l_PotentialMatch != null)
		{
		l_Class = l_PotentialMatch.getHospitalisationClass();
		if ((l_Class != null) && (c_Ambulatory.equals(l_Class.getAcronym())))
			{
			l_Period = p_FirstInPatientPeriod;
			if ((l_Period != null) && (l_Period.getStartDate().compareTo(l_PotentialMatch.getStartDate()) == 0))
				{
				l_PotentialMatch = l_Period;
				}
			}
		}
	
	return l_PotentialMatch;
	}

//---------------------------------------------------------------------------
/**
 * the method attaches the specified prescription to the specified hospitalisation
 * period.
 * @param p_Prescription specifies the prescription to be attached.
 * @param p_Period specifies the hospitalisation period to attach the
 * prescription to.
 * @return the modified hospitalisation period.
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod setPrescriptionForHospitalisationPeriod (HospitalPrescription		p_Prescription,
												      				   HospitalisationPeriod 	p_Period)
	{
	Set <HospitalPrescription>	l_Prescriptions;
	
	if ((p_Period == null) || (p_Prescription == null)) return p_Period;
	
	l_Prescriptions = p_Period.getPrescriptions();
	if (l_Prescriptions == null) l_Prescriptions = new HashSet <HospitalPrescription> ();
					
	p_Prescription.setHospperiodId (p_Period.getId());
	if (!l_Prescriptions.contains (p_Prescription))
		{
		l_Prescriptions.add (p_Prescription);
		p_Period.setPrescriptions (l_Prescriptions);
		}
	
	return p_Period;
	}

//---------------------------------------------------------------------------
/**
 * the method adds the specified passage to the specified hospitalisation
 * period.
 * @param p_Passage specifies the passage to be added.
 * @param p_Period specifies the hospitalisation period to add the passage to.
 * @return the modified hospitalisation period.
 */
//---------------------------------------------------------------------------

private HospitalisationPeriod addPassageToHospitalisationPeriod (Passage				p_Passage,
											    				 HospitalisationPeriod 	p_Period)
	{
	Set <Passage>	l_Passages;
	
	if ((p_Period == null) || (p_Passage == null)) return p_Period;
	
	l_Passages = p_Period.getPassages();
	if (l_Passages == null) l_Passages = new HashSet <Passage> ();
	if (!l_Passages.contains (p_Passage))
			l_Passages.add(p_Passage);					
	p_Period.setPassages(l_Passages);					
	
	return p_Period;
	}

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

private Invoice addActToInvoice (Invoice p_Invoice,	Act	p_Act)
	{
	Set  <Act>	l_InvoicedActs;

	if ((p_Invoice == null) || (p_Act == null)) return p_Invoice;
	
	l_InvoicedActs = p_Invoice.getActs();
	if (l_InvoicedActs == null) l_InvoicedActs = new HashSet <Act> ();
					
	l_InvoicedActs.add(p_Act);
	p_Invoice.setActs(l_InvoicedActs);

	return p_Invoice;
	}

//---------------------------------------------------------------------------
/**
 * The getInvoiceForPhysician returns an invoice for the specified physician.
 * Using the specified hospitalisation class and the specified date, the 
 * method attempts to find the proper invoice in the specified lookup table
 * specified by p_Invoices. In case no matching invoice could be found, the
 * method creates a new one matching the given criteria.
 * @param p_Invoices specifies the lookup table containing already existing
 * invoices.
 * @param p_Physician specifies the physician to get invoice for.
 * @param p_Patient specifies the patient to whom the invoice is destined. This
 * attribute will only be used if a new invoice has to be created.
 * @param p_Class specifies the hospitalisation class of the sought invoice.
 * @param p_Date specifies the date of which the month will be used to find
 * the correct invoice.
 */
//---------------------------------------------------------------------------

private Invoice getInvoiceForPhysician (Hashtable <String,Invoice> 	p_Invoices,
										Physician					p_Physician,
										Patient						p_Patient,
										HospitalisationClass		p_Class,
										Date						p_Date)
	{
	Invoice				l_Invoice			= null;
	String				l_Key;
	GregorianCalendar	l_RequiredDate;
	Physician			l_Physician = null;
	
	if ((p_Invoices == null) || (p_Physician == null) || (p_Class == null)) return null;

	//========================================================================
	//= Build key to lookup invoice in lookup table.
	//========================================================================
	
	l_RequiredDate = new GregorianCalendar ();
	l_RequiredDate.setTime(p_Date);

	if (c_InvoicePerPhysician) l_Physician = p_Physician;
	
	l_Key = Invoice.generateInvoiceKey(p_Class, l_Physician, l_RequiredDate.getTime());

	this.log (Level.INFO, "Looking up invoice with key " + l_Key);

	//========================================================================
	//= Use build key to lookup invoice in specified lookup table. If lookup
	//= was successful return found invoice, otherwise create a new invoice
	//= using the specified criteria. Newly created invoice will be added
	//= to specified lookup table.
	//========================================================================

	if (p_Invoices.containsKey(l_Key))
		{
		this.log (Level.INFO, "Lookup successful!");

		l_Invoice = p_Invoices.get(l_Key);
		}
	else
		{
		l_Invoice = new Invoice ();
		l_Invoice.setPatient				(p_Patient);
		l_Invoice.setHealthInsurance		(p_Patient.getInsurance());
		l_Invoice.setInvoiceDate			(p_Date);
		l_Invoice.setState					(Integer.valueOf(InvoiceWorkflow.c_OpenState));
		l_Invoice.setOldState				(Integer.valueOf(InvoiceWorkflow.c_OpenState));
//		l_Invoice.setDueDate				(Invoice.getDueDate(p_Date));
		l_Invoice.setDueDate				(false);
		l_Invoice.setHospitalisationClass 	(p_Class);
		l_Invoice.setPhysician				(p_Physician);
		
		l_Key = l_Invoice.generateInvoiceKey (c_InvoicePerPhysician);
		
		this.log (Level.INFO, "No such Invoice. Creating new one with key " + l_Key);

		p_Invoices.put(l_Key, l_Invoice);
		}
	
	return l_Invoice;
	}

//---------------------------------------------------------------------------
/**
 * The getInvoiceForAccident method attempts to find an invoice for the
 * specified patient related to a specific accident, either specified by
 * an accident number if available or by an accident date.
 * @param p_Patient specifies the patient whose invoices are to be searched
 * @param p_AccidentNumber specifies the accident number if it is already
 * available. Setting p_AccidentNumber to <code>NULL</code> will result in the
 * number being ignored
 * @param p_AccidentDate specifies the date the accident of interest happened
 * on.
 * @param p_DateOfInterest specifies the date the potential act to be added
 * to invoice.

 * @return The invoice related to the specified accident if such an invoice
 * exists and is still ellible to accept acts, <code>NULL</code> otherwise.
 */
//---------------------------------------------------------------------------

private Invoice getInvoiceForAccident (Patient p_Patient, String p_AccidentNumber, 
									   Date p_AccidentDate,	Date p_DateOfInterest) throws Exception
	{
	Collection <Invoice> l_Invoices;
	Iterator <Invoice>	 l_InvoiceIterator;
	Invoice				 l_Invoice;
	String				 l_Message;
	
	Date				 l_FirstOfInvoiceMonth;
	
	Invoice				 l_Candidate = null;
	Matcher				 l_AccidentNumberMatcher;
	boolean				 l_ValidAccidentNumber = false;
	
	if (p_Patient == null) return null;
	
	if (p_AccidentNumber != null)
		{
		l_AccidentNumberMatcher = c_AccidentNumberPattern.matcher(p_AccidentNumber);
		l_ValidAccidentNumber = l_AccidentNumberMatcher.matches();
		}

	l_Message = "Looking for open invoice related to accident ";
	if (l_ValidAccidentNumber) l_Message += p_AccidentNumber + " ";
	if (p_AccidentDate != null)   l_Message+= "happening on " + m_DateFormat.format(p_AccidentDate);
	 
	this.log (Level.INFO,l_Message);
	
	if (p_AccidentDate != null)
		{
		if (l_ValidAccidentNumber)
			{
			l_Invoices = this.getInvoicesByAccident(p_Patient, p_AccidentNumber, p_AccidentDate);
			}
		else
			{
			l_Invoices = this.getInvoicesByAccidentDate (p_Patient, p_AccidentDate);
			}
		
		if (l_Invoices != null)
			{
			this.log(Level.INFO, "Found " + l_Invoices.size() + " related invoices!");
			
			l_InvoiceIterator = l_Invoices.iterator();
			l_Candidate       = null;
			
			while (l_InvoiceIterator.hasNext() && (l_Candidate == null))
				{
				l_Invoice = l_InvoiceIterator.next();
				if (!l_Invoice.isLocked()) l_Candidate = l_Invoice;
				}
			}		
		}

	if (l_Candidate == null)
		{
		this.log(Level.INFO, "No related invoices available!");
		return null;
		}
	
	this.log(Level.INFO, "Invoice " + l_Candidate.formatInvoiceNumber(Invoice.c_LongFormat, true) + " is still open and can be used!");
	
	if (p_DateOfInterest != null)
		{
		l_FirstOfInvoiceMonth = this.getFirstOfMonth (l_Candidate.getInvoiceDate());
							
		if (l_FirstOfInvoiceMonth.before (this.getFirstOfMonth (p_DateOfInterest)))
			{
			this.log (Level.INFO, "Related Work Accident Invoice is from previous month! Can't use it!");					
			l_Candidate = null;
			}								
		}
	
	return l_Candidate;
	}

//---------------------------------------------------------------------------
//===========================================================================
//= Prescription Related methods
//===========================================================================
//---------------------------------------------------------------------------

//private HospitalPrescription deletePrescriptionPages (HospitalPrescription p_Prescription)
//	{
//	Set <HospitalPrescriptionPage>		l_Pages;
//	Iterator <HospitalPrescriptionPage>	l_PageIterator;
//	HospitalPrescriptionPage			l_Page;
//	
//	if (p_Prescription == null) return p_Prescription;
//	
//	l_Pages = p_Prescription.getPages();
//	if (l_Pages != null)
//		{
//		l_PageIterator = l_Pages.iterator();
//		while (l_PageIterator.hasNext())
//			{
//			l_Page = l_PageIterator.next();
//			this.deleteHospitalPrescriptionPage (l_Page);
//			}
//		}
//	
//	l_Pages = new LinkedHashSet <HospitalPrescriptionPage> ();
//	
//	p_Prescription.setPages(l_Pages);
//	
//	return p_Prescription;
//	}

//---------------------------------------------------------------------------
/**
 * The setHospitalPrescriptionPage either adds the specified prescription page
 * to the specified set of pages or updates an already existing page. The decision
 * whether to add or to update is taken by analyzing the specified prescription
 * page's scan number and page number. If a page exists in the specified set
 * with the same scan and page number, then the matching page's content will
 * be set to the specified page's content, i.e. an update occurs. If the
 * set doesn't contain a page with the specified scan and page number, then
 * the specified page will be added to the specified page, i.e. an add occurs.
 * @param p_Pages specifies the original set of prescription pages.
 * @param p_Page specifies the <i>new</i> page to either add or use to update
 * an already existing page.
 * @return the update set of prescription pages
 */

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

private Set <HospitalPrescriptionPage> setHospitalPrescriptionPage (Set <HospitalPrescriptionPage> p_Pages, HospitalPrescriptionPage p_Page)
	{
	Iterator <HospitalPrescriptionPage> l_PageIterator;
	HospitalPrescriptionPage			l_Page;
	HospitalPrescriptionPage			l_ExistingPage = null;
	
	Integer								l_PageNumber;
	String								l_ScanNumber;
	
	if (p_Page == null) return p_Pages;
	
	l_PageNumber = (p_Page.getPageNumber() != null)?p_Page.getPageNumber():Integer.valueOf(0);
	l_ScanNumber = (p_Page.getScanNumber() != null)?p_Page.getScanNumber():"n/a";
	
	this.log (Level.INFO, "Looking for page " + l_PageNumber + " of scan with number " + l_ScanNumber);

	l_PageIterator = p_Pages.iterator();
	while (l_PageIterator.hasNext() && (l_ExistingPage == null))
		{
		l_Page = l_PageIterator.next ();
			
		if (    (l_Page.getScanNumber() != null) && (l_Page.getScanNumber().equals(l_ScanNumber))
			 && (l_Page.getPageNumber() != null) && (l_Page.getPageNumber().equals(l_PageNumber)))
			l_ExistingPage = l_Page;
		}
		
	if (l_ExistingPage == null) 
		{
		this.log (Level.INFO, "Doesn't exist yet! Adding new page!");		
		p_Pages.add(p_Page);
		}
	else
		{
		this.log (Level.INFO, "Exists already! Replacing content!");		
		l_ExistingPage.setContent(p_Page.getContent());
		}
		
	return p_Pages;
	}

//---------------------------------------------------------------------------
/**
 * The method checks whether a prescription was attached to the specified order.
 * If yes, the method checks next whether the attached prescription already exists
 * in the GECAMed database. If yes, then the already existing prescription object
 * will be returned, otherwise, a new one will be created.
 * @param p_Order specifies the HL7Order to get attached prescription from, 
 * if available.
 * @return A prescription matching the specified order.
 */
//---------------------------------------------------------------------------

private HospitalPrescription getPrescriptionFromHL7Order (HL7Order p_Order) throws Exception
	{
	HospitalPrescription					l_Prescription = null;
	String									l_OrderNumber;
	
	if (p_Order == null) return null;
	
	l_OrderNumber = p_Order.getFillerOrderNumber();
	
	if (l_OrderNumber == null) return null;
	
	//========================================================================
	//= First Step consists in looking for an already existing prescription for
	//= the given order number. If none can be found, than we're assuming it 
	//= hasn't been created yet and we're creating a new one. 
	//========================================================================
	
	this.log (Level.INFO, "Looking for Prescription with Order Number " + l_OrderNumber);
	l_Prescription = this.getPrescriptionByOrderNumber(l_OrderNumber);
	if (l_Prescription == null)
		{
		this.log (Level.INFO, "Not found! Creating new one!");
		
		l_Prescription = new HospitalPrescription ();
		l_Prescription.setOrderNumber (l_OrderNumber);
		l_Prescription.setDate (p_Order.getServiceStart());
		
		l_Prescription = this.savePrescription (l_Prescription);	
		}	
	
	return l_Prescription;
	}

//---------------------------------------------------------------------------
/**
 * The setPrescriptionPagesFromHL7Order method attempts to extract the
 * prescription pages contained in the HL7 Order and attaches them to
 * the specified prescription.
 * @param p_Prescription specifies the prescription to attach the
 * prescription pages to.
 * @param p_Order specifies the HL7 Order containing the received prescription
 * pages.
 * @return the updated prescription.
 * @see the method relies on the setHospitalPrescriptionPage method to
 * attach the prescription pages.
 */
//---------------------------------------------------------------------------

private HospitalPrescription setPrescriptionPagesFromHL7Order (HospitalPrescription p_Prescription, HL7Order p_Order) throws Exception
	{
	Collection <HL7Prescription> 			l_HL7Prescriptions;
	Iterator <HL7Prescription>				l_HL7PrescriptionIterator;
	HL7Prescription							l_HL7Prescription;
	
	Set <HospitalPrescriptionPage>			l_PrescriptionPages;
	HospitalPrescriptionPage				l_Page;

	if ((p_Prescription == null) || (p_Order == null)) return p_Prescription;
	
	l_PrescriptionPages = p_Prescription.getPages();
	if (l_PrescriptionPages == null) l_PrescriptionPages = new LinkedHashSet <HospitalPrescriptionPage> ();
	
	//========================================================================
	//= First step consists in interating over the scans that came with the
	//= order.
	//========================================================================
	
	l_HL7Prescriptions = this.getHL7PrescriptionsByMessageId (p_Order.getMessageId());
	if (l_HL7Prescriptions != null)
		{
		l_HL7PrescriptionIterator = l_HL7Prescriptions.iterator();
		while (l_HL7PrescriptionIterator.hasNext())
			{
			l_HL7Prescription = l_HL7PrescriptionIterator.next();
			
			if (l_HL7Prescription.getContentType().equals(HL7Prescription.c_EncodedData))
				{
				l_Page = new HospitalPrescriptionPage ();	
				l_Page.setScanNumber(l_HL7Prescription.getScanNumber());	
				l_Page.setPageNumber(l_HL7Prescription.getPageNumber());	
				l_Page.setContent(l_HL7Prescription.getScan());
			
				l_PrescriptionPages = this.setHospitalPrescriptionPage (l_PrescriptionPages, l_Page);
				}
			}
		
		p_Prescription.setPages (l_PrescriptionPages);
		p_Prescription = this.savePrescription(p_Prescription);		
		}
	
	return p_Prescription;
	}

//---------------------------------------------------------------------------
//===========================================================================
//= Passage Related methods
//===========================================================================
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
/**
 * The newActFromHL7Passage method creates a new act using data contained in
 * the specified passage. The method first of all attempts to lookup a rate
 * matching the treatment UCM code of the specified passage. if a matching rate
 * could be found, an new act is being created and initialized using data
 * contained in the specified passage.
 * @param p_Passage specifies the HL7Passage object holding the data relative
 * to a particular passage
 * @param p_Physician specifies the performing physician for the new Act
 * to a particular passage
 * @return An Act reflecting the specified passage if specified UCM code exists
 * as a rate, <code>NULL</code> otherwise.
 */
//---------------------------------------------------------------------------

private Act newActFromHL7Passage (HL7Passage p_Passage, Physician p_Physician) throws Exception
	{
	Act		l_Act		  = null;
	Integer	l_PhysicianId = null;
	
	l_Act = new Act ();
	l_Act = this.setActFromHL7Passage (l_Act, p_Passage);
	l_Act.setChanges(Act.CHANGED_USER);
	l_PhysicianId = (p_Physician != null)?p_Physician.getId():null;
	l_Act.setPhysicianId (l_PhysicianId);
	
	return l_Act;
	}

//---------------------------------------------------------------------------
/**
 * The setActFromHL7Passage method initializes the specified Act using data 
 * contained in the specified passage. A serie of perliminary checks are performed
 * before hand though. First check consists in making sure specified passage
 * actualy contains a treatment code. This code is required to lookup the proper
 * rate for the act. If no treatment code can be found, the method aborts. 
 * Second check consists in making sure that specified passage contains
 * an accession number. If none is found, the method also aborts. Last preliminary 
 * check consists in making sure that the accession number (if any) of the specified
 * act matches the one in the specified passage. No match will also result in an
 * abort. If all these checks could be performed successfully, the method first of all 
 * attempt to lookup a rate matching the treatment UCM code of the specified passage. 
 * if a matching rate could be found, an the specified act is being initialized using 
 * data contained in the specified passage.
 * @param p_Act specifies the Act to be initialized from passage data.
 * @param p_Passage specifies the HL7Passage object holding the data relative
 * to a particular passage
 * @return The properly intialized Act if method was successful. In case of failure
 * or abort, the original Act will be returned unscathed.
 */
//---------------------------------------------------------------------------

private Act setActFromHL7Passage (Act p_Act, HL7Passage p_Passage) throws Exception
	{
	Rate			l_Rate	= null;
	Matcher			l_CodeMatcher;
	
	if ((p_Act == null) || (p_Passage == null)) return p_Act;
	
	if (p_Passage.getTreatmentCode() == null) 
		{
		this.log (Level.WARN, "Rejecting Passage without treatment code! Accession Number was " + p_Passage.getAccessionNumber());
		return p_Act;
		}
	
	if (p_Passage.getAccessionNumber() == null)
		{
		this.log (Level.WARN, "Rejecting Passage without Accession Number!");
		return p_Act;	
		}
	
	if (	  (p_Act.getAccessionNumber() != null) 
		 &&   (p_Act.getAccessionNumber().length() > 0)
		 && ! (p_Act.getAccessionNumber().equals (p_Passage.getAccessionNumber())) )
		{
		this.log (Level.WARN, "Accession Number of specified Act and Passage DO NOT match!");
		return p_Act;	
		}
	
	l_CodeMatcher = c_HL7ExaminationCodePattern.matcher(p_Passage.getTreatmentCode());
	
	this.log (Level.INFO, "Looking for Act in Passage '" + p_Passage.getMessageId() + "'");
	
	if (l_CodeMatcher.matches()) 
		{
		this.log (Level.INFO, "Looking for Rate with code '" + l_CodeMatcher.group(1) + "'");
		
		l_Rate = this.getRateByCode(l_CodeMatcher.group(1));
		if (l_Rate != null)
			{
			this.log (Level.INFO, "Rate found! Creating Act for Accession Number " + p_Passage.getAccessionNumber());
			p_Act.setAccessionNumber(p_Passage.getAccessionNumber());
//			l_Rate.initializeAct(p_Act);
			p_Act.setPerformedDate(p_Passage.getPassageDate());
			p_Act = m_NomenclatureInterface.initializeAct(l_Rate.getCode(), p_Act);
			}
		else
			{
			this.log (Level.WARN, "Failed to match rate " + l_CodeMatcher.group(1));	
			}
		}
	
	return p_Act;
	}

//---------------------------------------------------------------------------
/**
 * The getPhysicianFromHL7Passage attempts to retrieve the interpreting
 * physician from the specified passage. First step consists in matching
 * HL7Passage interpreter property with a Regular Expression in order to
 * extract UCM code, Name and Firstname of physician. If UCM code could be
 * properly matched, we're using the code to lookup the physician entity
 * from our database. If a matching physician exists, it will be returned.
 * In case UCM code failed to match or was missing, the method attempts a
 * lookup via name and first name. If lookup is successful, the found
 * physician will be returned. If everything fails, i.e. no matching physician
 * could be found, the method returns NULL.
 * @param p_HL7Passage specifies the HL7Passage to retrieve the interpreting
 * physician from.
 * @return An existing Physician object from the database if lookup was successful,
 * <code>NULL</code> otherwise.
 */
//---------------------------------------------------------------------------

private Physician getPhysicianFromHL7Passage (HL7Passage p_HL7Passage) throws Exception
	{
	Matcher		l_InterpreterMatcher;
	Matcher		l_UCMCodeMatcher;
	
	String		l_UCMCode;
	String		l_Name;
	String		l_FirstName;
	
	Physician	l_Physician	= null;
	
	if ((p_HL7Passage == null) || (p_HL7Passage.getInterpreter()==null)) return null;

	l_InterpreterMatcher = c_HL7InterpreterPattern.matcher (p_HL7Passage.getInterpreter());
	if (l_InterpreterMatcher.matches())
		{
		l_UCMCode 	= l_InterpreterMatcher.group(1);
		l_Name 		= l_InterpreterMatcher.group(2);
		l_FirstName 	= l_InterpreterMatcher.group(3);
		
		if (l_UCMCode != null)
			{
			l_UCMCodeMatcher = c_HL7UCMCodePattern.matcher (l_UCMCode);
			if (l_UCMCodeMatcher.matches() && (l_UCMCodeMatcher.groupCount() == 2))
				{
				l_UCMCode = l_UCMCodeMatcher.group(1) + "-" + l_UCMCodeMatcher.group(2);
				l_Physician = m_PhysiciansByUCMCode.get(l_UCMCode);
				}
			}
		
		if (l_Physician == null)
			{
			l_Name = l_FirstName + " " + l_Name;
			l_Physician = m_PhysiciansByName.get(l_Name.toLowerCase());
			}
		}
	
	if (l_Physician == null)
		{
		this.log (Level.WARN, "Failed to match physician " + p_HL7Passage.getInterpreter());
		}
	
	return l_Physician;
	}

//---------------------------------------------------------------------------
/**
 * The newPassageFromHL7Passage method creates a new Passage Entity from the
 * specified HL7 Passage.
 * @param p_HL7Passage specifies the HL7Passage Entity whose data is to be used
 * to initialize the new Passage Entity.
 * @return the method returns a new Passage Entity, whose properties are initialized
 * with data from the specified HL7 Passage Entity.
 * @see setPassageFromHL7Passage from more information about which properties
 * are set.
 */
//---------------------------------------------------------------------------

private Passage newPassageFromHL7Passage (HL7Passage p_HL7Passage) throws Exception
	{
	Passage		l_NewPassage;
	
	l_NewPassage = new Passage ();
	
	l_NewPassage = this.setPassageFromHL7Passage(l_NewPassage, p_HL7Passage);

	return l_NewPassage;
	}

//---------------------------------------------------------------------------
/**
 * The setPassageFromHL7Passage intitializes the specified Passage Entity
 * with data from the specified HL7 Passage Entity
 * @param p_Passage specifies the Passage Entity to be initialized.
 * @param p_HL7Passage specifies the HL7 Passage Entity whose data should be
 * used to initialize the specified Passage Entity.
 * @return the specified Passage Entity whose properties have been initialized
 * with data from the specified HL7 Passage Entity. However, the original
 * Passage Entity will be returned AS IS in case HL7 Passage specifies a
 * physician which is unkown to the system.
 */
//---------------------------------------------------------------------------

private Passage setPassageFromHL7Passage (Passage p_Passage, HL7Passage p_HL7Passage) throws Exception
	{
	Physician	l_Physician;
	
	if ((p_Passage == null) || (p_HL7Passage == null)) return p_Passage;
	
	l_Physician = this.getPhysicianFromHL7Passage(p_HL7Passage);
	if (l_Physician != null)
		{
		p_Passage.setPhysicianId (l_Physician.getId());
		}
	else 
		{
		this.log (Level.WARN, "Received Passage for unkown physician! Original Passage will be left as is!");
		return p_Passage;
		}
	
	p_Passage.setAccessionNumber	(p_HL7Passage.getAccessionNumber());
	p_Passage.setDate				(p_HL7Passage.getPassageDate());

	p_Passage.setTechnicianName		(p_HL7Passage.getTechnician());
	p_Passage.setTreatmentName		(p_HL7Passage.getTreatmentName());
	p_Passage.setTreatUCMCode		(p_HL7Passage.getTreatmentCode());

	return p_Passage;
	}

//---------------------------------------------------------------------------
/**
 * The withdrawPassage method attempts to remove the specified Passage from
 * the specified set of hospitalisation periods. The method firt of all looks
 * for the hospitalisation period in the specified set which contains the
 * specified passage. If the specified set doesn't contain the affected
 * hospitalisation period, the method aborts.
 * After the proper hospitalisation period was found, the specified Passage
 * will be removed from it.
 * @param p_Periods specifies the set of hospitalisation periods to look for
 * the one that ought to contain the Passage to remove.
 * @param p_PasssageToWithdraw specifies the Passage to be withdrawn, i.e.
 * to be removed.
 * @result If the specified Passage could be removed, the method will return
 * the modified Set of Hospitalisation Periods. If the method fails to withdraw
 * the passage, the method returns the original set of periods.
 */
//---------------------------------------------------------------------------

private TreeSet <HospitalisationPeriod> withdrawPassage (TreeSet <HospitalisationPeriod> p_Periods, Passage p_PasssageToWithdraw) throws Exception
	{
	Iterator <HospitalisationPeriod> 	l_PeriodIterator			= null;
	HospitalisationPeriod 			 	l_Period					= null;
	
	Set <Passage>					 	l_Passages	= null;
	boolean								l_Found		= false;
	
	if ((p_Periods == null) || (p_PasssageToWithdraw == null)) return p_Periods;
	
	this.log (Level.INFO, "Looking for Hospitalisation Period containing Passage with Accession Number " + p_PasssageToWithdraw.getAccessionNumber());

	//========================================================================
	//= First of all we're iterating over specified hospitalisation periods
	//= to find the one the specified passage is part of. If none can be found
	//= we're aborting withdrawal.
	//========================================================================
	
	l_PeriodIterator = p_Periods.iterator();
	while (l_PeriodIterator.hasNext() && !l_Found) 
		{
		l_Period = l_PeriodIterator.next();
		l_Found = (l_Period.getId().equals(p_PasssageToWithdraw.getHospperiodId()));
		}	
		
	if (l_Found == false) return p_Periods;	
	
	//========================================================================
	//= Once the hospitalisation period corresponding to the specified passage
	//= has been found, we're going to remove it first of all from set of
	//= periods. This operation is necessary as changing an element retrieved
	//= from a set does not necessarily change the element in the set. Next
	//= we're going to remove the specified passage from the hospitalisation
	//= periods set of passages.
	//========================================================================

	p_Periods.remove (l_Period);
	
	l_Passages = l_Period.getPassages ();
	l_Passages.remove(p_PasssageToWithdraw);
	this.deletePassage(p_PasssageToWithdraw);
	l_Period.setPassages(l_Passages);
	
	p_Periods.add (l_Period);	
	
	return p_Periods;
	}

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

private Collection <Invoice> withdrawAct (Collection <Invoice> p_Invoices, Act p_ActToWithdraw)
	{
	Iterator <Invoice> 				 l_InvoiceIterator;
	Invoice 			 			 l_Invoice	= null;
	Set <Act>						 l_Acts		= null;
	
	boolean							 l_Found = false;
	
	this.log (Level.INFO, "Looking for Act with Accession Number " + p_ActToWithdraw.getAccessionNumber());

	l_InvoiceIterator = p_Invoices.iterator();
	while (l_InvoiceIterator.hasNext() && !l_Found) 
		{
		l_Invoice = l_InvoiceIterator.next();
		l_Found   = (l_Invoice.getId().equals(p_ActToWithdraw.getInvoiceId()));
		}
		
	if (l_Found == false) return p_Invoices;
	
	if (l_Invoice.isLocked())
		{
		this.log (Level.WARN, "Act with Accession Number " + p_ActToWithdraw.getAccessionNumber() +
                				 "could NOT be withdrawn. Act is most likely on an invoice which" + 
                				 "is no longer open!");
		return p_Invoices;
		}
	
	p_Invoices.remove(l_Invoice);
	
	this.log (Level.INFO, "Act Found! Withdrawing it now!");

	l_Acts = l_Invoice.getActs();
	l_Acts.remove (p_ActToWithdraw);
	l_Invoice.setActs (l_Acts);
	this.deleteAct (p_ActToWithdraw);
	
	l_Invoice = this.applyRules(l_Invoice);
	p_Invoices.add(l_Invoice);

//	if (l_Acts.size() > 0)
//		{
//		l_Invoice = this.applyRules(l_Invoice);
//		p_Invoices.add(l_Invoice);
//		}
//	else 
//		{
//		this.log (Level.INFO, "No Acts left on Invoice! Deleting Invoice with ID " + l_Invoice.getId());
//		this.deleteInvoice(l_Invoice);
//		}
	
	return p_Invoices;
	}

//---------------------------------------------------------------------------
/**
 * the updateInvoicesFromOrder method does a couple of things. The method
 * first of all looks for a hospitalisation matching the Visit ID specified
 * by the HL7 Order. If the specified hospitalisation exists, the method
 * retrieves all hospitalisation periods for this hospitalisation as well
 * as all invoices related to it. As one HL7 Order may refer to more than
 * one HL7 Passage, the method iterates over all of them and trying to do
 * the following for each one of them:
 * 1.) Locate the proper hospitalisation period based on performed date and
 * time of act.
 * 2.) Create a new passage in database, and add it to corresponding hospitalisation
 * period
 * 3.) Locate the proper invoice based on performed date and time as well as
 * hospitalisation class of act. If none exists, a new one will be created.
 * 4.) Create new act and add it to corresponding invoice.
 */
//---------------------------------------------------------------------------

private Collection <Invoice> updateInvoicesFromOrder (Patient p_Patient, HL7Order p_Order) throws Exception
	{
	Collection	<HL7Passage>			l_HL7Passages				= null;
	Iterator	<HL7Passage>			l_HL7PassageIterator		= null;	
	HL7Passage							l_HL7Passage				= null;
	
	Hashtable <String,Invoice>			l_InvoiceLookup				= null;
	Collection <Invoice>				l_Invoices					= null;
	Invoice								l_Invoice;

	Act									l_ObsoleteAct;
	Act									l_NewAct;
	
	Physician							l_Physician;
	Hospitalisation						l_Hospitalisation;
	
	Passage								l_ObsoletePassage;
	Passage								l_NewPassage;
	
	TreeSet <HospitalisationPeriod> 	l_HospitalisationPeriods	= null;
	Iterator <HospitalisationPeriod>	l_PeriodIterator;
	HospitalisationPeriod				l_FirstInPatientPeriod		= null;
	
	HospitalisationPeriod				l_Period					= null;
	HospitalisationClass				l_Class						= null;
	HospitalPrescription				l_Prescription				= null;
	
	//========================================================================
	//= Fetch Hospitalisation specified by Visit ID.
	//========================================================================
	
	this.log (Level.INFO, "Creating Invoices for Visit " + p_Order.getVisitId().toString());
	
	l_Hospitalisation = this.getHospitalisationByVisitId (p_Order.getVisitId().toString());
	if (l_Hospitalisation != null)
		{	
		//========================================================================
		//= Fetch all Invoices already exisiting for this Hospitalisation. To simplify
		//= lookup, we're going to build a lookup table with these invoices. Keys 
		//= generate by Invoice's generateInvoiceKey method will be used to lookup
		//= invoices.
		//========================================================================

		l_Invoices = this.getInvoicesByHospitalisation (l_Hospitalisation);
		l_InvoiceLookup = this.buildInvoiceLookupTable (l_Invoices);
		
		//========================================================================
		//= Next we're going to fetch all hospitalisation periods for current
		//= hospitalisation. Putting those periods into a new TreeSet ensures that
		//= hospitalisation periods are sorted in chronological order, i.e earliest
		//= period first. Keeping them in sorted order is necessary to determine
		//= the first period as in patient, if any.
		//========================================================================
		
		l_HospitalisationPeriods = new TreeSet <HospitalisationPeriod> (l_Hospitalisation.getHospitalisationPeriods());
		l_FirstInPatientPeriod   = this.getFirstInPatientPeriod (l_HospitalisationPeriods);
		
		//========================================================================
		//= Next step consists in retrieving the prescription contained in the
		//= HL7 Order.
		//========================================================================

		l_Prescription = this.getPrescriptionFromHL7Order (p_Order);
		if (l_Prescription != null)
			{
			l_Prescription = this.setPrescriptionPagesFromHL7Order (l_Prescription, p_Order);
			}
		
		//========================================================================
		//= Next step consists in retrieving the actual passages. To avoid
		//= confusion, we'll herafter refer to a performed exam as a passage wheras
		//= the term act is reserved for the corresponding item on the invoice.
		//= For every retrieved passage, we're going to determine via its start
		//= date to which hospitalisation period it belongs to. Once this is done,
		//= we can assign the passage to the respective hospitalisation period.
		//= This step is necessary first of all to have a trace of the different
		//= passages in the patient's medical history and to know which invoice
		//= to put the corresponding act on.
		//========================================================================
		
		l_HL7Passages = this.getHL7PassagesByMessageId (p_Order.getMessageId());
		if (l_HL7Passages != null)
			{
			l_HL7PassageIterator = l_HL7Passages.iterator();
			while (l_HL7PassageIterator.hasNext())
				{
				l_HL7Passage = l_HL7PassageIterator.next();
				
				l_Physician  	= this.getPhysicianFromHL7Passage(l_HL7Passage);
				if (l_Physician == null)
					{
					this.log (Level.WARN, "Received Passage for unkown physician! Message Control ID was " + p_Order.getControlId());
					this.deleteHL7Passage(l_HL7Passage);
					continue;
					}
								
				//================================================================
				//= Check whether a passage or/and an act with the accession number
				//= specified by the HL7Passage already exists. If yes, then we 
				//= have to withdraw it, i.e. remove it first. This shouldn't happen,
				//= but, just wanted to make sure ;-)
				//================================================================
				
				l_ObsoletePassage = this.getPassageByAccessionNumber(l_HL7Passage.getAccessionNumber());
				if (l_ObsoletePassage != null) 
					l_HospitalisationPeriods = this.withdrawPassage (l_HospitalisationPeriods, l_ObsoletePassage); 
				
				//================================================================
				//= Look up the hospitalisation period the current passage was
				//= performed in. Set Presciption for that period and add passage
				//= to that period's list of performed passages.
				//================================================================

				l_Period	 = this.getHospitalisationPeriodByDate (l_HospitalisationPeriods,
																    l_FirstInPatientPeriod,
																    l_HL7Passage.getPassageDate());
				if (l_Period != null)
					{
					l_Period = this.setPrescriptionForHospitalisationPeriod (l_Prescription, l_Period);

					l_NewPassage = this.newPassageFromHL7Passage(l_HL7Passage);
					l_NewPassage.setPatientId (p_Patient.getId());	
					if (l_Prescription != null) l_NewPassage.setHospitalPrescriptionId(l_Prescription.getId());

					l_Period = this.addPassageToHospitalisationPeriod (l_NewPassage, l_Period);
					}
				else
					{
					this.log (Level.ERROR, "Could not find a matching hospitalisation period for passage date of " + l_HL7Passage.getPassageDate().toString() + 
											  "Putting Passage into Quarantine!");					
					this.quarantineHL7Passage (l_HL7Passage);
					this.quarantineHL7Order (p_Order);
					continue;
					}
					
				//================================================================
				//= Next we're going to take care of the billing portion. 
				//================================================================
		
				//================================================================
				//= First of all we're going to create a new act from data contained
				//= in HL7 passage. At the same time we're going to look for an already
				//= existing act with the same accession number. If such an act already
				//= exists, it might be obsolete.
				//================================================================
				
				l_NewAct 	  = this.newActFromHL7Passage (l_HL7Passage,l_Physician);
				l_ObsoleteAct = this.getActByAccessionNumber (l_HL7Passage.getAccessionNumber());
				
				if (this.areSameActs (l_ObsoleteAct,l_NewAct))
					{
					//============================================================
					//= If there is an act with the same accession number and it
					//= is absolutely the same as the new act, then no further action 
					//= is required.
					//============================================================
					
					this.log (Level.INFO, "No need to withdraw Act with Accession Number " + l_ObsoleteAct.getAccessionNumber());					
					this.deleteHL7Passage (l_HL7Passage);
					continue;
					}
				
				//================================================================
				//= Both acts are not absolutely the same, so we ave to process
				//= the new act. In case there was an act with a matching accession 
				//= number, this act is obsolete and we have to withdraw it first.
				//================================================================

				if (l_ObsoleteAct != null) l_Invoices = this.withdrawAct (l_Invoices, l_ObsoleteAct); 
									
				if ((l_Period != null) && (l_NewAct != null))
					{
					
					//=========================================================
					//= First step in processing new act consists in finding out
					//= which hospitalisation class is applicable. Knowing that 
					//= ambulatory invoices automatically become a first respectively 
					//= second class invoices if the patient is found to require 
					//= hospitalisation, we have to check for ambulatory period 
					//= whether that period wasn't merged with an hospitalisation 
					//= period. The l_FirstInPatientPeriod variable keeps track 
					//= of which hospitalisation class was merged with the ambulatory 
					//= class.
					//==========================================================
						
					l_Class = this.getInPatientHospitalisationClass (l_Period, l_FirstInPatientPeriod);	
					
					l_Invoice = null;
						
					//=========================================================
					//= In case new act is related to a work accident, we have
					//= to check whether there is an open invoice related to
					//= the same accident for this month. If yes, then we have 
					//= to put new act on that invoice
					//=========================================================
						
					if (l_Hospitalisation.getIsWorkAccident())
						{
						l_Invoice = this.getInvoiceForAccident (p_Patient, 
															   l_Hospitalisation.getWorkAccidentNumber(), 
															   l_Hospitalisation.getWorkAccidentDate(),
															   l_NewAct.getPerformedDate());
							
						if ((l_Invoice != null) && (!l_Invoices.contains(l_Invoice)))
							{
							l_InvoiceLookup = this.addToInvoiceLookupTable (l_InvoiceLookup, l_Invoice);
							}
						}

					//=========================================================
					//= If new act is not related to a work accident or there
					//= isn't an open invoice related to an accident, then we're
					//= going to look for open invoices for specified physician,
					//= hospitalisation class and date.
					//=========================================================

					if (l_Invoice == null)
						{
						l_Invoice = this.getInvoiceForPhysician (l_InvoiceLookup, 
															 	 l_Physician, 
															 	 p_Patient, 
															 	 l_Class,
															 	 l_NewAct.getPerformedDate());
						
						}
					
					if (l_Hospitalisation.getIsWorkAccident())
						{
						l_Invoice.setAccidentDate(l_Hospitalisation.getWorkAccidentDate());
						l_Invoice.setAccidentNumber(l_Hospitalisation.getWorkAccidentNumber());
						}
					if (l_Invoice.getHospitalisation() == null) l_Invoice.setHospitalisation (l_Hospitalisation);	
									
					l_Invoice = this.addActToInvoice(l_Invoice, l_NewAct);
					
					}
				this.deleteHL7Passage(l_HL7Passage);
				}
			}
		else
			{
			this.log (Level.ERROR, "Could not find a matching hospitalisation for order " + p_Order.getVisitId().toString() + 
									  "Putting Order into Quarantine!");					
			this.quarantineHL7Order (p_Order);
			}
		
		//========================================================================
		//= Step 3. Since most of the hospitalisation periods have been modified
		//= by attaching passages to them, we'll have to save them. We don't really
		//= bother which once were modified and which ones weren't. We're just
		//= saving them all.
		//========================================================================

		l_PeriodIterator = l_HospitalisationPeriods.iterator();
		while (l_PeriodIterator.hasNext()) 
			{
			l_Period = l_PeriodIterator.next();
			this.saveHospitalisationPeriod(l_Period);
			}
		
		if (l_InvoiceLookup != null) l_Invoices = l_InvoiceLookup.values();
		}
		
	return l_Invoices;
	}

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

private Invoice applyRules (Invoice p_Invoice)
	{
	this.log(Level.INFO, "Applying Rules to Invoice");
	
	m_RuleInterface = this.getRuleInterface();

	if (m_RuleInterface != null)
		{
		try {
			p_Invoice = m_RuleInterface.applyRules (p_Invoice, null);
			p_Invoice.monetize();
			}
		catch (Exception p_Exception) 
			{
			this.log (Level.FATAL, "Failed to apply Rules to Invoice",p_Exception);
			}
		}
	return p_Invoice;
	}

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

private void notifyClients (InvoiceChangeTracker p_Tracker)
	{
	Collection <Integer>	l_InvoiceIds;
	InvoiceChangeEvent		l_Event;
	
	this.log(Level.INFO, "Notifying clients about invoice change!");
	
	if (p_Tracker.containsUpdatedInvoiceIds())
		{
		l_InvoiceIds = p_Tracker.getUpdatedInvoiceIds();
		
		this.log(Level.INFO, l_InvoiceIds.size() + " Invoices have been updated!");
		
		l_Event = new InvoiceChangeEvent (null,InvoiceChangeEvent.c_InvoiceUpdated,l_InvoiceIds);
		this.publishInvoiceChange(l_Event);
		}
		
	if (p_Tracker.containsDeletedInvoiceIds())
		{
		l_InvoiceIds = p_Tracker.getUpdatedInvoiceIds();
		
		this.log(Level.INFO, l_InvoiceIds.size() + " Invoices have been deleted!");
		
		l_Event = new InvoiceChangeEvent (null,InvoiceChangeEvent.c_InvoiceDeleted,l_InvoiceIds);
		this.publishInvoiceChange(l_Event);
		}
	}
		
//---------------------------------------------------------------------------
	
private void publishInvoiceChange (InvoiceChangeEvent p_Event)	
	{
	TopicConnection	l_TopicConnection;
	TopicSession   	l_TopicSession;
	TopicPublisher 	l_Publisher;
	ObjectMessage	l_Message;

	try {
		l_TopicConnection = m_ConnectionFactory.createTopicConnection();
		l_TopicConnection.start();
		l_TopicSession = l_TopicConnection.createTopicSession (false, TopicSession.AUTO_ACKNOWLEDGE);  
		l_Publisher = l_TopicSession.createPublisher(m_BillingUpdateTopic);
        l_Message   = l_TopicSession.createObjectMessage (p_Event);
		l_Publisher.publish(l_Message);
		l_Publisher.close();
        l_TopicSession.close();
        
        l_TopicConnection.stop();
        l_TopicConnection.close();
		} 
	catch (JMSException p_Exception)       
        {
         this.log (Level.FATAL, "Failed to notify clients about patient data change",p_Exception);
        }
 	}

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

private InvoiceChangeTracker commitOrder (Patient p_Patient, HL7Order p_Order) throws Exception
	{
	Collection <Invoice>	l_Invoices;
	Iterator   <Invoice>	l_InvoiceIterator;
	Invoice					l_Invoice;
	InvoiceChangeTracker	l_Tracker;
	
	l_Tracker = new InvoiceChangeTracker ();
	
	l_Invoices = this.updateInvoicesFromOrder (p_Patient,p_Order);	
	if (l_Invoices != null)
		{
		l_InvoiceIterator = l_Invoices.iterator();
		while (l_InvoiceIterator.hasNext()) 
			{
			l_Invoice = l_InvoiceIterator.next();
			l_Invoice.monetize();
			if (l_Invoice.getAmount() > 0) 
				{
				l_Invoice = this.applyRules (l_Invoice);
				l_Invoice = this.saveInvoice (l_Invoice);
				l_Tracker.setAsUpdated(l_Invoice.getId());
				}
			}
		}

	return l_Tracker;
	}

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

private boolean updateOrder (Patient p_Patient, HL7Order p_Order) throws Exception
	{
	Collection	<HL7Passage>			l_HL7Passages				= null;
	Iterator	<HL7Passage>			l_HL7PassageIterator		= null;	
	HL7Passage							l_HL7Passage				= null;

	String								l_OrderNumber				= null;
	HospitalPrescription				l_Prescription				= null;
	Passage								l_PassageToUpdate			= null;
	Physician							l_Physician					= null;
	Integer								l_CommonPhysicianId			= null;
	
	Set <Act>							l_Acts						= null;
	Act									l_ActToUpdate				= null;
	Invoice								l_InvoiceToUpdate			= null;
	
	boolean								l_PatientDataChanged 		= false;
	
	if ((p_Patient == null) || (p_Order == null)) return false;
	
	//========================================================================
	//= First Step consists in looking for an already existing prescription for
	//= the given order number. If none can be found, then we're skipping
	//= prescription part of order
	//========================================================================

	l_OrderNumber = p_Order.getFillerOrderNumber();
	
	if (l_OrderNumber != null) 
		{
		this.log (Level.INFO, "Looking for Prescription with Order Number " + l_OrderNumber);
		l_Prescription = this.getPrescriptionByOrderNumber(l_OrderNumber);
		
		if (l_Prescription != null)
			{
			l_Prescription = this.setPrescriptionPagesFromHL7Order (l_Prescription, p_Order);
			l_PatientDataChanged = true;
			}
		else
			{
			this.log (Level.WARN, "Order refers to non-existing Prescription! Ignoring Prescription Part of Order!");
			}
		}
	else
		{
		this.log (Level.WARN, "Order doesn't specify an Order Number! Ignoring Prescription Part of Order!");
		}
		
	//========================================================================
	//= Next Step consists in iterating over all the passages attached to the
	//= specified order.
	//========================================================================
	
	l_HL7Passages = this.getHL7PassagesByMessageId (p_Order.getMessageId());
	if (l_HL7Passages != null)
		{
		l_HL7PassageIterator = l_HL7Passages.iterator();
		while (l_HL7PassageIterator.hasNext())
			{
			l_HL7Passage = l_HL7PassageIterator.next();
			
			//================================================================
			//= For every HL7 Passage, we're going to retrieve the physician
			//= who was specified for it. In case we fail to lookup the
			//= physician, this means we received a HL7 Passage for an unkown
			//= physician and we're skipping the HL7 Passage.
			//================================================================

			l_Physician  	= this.getPhysicianFromHL7Passage (l_HL7Passage);
			if (l_Physician == null)
				{
				this.log (Level.WARN, "Received Passage for unkown physician! Message Control ID was " + p_Order.getControlId());
				continue;
				}
			
			//================================================================
			//= Next step consists in looking up the already existing passage
			//= using the accession number specified by the HL7 Passage. If such
			//= a passage could be found, we're going to update the fetched
			//= passage using data specified by the HL7 Passage.
			//================================================================

			l_PassageToUpdate = this.getPassageByAccessionNumber (l_HL7Passage.getAccessionNumber());
			if (l_PassageToUpdate != null) 
				{
				l_PassageToUpdate = this.setPassageFromHL7Passage (l_PassageToUpdate, l_HL7Passage);
				l_PatientDataChanged = true;
				}
				
			//================================================================
			//= Next we're going to look up the already existing act by also
			//= using the accession number specified by the HL7 Passage. If
			//= such an act could be found, then we're checking wether the
			//= invoice this act is on isn't locked yet, i.e. can still be
			//= modified. If this is the case, then we're updating the act
			//= using data specified by the HL7 Passage, thus modifying the
			//= invoice to.
			//================================================================

			l_ActToUpdate = this.getActByAccessionNumber (l_HL7Passage.getAccessionNumber());	
			if (l_ActToUpdate != null)
				{
				l_InvoiceToUpdate = this.getInvoiceByAct (l_ActToUpdate);
				if ((l_InvoiceToUpdate != null) && (!l_InvoiceToUpdate.isLocked()))
					{
					l_Acts = l_InvoiceToUpdate.getActs();
					l_Acts.remove (l_ActToUpdate);
					l_ActToUpdate = this.setActFromHL7Passage(l_ActToUpdate, l_HL7Passage);					
					l_ActToUpdate.setPhysicianId (l_Physician.getId());
					l_Acts.add (l_ActToUpdate);
					l_InvoiceToUpdate.setActs(l_Acts);
					
					l_CommonPhysicianId = l_InvoiceToUpdate.getCommonPhysicianId();
					if (l_CommonPhysicianId.equals(l_Physician.getId()))
						{
						this.log (Level.INFO, "Invoice " + l_InvoiceToUpdate.formatInvoiceNumber(Invoice.c_LongFormat, true) + 
									 		 	" contains only acts of same physician. Setting physician of invoice also to intrepreting pyhsician!");
					
						l_InvoiceToUpdate.setPhysician (l_Physician);
						}
					
					l_InvoiceToUpdate = this.applyRules (l_InvoiceToUpdate);
					l_InvoiceToUpdate = this.saveInvoice (l_InvoiceToUpdate);
					l_PatientDataChanged = true;
					}
				}
			}
		}
	
	return l_PatientDataChanged;
	}

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

private InvoiceChangeTracker withdrawOrder (HL7Order p_Order) throws Exception
	{
	Collection	<HL7Passage>			l_HL7Passages				= null;
	Iterator	<HL7Passage>			l_HL7PassageIterator		= null;	
	HL7Passage							l_HL7Passage				= null;

	HospitalisationPeriod				l_Period					= null;	
	
	Set <Passage>						l_Passages					= null;
	
	Invoice								l_Invoice					= null;
	Set <Act>							l_InvoicedActs;
	Act									l_ActToWithdraw				= null;
	InvoiceChangeTracker				l_Tracker					= null;
	
	Passage								l_PassageToWithdraw			= null;
		
	l_HL7Passages = this.getHL7PassagesByMessageId (p_Order.getMessageId());
	if (l_HL7Passages != null)
		{
		l_Tracker = new InvoiceChangeTracker ();
		
		l_HL7PassageIterator = l_HL7Passages.iterator();
		while (l_HL7PassageIterator.hasNext())
			{
			l_HL7Passage = l_HL7PassageIterator.next();
	
			//================================================================
			//= Step 1: First of all we're going to try to locate an invoiced
			//= act with the same accession number. If such an act could be
			//= found, we're going to remove it from the invoice and we're
			//= applying the billing rules again.
			//================================================================
						
			this.log (Level.INFO, "Looking for already existing Act with Accession Number " + l_HL7Passage.getAccessionNumber());

			l_ActToWithdraw = this.getActByAccessionNumber(l_HL7Passage.getAccessionNumber());
			if (l_ActToWithdraw != null) 
				{
				this.log (Level.INFO, "Withdrawing Act with Accession Number " + l_HL7Passage.getAccessionNumber());
				
				l_Invoice = this.getInvoiceByAct(l_ActToWithdraw);
				if (!l_Invoice.isLocked())
					{
					l_InvoicedActs = l_Invoice.getActs();
					l_InvoicedActs.remove(l_ActToWithdraw);
					this.deleteAct(l_ActToWithdraw);
					
					if (l_InvoicedActs.size() > 0)
						{
						l_Invoice.setActs(l_InvoicedActs);			
						l_Invoice = this.applyRules(l_Invoice);
						l_Invoice = this.saveInvoice (l_Invoice);
						
						l_Tracker.setAsUpdated(l_Invoice.getId());
						}
					else 
						{
						this.log (Level.INFO, "No Acts left on invoice with ID " + l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true) +
			                 				 	 ". Deleting invoice!");

						
						this.deleteInvoice(l_Invoice);
						
						l_Tracker.setAsDeleted(l_Invoice.getId());
						}
					}
				else
					{
					this.log (Level.WARN, "Act with Accession Number " + l_ActToWithdraw.getAccessionNumber() +
			                 				 " could NOT be withdrawn. Act is most likely on an invoice which " + 
			                 			     "is no longer open!");
					}	
				}
			
			//================================================================
			//= Step 2: Next we're trying to locate the passage with the same
			//= accession number. If such a passage could be found (normally
			//= should be the case), then we're going to remove it from the
			//= patients medical history
			//================================================================

			this.log (Level.INFO, "Looking for already existing Passage with Accession Number " + l_HL7Passage.getAccessionNumber());

			l_PassageToWithdraw = this.getPassageByAccessionNumber(l_HL7Passage.getAccessionNumber());
			if (l_PassageToWithdraw != null) 
				{
				this.log (Level.INFO, "Withdrawing Passage with Accession Number " + l_HL7Passage.getAccessionNumber());

				l_Period = this.getHospitalisationPeriodById(l_PassageToWithdraw.getHospperiodId());
				if (l_Period != null)
					{
					l_Passages = l_Period.getPassages ();
					l_Passages.remove(l_PassageToWithdraw);
					l_Period.setPassages(l_Passages);
					this.saveHospitalisationPeriod(l_Period);
					}
																	
				this.deletePassage(l_PassageToWithdraw);
				}	
			}
		}
	
	return l_Tracker;
	}

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

private void processStatusChanged (HL7Order p_Order) throws Exception
	{
	Patient 				l_Patient = null;			
	int						l_StatusCode;	
	InvoiceChangeTracker	l_Tracker = null;
			
	if (p_Order.getPatientId() != null)
		{
		this.log(Level.INFO,"Looking for patient with RIS ID " + p_Order.getPatientId().toString());
		l_Patient = this.getPatientByRISid (p_Order.getPatientId().toString());
		}
		
	if ((l_Patient != null) && (l_Patient.isPersistent()))
		{
		l_StatusCode = p_Order.getStatusCode();
					
		switch (l_StatusCode)
			{
			//========================================================
			//= Case 1: Order Status set to COMPLETED
			//= In that case, we have to commit the order, i.e. add
			//= the contained passage to the patients medical history
			//= and add the corresponding act the invoice.
			//========================================================
			
			case HL7Order.c_Completed: 
				
				this.log(Level.INFO,"Order status is COMPLETED! Committing Order!");
				l_Tracker = this.commitOrder (l_Patient, p_Order);
				
				break;
			
			//========================================================
			//= Case 2: Order Status set to CANCELED
			//= If an order is canceled we have to withdraw the
			//= order.
			//========================================================

			case HL7Order.c_Canceled:
					
				this.log(Level.INFO,"Order status is CANCELED! Withdrawing Order!");
				l_Tracker = this.withdrawOrder (p_Order);
				break;

			//========================================================
			//= Case 3: Order Status set to SCHEDULED
			//= Scheduled Status may have two meanings. Either its
			//= an order for a new passage or it may be a request
			//= to modify an already existing one. In the first
			//= scenario we may ignore the order, in the later we
			//= have to withdraw the order.
			//========================================================

			case HL7Order.c_Scheduled:
				
				this.log(Level.INFO,"Order status is SCHEDULED! Withdrawing Order!");
				l_Tracker = this.withdrawOrder(p_Order);
				break;
			}						
		
		if ((l_Tracker != null) && (l_Tracker.containsUpdatedInvoiceIds() || l_Tracker.containsDeletedInvoiceIds())) 
			this.notifyClients(l_Tracker);
		}
	else 
		{
		this.log(Level.WARN,"Couldn't find patient with RIS ID " + p_Order.getPatientId().toString() +
							   ". Putting Order into Quarantine!");
		this.quarantineReferences(p_Order);
		this.quarantineHL7Order(p_Order);
		}	
	}

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

private void processUnsolicited (HL7Order p_Order) throws Exception
	{
	Patient 	l_Patient = null;			
	int			l_StatusCode;	
	boolean		l_PatientDataChanged = false;
			
	if (p_Order.getPatientId() != null)
		{
		this.log(Level.INFO,"Looking for patient with RIS ID " + p_Order.getPatientId().toString());
		l_Patient = this.getPatientByRISid (p_Order.getPatientId().toString());
		}

	if ((l_Patient != null) && (l_Patient.isPersistent()))
		{
		l_StatusCode = p_Order.getStatusCode();
				
		switch (l_StatusCode)
			{
			//========================================================
			//= Case 2.11: Order Status set to COMPLETED
			//= In that case, we have to commit the order, i.e. add
			//= the contained passage to the patients medical history
			//= and add the corresponding act the invoice.
			//========================================================
			
			case HL7Order.c_Completed:
				
//				this.log(Level.INFO,"Order status is COMPLETED! Updating Order!");
//				l_PatientDataChanged = this.updateOrder(l_Patient, p_Order);	
				break;
			}
		
//		if (l_PatientDataChanged) this.signalizePatientDataChange (l_Patient);
		}
	else 
		{
		this.log(Level.WARN,"Couldn't find patient with RIS ID " + p_Order.getPatientId().toString() +
							   ". Putting Order into Quarantine!");
		this.quarantineReferences(p_Order);
		this.quarantineHL7Order(p_Order);
		}
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body                                                              *
//***************************************************************************
//---------------------------------------------------------------------------

@SuppressWarnings("unchecked")
public Collection <HL7Order> getAllOrders() throws Exception 
	{
	Collection l_Orders;

	try	{	
		l_Orders = m_GECAMedEntityManager.createNamedQuery ("getAllHL7Orders")
										 .setMaxResults(HL7Bean.c_MaxBatchSize)	
										 .getResultList();
		}
	catch (NoResultException p_Exception)
		{
		l_Orders = null;
		}
	
	return l_Orders;
	}

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

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public void processOrder (HL7Order p_Order) throws Exception 
	{
	int						l_ControlCode;
	long					l_Time;
	
	if (p_Order == null) return;
	
	l_Time = System.currentTimeMillis();
	
	try	{
		this.log(Level.INFO, "Processing Order with MessageID " + p_Order.getMessageId() +
							    " and Control Code " + p_Order.getOrderControlCode());
	 	
		l_ControlCode = p_Order.getControlCode();

		switch (l_ControlCode)
			{
			//====================================================================
			//= 1. Process Orders whose control code is set to STATUS CHANGED (SC) 
			//====================================================================
			
			case HL7Order.c_StatusChanged :
			
				this.log(Level.INFO,"Received Status Changed Order!");
				this.processStatusChanged (p_Order);
				break;
			
			//====================================================================
			//= Process Orders whose control code is set to UNSOLICITED (XX) 
			//====================================================================
			
			case HL7Order.c_Unsolicited:
				
				this.log(Level.INFO,"Received Unsolicited Order!");		
				this.processUnsolicited (p_Order);
				break;
			}

		this.deleteReferences(p_Order);
		this.deleteHL7Order(p_Order);
	
		l_Time = System.currentTimeMillis() - l_Time;
		
		this.log(Level.INFO, "Processed HL7 Message " + p_Order + " took " + l_Time + " ms to complete!");
		

		Log logEntry = new Log(LogType.SYSTEM, "HL7 ORM IMPORT", "HL7", "Processed HL7 Message " + p_Order, l_Time);
		logManager.saveLog(logEntry);
		
		}
	catch (Exception p_Exception)
		{

		this.log(Level.ERROR, "Error while processing HL7 Message " + p_Order + ". Putting Order into Quarantine!",p_Exception);
		
		Log logEntry = new Log(LogType.SYSTEM, "HL7 ORM ERROR", "HL7", "Error while processing HL7 Message " + p_Order + ". Putting Order into Quarantine!", l_Time);
		logManager.saveLog(logEntry);
		
		this.quarantineReferences(p_Order);
		this.quarantineHL7Order(p_Order);
		}

	}

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

@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public void dailyTask () throws Exception 
	{
	Collection <Invoice> l_EligibleInvoices;
	Iterator <Invoice>	 l_InvoiceIterator;
	Invoice				 l_Invoice;
	long				 l_Time;
	
	l_Time = System.currentTimeMillis();
	this.log(Level.INFO, "Processing Daily Task now!");

	this.log(Level.INFO, "Looking for invoices eligible to apply third party payment rules!");
	
	l_EligibleInvoices = this.getInvoicesEligibleForThirdParty();
	if (l_EligibleInvoices != null)
		{
		this.log(Level.INFO, "Found " + l_EligibleInvoices.size() + " eligible invoices!");
		
		l_InvoiceIterator = l_EligibleInvoices.iterator();
		while (l_InvoiceIterator.hasNext())
			{
			l_Invoice = l_InvoiceIterator.next();
			if (l_Invoice.isLocked() == false)
				{
				this.log(Level.INFO, "Applying Rules to invoice " + l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true));
				l_Invoice = this.applyRules  (l_Invoice);
				l_Invoice = this.saveInvoice (l_Invoice);
				}
			}		
		}
	
	l_Time = System.currentTimeMillis() - l_Time;
	
	this.log(Level.INFO, "Processing of daily task took " + l_Time + " ms to complete!");
	}

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

}
