/******************************************************************************
 * The contents of this file are subject to the   Compiere License  Version 1.1
 * ("License"); You may not use this file except in compliance with the License
 * You may obtain a copy of the License at http://www.compiere.org/license.html
 * Software distributed under the License is distributed on an  "AS IS"  basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 * The Original Code is Compiere ERP & CRM Smart Business Solution. The Initial
 * Developer of the Original Code is Jorg Janke. Portions created by Jorg Janke
 * are Copyright (C) 1999-2005 Jorg Janke.
 * All parts are Copyright (C) 1999-2005 ComPiere, Inc.  All Rights Reserved.
 * Contributor(s): ______________________________________.
 *****************************************************************************/
package com.ampiere.grid.ed;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.model.GridTab;
import org.compiere.model.MCash;
import org.compiere.model.MCashLine;
import org.compiere.model.MInvoice;
import org.compiere.model.MOrder;
import org.compiere.model.MPayment;
import org.compiere.model.MRole;
import org.compiere.model.X_C_Order;
import org.compiere.process.DocAction;
import org.compiere.util.CLogger;
import org.compiere.util.Ctx;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.TimeUtil;
import org.compiere.util.ValueNamePair;

import com.ampiere.dto.CodeValue;

/**
 *	Display (and process) Payment Options.
 *  <pre>
 *  Payment Rule
 *  -B- Cash          (Date)          -> Cash Entry
 *  -P- Payment Term  (Term)
 *  -S- Check         (Routing, ..)   -> Payment Entry
 *  -K- CreditCard    (No)            -> Payment Entry
 *  -U- ACH Transfer  (Routing)       -> Payment Entry
 *
 *  When processing:
 *  - If an invoice is a S/K/U, but has no Payment Entry, it is changed to P
 *  - If an invoive is B and has no Cash Entry, it is created
 *  - An invoice is "Open" if it is "P" and no Payment
 *
 *  Entry:
 *  - If not processed, an invoice has no Cash or Payment entry
 *  - The entry is created, during "Online" and when Saving
 *
 *  Changes/Reversals:
 *  - existing Cash Entries are reversed and newly created
 *  - existing Payment Entries are not changed and then "hang there" and need to be allocated
 *  </pre>
 *
 * 	@author 	Jorg Janke
 * 	@version 	$Id: WPayment.java,v 1.1 2010/01/13 11:52:46 clmg Exp $
 */
public class WPayment {
	/**
	 *	Constructor
	 *
	 *	@param WindowNo	owning window
	 *  @param mTab     owning tab
	 *	@param button	button with access information
	 */
	public WPayment (int WindowNo, GridTab mTab, Ctx ctx)
	{
		m_WindowNo = WindowNo;
//		m_isSOTrx = "Y".equals(Env.getContext(ctx, WindowNo, "IsSOTrx"));
		m_isSOTrx = "Y".equals(ctx.getContext(WindowNo, "IsSOTrx"));
		m_mTab = mTab;
		try
		{
			jbInit(ctx);
			m_initOK = dynInit(ctx);	// Null Pointer if order/invoice not saved yet
		}
		catch(Exception ex)
		{
			log.log(Level.SEVERE, "WPayment", ex);
			m_initOK = false;
		}
	}	// WPayment

	/**	Window						*/
	private int                 m_WindowNo = 0;
	/**	Tab							*/
	private GridTab         	m_mTab;

	//	Data from Order/Invoice
	private String              m_DocStatus = null;
	/** Start Payment Rule          */
	private String				m_PaymentRule = "";
	/** Start Payment Term          */
	private int					m_C_PaymentTerm_ID = 0;
	/** Start Acct Date             */
	private Timestamp			m_DateAcct = null;
	/** Start Payment               */
	private int					m_C_Payment_ID = 0;
	private MPayment            m_mPayment = null;
	private MPayment            m_mPaymentOriginal = null;
	/** Start CashBook Line         */
	private int                 m_C_CashLine_ID = 0;
	private MCashLine			m_cashLine = null;
	/** Start CreditCard            */
	private String              m_CCType = "";
	/** Start Bank Account			*/
	private int					m_C_BankAccount_ID = 0;
	/** Start CashBook              */
	private int                 m_C_CashBook_ID = 0;

	/** Is SOTrx					*/
	private boolean				m_isSOTrx = true;

	/** Invoice Currency              */
	private int	 				m_C_Currency_ID = 0;
//	private int                 m_AD_Client_ID = 0;
	private int                 m_AD_Org_ID = 0;
	private int                 m_C_BPartner_ID = 0;
	private BigDecimal			m_Amount = Env.ZERO;	//	Payment Amount
	//
	private boolean 			m_initOK = false;
	/** Only allow changing Rule        */
	private boolean             m_onlyRule = false;
//	private DecimalFormat 		m_Format = DisplayType.getNumberFormat(DisplayType.Amount);
	private static Hashtable<Integer, CodeValue> s_Currencies = null;	//	EMU Currencies

	private boolean				m_needSave = false;
	/**	Logger			*/
	private static CLogger log = CLogger.getCLogger(WPayment.class);

	//
//	private String paymentLabel = "";
	private ArrayList<CodeValue> paymentCombo = new ArrayList<CodeValue>();
	private String paymentComboSelected = "";

//	private String kTypeLabel = "";
	private ArrayList<CodeValue> kTypeCombo = new ArrayList<CodeValue>();
	private String kTypeComboSelected = "";
//	private String kNumnerLabel = "";
	private String kNumberField = "";
//	private String kExpLabel = "";
	private String kExpField = "";
//	private String kApprovalLabel = "";
//	private String kApprovalField = "";

//	private String tAccountLabel = "";
	private ArrayList<CodeValue> tAccountCombo = new ArrayList<CodeValue>();

//	private String sNumberLabel = "";
	private String sNumberField = "";
//	private String sRoutingLabel = "";
	private String sRoutingField = "";
//	private String sCurrencyLabel = "";
	private ArrayList<CodeValue> sCurrencyCombo = new ArrayList<CodeValue>();
//	private String sCurrencyComboSelected = "";
//	private String bCurrencyLabel = "";
	private ArrayList<CodeValue> bCurrencyCombo = new ArrayList<CodeValue>();
//	private String bCurrencyComboSelected = "";

//	private String pTermLabel = "";
	private ArrayList<CodeValue> pTermCombo = new ArrayList<CodeValue>();
	private String pTermComboSelected = "";
//	private String bAmountLabel = "";
//	private String bAmountField = "";
//	private String sAmountLabel = "";
//	private String sAmountField = "";
//	private String bDateLabel = "";
	private String sCheckField = "";
//	private String sCheckLabel = "";
//	private String kOnline = "";
//	private String sOnline = "";
//	private String sBankAccountLabel = "";
	private ArrayList<CodeValue> sBankAccountCombo = new ArrayList<CodeValue>();
	private String sBankAccountComboSelected = "";
//	private String bCashBookLabel = "";
	private ArrayList<CodeValue> bCashBookCombo = new ArrayList<CodeValue>();
	private String bCashBookComboSelected = "";
//	private String tOnline = "";
//	private String kStatus = "";
	private String tRoutingField = "";
	private String tNumberField = "";
//	private String tStatus = "";
//	private String tRoutingText = "";
//	private String tNumberText = "";
//	private String sStatus = "";

//	private String bDateFieldLabel = "";
//	private String bDateField = "";

	/**
	 *	Static Init
	 *  @throws Exception
	 */
	private void jbInit(Ctx ctx) throws Exception
	{
/*
		bDateFieldLabel = Msg.translate(ctx, "DateAcct");

		paymentLabel = Msg.translate(ctx, "PaymentRule");

		kTypeLabel = Msg.translate(ctx, "CreditCardType");
		kNumnerLabel = Msg.translate(ctx, "CreditCardNumber");
		kExpLabel = Msg.getMsg(ctx, "Expires");
		kApprovalLabel = Msg.translate(ctx, "VoiceAuthCode");
		kOnline = Msg.translate(ctx, "Online");
		kStatus = " ";

		tAccountLabel = Msg.translate(ctx, "C_BP_BankAccount_ID");
		tRoutingText = Msg.translate(ctx, "RoutingNo");
		tNumberText = Msg.translate(ctx, "AccountNo");
		tOnline = Msg.translate(ctx, "Online");
		tStatus = " ";

		sBankAccountLabel = Msg.translate(ctx, "C_BankAccount_ID");
		sAmountLabel = Msg.translate(ctx, "Amount");
		sAmountField = "";
		sRoutingLabel = Msg.translate(ctx, "RoutingNo");
		sNumberLabel = Msg.translate(ctx, "AccountNo");
		sCheckLabel = Msg.translate(ctx, "CheckNo");
		sCurrencyLabel = Msg.translate(ctx, "C_Currency_ID");
		sStatus = " ";
		sOnline = Msg.translate(ctx, "Online");

		pTermLabel = Msg.translate(ctx, "C_PaymentTerm_ID");

		bCashBookLabel = Msg.translate(ctx, "C_CashBook_ID");
		bCurrencyLabel = Msg.translate(ctx, "C_Currency_ID");
		bAmountLabel = Msg.translate(ctx, "Amount");
		bAmountField = "";
		bDateLabel = Msg.translate(ctx, "DateAcct");
*/
	}	//	jbInit
	
	/**************************************************************************
	 *	Dynamic Init.
	 *		B (Cash)		(Currency)
	 *		K (CreditCard)  Type, Number, Exp, Approval
	 *		L (DirectDebit)	BPartner_Bank
	 *		P (PaymentTerm)	PaymentTerm
	 *		S (Check)		(Currency) CheckNo, Routing
	 *
	 *	Currencies are shown, if member of EMU
	 *  @return true if init OK
	 *  @throws Exception
	 */
	private boolean dynInit (Ctx ctx) throws Exception
	{
		m_DocStatus = (String)m_mTab.getValue("DocStatus");
		log.config(m_DocStatus);

		if (m_mTab.getValue("C_BPartner_ID") == null)
		{
			log.log(Level.SEVERE, Msg.translate(ctx, "SaveErrorRowNotFound"));
			return false;
		}

		//	Is the Trx posted?
	//	String Posted = (String)m_mTab.getValue("Posted");
	//	if (Posted != null && Posted.equals("Y"))
	//		return false;

		//  DocStatus
		m_DocStatus = (String)m_mTab.getValue("DocStatus");
		if (m_DocStatus == null)
			m_DocStatus = "";
		//	Is the Trx closed?		Reversed / Voided / Cloased
		if (m_DocStatus.equals("RE") || m_DocStatus.equals("VO") || m_DocStatus.equals("CL"))
			return false;
		//  Document is not complete - allow to change the Payment Rule only
		if (m_DocStatus.equals("CO") || m_DocStatus.equals("WP") )
			m_onlyRule = false;
		else
			m_onlyRule = true;
		//	PO only  Rule
		if (!m_onlyRule		//	Only order has Warehouse
			&& !m_isSOTrx && m_mTab.getValue("M_Warehouse_ID") != null)
			m_onlyRule = true;

		//  Amount
		m_Amount = (BigDecimal)m_mTab.getValue("GrandTotal");
		if (!m_onlyRule && m_Amount.compareTo(Env.ZERO) == 0)
		{
			log.log(Level.SEVERE, Msg.translate(ctx, "PaymentZero"));
			return false;
		}

//		bAmountField = m_Format.format(m_Amount);
//		sAmountField = m_Format.format(m_Amount);

		/**
		 *	Get Data from Grid
		 */
/*
		m_AD_Client_ID = ((Integer)m_mTab.getValue("AD_Client_ID")).intValue();
		m_AD_Org_ID = ((Integer)m_mTab.getValue("AD_Org_ID")).intValue();
		m_C_BPartner_ID = ((Integer)m_mTab.getValue("C_BPartner_ID")).intValue();
		m_PaymentRule = (String)m_mTab.getValue("PaymentRule");
		m_C_Currency_ID = ((Integer)m_mTab.getValue("C_Currency_ID")).intValue();
		m_DateAcct = (Timestamp)m_mTab.getValue("DateAcct");
		if (m_mTab.getValue("C_PaymentTerm_ID") != null)
			m_C_PaymentTerm_ID = ((Integer)m_mTab.getValue("C_PaymentTerm_ID")).intValue();
		//  Existing Payment
		if (m_mTab.getValue("C_Payment_ID") != null)
		{
			m_C_Payment_ID = ((Integer)m_mTab.getValue("C_Payment_ID")).intValue();
			if (m_C_Payment_ID != 0)
			{
				m_mPayment = new MPayment(ctx, m_C_Payment_ID, null);
				m_mPaymentOriginal = new MPayment(ctx, m_C_Payment_ID, null);	//	full copy
				//  CreditCard
				m_CCType = m_mPayment.getCreditCardType();
				kNumberField = m_mPayment.getCreditCardNumber();
				kExpField = m_mPayment.getCreditCardExp(null);
				kApprovalField = m_mPayment.getVoiceAuthCode();
				kStatus = m_mPayment.getR_PnRef();
				//	if approved/paid, don't let it change
*/

/*
&&&&&&
				kTypeCombo.setReadWrite(!m_mPayment.isApproved());
				kNumberField.setReadWrite(!m_mPayment.isApproved());
				kExpField.setReadWrite(!m_mPayment.isApproved());
				kApprovalField.setReadWrite(!m_mPayment.isApproved());
				kOnline.setReadWrite(!m_mPayment.isApproved());
 */
/*
				//  Check
				m_C_BankAccount_ID = m_mPayment.getC_BankAccount_ID();
				sRoutingField = m_mPayment.getRoutingNo();
				sNumberField = m_mPayment.getAccountNo();
				sCheckField = m_mPayment.getCheckNo();
				sStatus = m_mPayment.getR_PnRef();
				//  Transfer
				tRoutingField = m_mPayment.getRoutingNo();
				tNumberField = m_mPayment.getAccountNo();
				tStatus = m_mPayment.getR_PnRef();
			}
		}
		if (m_mPayment == null)
		{
			m_mPayment = new MPayment (ctx, 0, null);
			m_mPayment.setAD_Org_ID(m_AD_Org_ID);
			m_mPayment.setAmount (m_C_Currency_ID, m_Amount);
		}

		//  Existing Cahbook entry
		m_cashLine = null;
		m_C_CashLine_ID = 0;
		if (m_mTab.getValue("C_CashLine_ID") != null)
		{
			m_C_CashLine_ID = ((Integer)m_mTab.getValue("C_CashLine_ID")).intValue();
			if (m_C_CashLine_ID == 0)
				m_cashLine = null;
			else
			{
				m_cashLine = new MCashLine (ctx, m_C_CashLine_ID, null);
				m_DateAcct = m_cashLine.getStatementDate();
			}
		}
*/
		//	Accounting Date
// &&&&&&
//		bDateField = m_DateAcct;

		if (s_Currencies == null) {
			loadCurrencies();
		}

		//	Is the currency an EMU currency?
		Integer C_Currency_ID = new Integer(m_C_Currency_ID);
		if (s_Currencies.containsKey(C_Currency_ID))
		{
			Enumeration en = s_Currencies.keys();
			while (en.hasMoreElements())
			{
				Object key = en.nextElement();
				bCurrencyCombo.add(s_Currencies.get(key));
				sCurrencyCombo.add(s_Currencies.get(key));
			}
			if (s_Currencies.get(C_Currency_ID) != null) {
//				sCurrencyComboSelected = s_Currencies.get(C_Currency_ID).getCode();
//				bCurrencyComboSelected = s_Currencies.get(C_Currency_ID).getCode();
			}
		}
		else	//	No EMU Currency
		{
/*
&&&&&&
			bCurrencyLabel.setVisible(false);	//	Cash
			bCurrencyCombo.setVisible(false);
			sCurrencyLabel.setVisible(false);	//	Check
			sCurrencyCombo.setVisible(false);
*/
		}

		/**
		 *	Payment Combo
		 */
		if (m_PaymentRule == null) {
			m_PaymentRule = "";
		}
		CodeValue vp = null;
		HashMap values = (HashMap)readReference(195);
		Object[] a = values.keySet().toArray();
		for (int i = 0; i < a.length; i++)
		{
			String PaymentRule = (String)a[i];
			CodeValue pp = new CodeValue(PaymentRule, (String)values.get(a[i]));
			paymentCombo.add(pp);
			if (PaymentRule.toString().equals(m_PaymentRule))	//	to select
				vp = pp;
		}

		//	Set PaymentRule
		if (vp != null) {
			paymentComboSelected = vp.getCode();
		}

		/**
		 * 	Load Payment Terms
		 */
		String SQL = MRole.getDefault().addAccessSQL(
			"SELECT C_PaymentTerm_ID, Name FROM C_PaymentTerm WHERE IsActive='Y' ORDER BY Name",
			"C_PaymentTerm", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);
		CodeValue kp = null;
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				int key = rs.getInt(1);
				String name = rs.getString(2);
				CodeValue pp = new CodeValue(Integer.toString(key), name);
				pTermCombo.add(pp);
				if (key == m_C_PaymentTerm_ID)
					kp = pp;
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException ept)
		{
			log.log(Level.SEVERE, SQL, ept);
		}
		//	Set Selection
		if (kp != null)
			pTermComboSelected = kp.getCode();

		/**
		 * 	Load Accounts
		 */
		SQL = "SELECT a.C_BP_BankAccount_ID, NVL(b.Name, ' ')||a.AccountNo AS Acct "
			+ "FROM C_BP_BankAccount a,C_Bank b "
			+ "WHERE C_BPartner_ID=? AND a.IsActive='Y'";
		kp = null;
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			pstmt.setInt(1, m_C_BPartner_ID);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				int key = rs.getInt(1);
				String name = rs.getString(2);
				CodeValue pp = new CodeValue(Integer.toString(key), name);
				tAccountCombo.add(pp);
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException eac)
		{
			log.log(Level.SEVERE, SQL, eac);
		}
		//	Set Selection
//		if (kp != null)
//			tAccountCombo.setSelectedItem(kp);

		/**
		 *	Load Credit Cards
		 */
		ValueNamePair[] ccs = m_mPayment.getCreditCards();
		String selVal = null;
		for (int i = 0; i < ccs.length; i++)
		{
			kTypeCombo.add(new CodeValue(ccs[i].getValue(), ccs[i].getName()));
			if (ccs[i].getValue().equals(m_CCType))
				selVal = ccs[i].getValue();
		}
		//	Set Selection
		if (selVal != null) {
			kTypeComboSelected = selVal;
		}

		/**
		 *  Load Bank Accounts
		 */
		SQL = MRole.getDefault().addAccessSQL(
			"SELECT C_BankAccount_ID, Name || ' ' || AccountNo, IsDefault "
			+ "FROM C_BankAccount ba"
			+ " INNER JOIN C_Bank b ON (ba.C_Bank_ID=b.C_Bank_ID) "
			+ "WHERE b.IsActive='Y'",
			"ba", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
		selVal = null;
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				int key = rs.getInt(1);
				String name = rs.getString(2);
				KeyNamePair pp = new KeyNamePair(key, name);
				sBankAccountCombo.add(new CodeValue(Integer.toString(pp.getKey()), pp.getName()));
				if (key == m_C_BankAccount_ID)
					selVal = Integer.toString(pp.getKey());
				if (kp == null && rs.getString(3).equals("Y"))    //  Default
					selVal = Integer.toString(pp.getKey());
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException ept)
		{
			log.log(Level.SEVERE, SQL, ept);
		}
		//	Set Selection
		if (selVal != null)
			sBankAccountComboSelected = selVal;


		/**
		 *  Load Cash Books
		 */
		SQL = MRole.getDefault().addAccessSQL(
			"SELECT C_CashBook_ID, Name, AD_Org_ID FROM C_CashBook WHERE IsActive='Y'",
			"C_CashBook", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);
		selVal = null;
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				int key = rs.getInt(1);
				String name = rs.getString(2);
				KeyNamePair pp = new KeyNamePair(key, name);
				bCashBookCombo.add(new CodeValue(Integer.toString(pp.getKey()), pp.getName()));
				if (key == m_C_CashBook_ID)
					selVal = Integer.toString(pp.getKey());
				if (kp == null && key == m_AD_Org_ID)       //  Default Org
					selVal = Integer.toString(pp.getKey());
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException epc)
		{
			log.log(Level.SEVERE, SQL, epc);
		}
		//	Set Selection
		if (selVal != null)
		{
			bCashBookComboSelected = selVal;
			if (m_C_CashBook_ID == 0) {
				m_C_CashBook_ID = Integer.parseInt(selVal);	//  set to default to avoid 'cashbook changed' message
			}
		}

		//
		return true;
	}	//	dynInit

	/**
	 *	Init OK to be able to make changes?
	 *  @return true if init OK
	 */
	public boolean isInitOK()
	{
		return m_initOK;
	}	//	isInitOK


	/**
	 *	Fill s_Currencies with EMU currencies
	 */
	private void loadCurrencies()
	{
		s_Currencies = new Hashtable<Integer, CodeValue>(12);	//	Currenly only 10+1
		String SQL = "SELECT C_Currency_ID, ISO_Code FROM C_Currency "
			+ "WHERE (IsEMUMember='Y' AND EMUEntryDate<SysDate) OR IsEuro='Y' "
			+ "ORDER BY 2";
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				int id = rs.getInt(1);
				String name = rs.getString(2);
				s_Currencies.put(id, new CodeValue(Integer.toString(id), name));
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException e)
		{
			log.log(Level.SEVERE, SQL, e);
		}
	}	//	loadCurrencies


	/**************************************************************************
	 *	Action Listener
	 *  @param e event
	 */
	public void actionPerformed()
	{
/*
&&&&&&
		//	Finish
		if (e.getActionCommand().equals(ConfirmPanel.A_OK))
		{
			if (checkMandatory())
			{
				saveChanges (); // cannot recover
				dispose ();
			}
		}
		else if (e.getActionCommand().equals(ConfirmPanel.A_CANCEL))
			dispose();

		//	Payment Method Change
		else if (e.getSource() == paymentCombo)
		{
			//	get selection
			ValueNamePair pp = (ValueNamePair)paymentCombo.getSelectedItem();
			if (pp != null)
			{
				String s = pp.getValue().toLowerCase() + "Panel";
				centerLayout.show(centerPanel, s);	//	switch to panel
			}
		}

		//	Check Currency change
		else if (e.getSource() == sCurrencyCombo)
		{
			KeyNamePair pp = (KeyNamePair)sCurrencyCombo.getSelectedItem();
			BigDecimal amt = MConversionRate.convert(ctx,
				m_Amount, m_C_Currency_ID, pp.getKey(), m_AD_Client_ID, m_AD_Org_ID);
			sAmountField.setText(m_Format.format(amt));
		}
		//	Cash Currency change
		else if (e.getSource() == bCurrencyCombo)
		{
			KeyNamePair pp = (KeyNamePair)bCurrencyCombo.getSelectedItem();
			BigDecimal amt = MConversionRate.convert(ctx,
				m_Amount, m_C_Currency_ID, pp.getKey(), m_AD_Client_ID, m_AD_Org_ID);
			bAmountField.setText(m_Format.format(amt));
		}

		//  Online
		else if (e.getSource() == kOnline || e.getSource() == sOnline || e.getSource() == tOnline)
			processOnline();
*/
	}	//	actionPerformed


	/**************************************************************************
	 *	Save Changes
	 *	@return true, if eindow can exit
	 */
	protected boolean saveChanges(Ctx ctx)
	{
		String newPaymentRule = paymentComboSelected;
		log.info("New Rule: " + newPaymentRule);

		//  only Payment Rule
		if (m_onlyRule)
		{
			if (!newPaymentRule.equals(m_PaymentRule))
				m_mTab.setValue("PaymentRule", newPaymentRule);
			return true;
		}

		//	New Values
		Timestamp newDateAcct = m_DateAcct;
		int newC_PaymentTerm_ID = m_C_PaymentTerm_ID;
		int newC_CashLine_ID = m_C_CashLine_ID;
		int newC_CashBook_ID = m_C_CashBook_ID;
		String newCCType = m_CCType;
		int newC_BankAccount_ID = 0;
		
		//	B (Cash)		(Currency)
		if (newPaymentRule.equals(X_C_Order.PAYMENTRULE_Cash))
		{
			String kp = bCashBookComboSelected;
			if (kp != null)
				newC_CashBook_ID = Integer.parseInt(kp);
// &&&&&&
//			newDateAcct = (Timestamp)bDateField.getValue();
		}

		//	K (CreditCard)  Type, Number, Exp, Approval
		else if (newPaymentRule.equals(X_C_Order.PAYMENTRULE_CreditCard))
		{
			String vp = kTypeComboSelected;
			if (vp != null)
				newCCType = vp;
		}

		//	T (Transfer)	BPartner_Bank
		else if (newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDeposit) 
			|| newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDebit) )
		{
//			tAccountCombo.getSelectedItem();
		}

		//	P (PaymentTerm)	PaymentTerm
		else if (newPaymentRule.equals(X_C_Order.PAYMENTRULE_OnCredit))
		{
			String kp = pTermComboSelected;
			if (kp != null)
				newC_PaymentTerm_ID = Integer.parseInt(kp);
		}

		//	S (Check)		(Currency) CheckNo, Routing
		else if (newPaymentRule.equals(X_C_Order.PAYMENTRULE_Check))
		{
		//	sCurrencyCombo.getSelectedItem();
			String kp = sBankAccountComboSelected;
			if (kp != null)
				newC_BankAccount_ID = Integer.parseInt(kp);
		}
		else {
			return false;
		}

		//  find Bank Account if not qualified yet
		if ("KTSD".indexOf(newPaymentRule) != -1 && newC_BankAccount_ID == 0)
		{
/*
			String tender = MPayment.TENDERTYPE_CreditCard;
			if (newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDeposit))
				tender = MPayment.TENDERTYPE_DirectDeposit;
			else if (newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDebit))
				tender = MPayment.TENDERTYPE_DirectDebit;
			else if (newPaymentRule.equals(MOrder.PAYMENTRULE_Check))
				tender = MPayment.TENDERTYPE_Check;
*/
		}

		/***********************
		 *  Changed PaymentRule
		 */
		if (!newPaymentRule.equals(m_PaymentRule))
		{
			log.fine("Changed PaymentRule: " + m_PaymentRule + " -> " + newPaymentRule);
			//  We had a CashBook Entry
			if (m_PaymentRule.equals(X_C_Order.PAYMENTRULE_Cash))
			{
				log.fine("Old Cash - " + m_cashLine);
				if (m_cashLine != null)
				{
					MCashLine cl = m_cashLine.createReversal();
					if (cl.save())
						log.config( "CashCancelled");
					else {
//						ADialog.error(m_WindowNo, this, "PaymentError", "CashNotCancelled");
						log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
					}
				}
				newC_CashLine_ID = 0;      //  reset
			}
			//  We had a change in Payment type (e.g. Check to CC)
			else if ("KTSD".indexOf(m_PaymentRule) != -1 && "KTSD".indexOf(newPaymentRule) != -1 && m_mPaymentOriginal != null)
			{
				log.fine("Old Payment(1) - " + m_mPaymentOriginal);
				m_mPaymentOriginal.setDocAction(DocAction.ACTION_Reverse_Correct);
				boolean ok = m_mPaymentOriginal.processIt(DocAction.ACTION_Reverse_Correct);
				m_mPaymentOriginal.save();
				if (ok)
					log.info( "Payment Canecelled - " + m_mPaymentOriginal);
				else
//					ADialog.error(m_WindowNo, this, "PaymentError", "PaymentNotCancelled " + m_mPaymentOriginal.getDocumentNo());
					log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
				m_mPayment.resetNew();
			}
			//	We had a Payment and something else (e.g. Check to Cash)
			else if ("KTSD".indexOf(m_PaymentRule) != -1 && "KTSD".indexOf(newPaymentRule) == -1)
			{
				log.fine("Old Payment(2) - " + m_mPaymentOriginal);
				if (m_mPaymentOriginal != null)
				{
					m_mPaymentOriginal.setDocAction(DocAction.ACTION_Reverse_Correct);
					boolean ok = m_mPaymentOriginal.processIt(DocAction.ACTION_Reverse_Correct);
					m_mPaymentOriginal.save();
					if (ok)        //  Cancel Payment
					{
						log.fine("PaymentCancelled " + m_mPayment.getDocumentNo ());
						m_mTab.getTableModel().dataSave(true);
						m_mPayment.resetNew();
						m_mPayment.setAmount(m_C_Currency_ID, m_Amount);
					}
					else
//						ADialog.error(m_WindowNo, this, "PaymentError", "PaymentNotCancelled " + m_mPayment.getDocumentNo());
						log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
				}
			}
		}

		//  Get Order and optionally Invoice
//		int C_Order_ID = Env.getContextAsInt(ctx, m_WindowNo, "C_Order_ID");
//		int C_Invoice_ID = Env.getContextAsInt(ctx, m_WindowNo, "C_Invoice_ID");
		int C_Order_ID = ctx.getContextAsInt(m_WindowNo, "C_Order_ID");
		int C_Invoice_ID = ctx.getContextAsInt(m_WindowNo, "C_Invoice_ID");
		if (C_Invoice_ID == 0 && m_DocStatus.equals("CO"))
			C_Invoice_ID = getInvoiceID (C_Order_ID);

		//  Amount sign negative, if ARC (Credit Memo) or API (AP Invoice)
		boolean negateAmt = false;
		MInvoice invoice = null;
		if (C_Invoice_ID != 0)
		{
			invoice = new MInvoice (ctx, C_Invoice_ID, null);
			negateAmt = invoice.isCreditMemo();
		}
		MOrder order = null;
		if (invoice == null && C_Order_ID != 0)
			order = new MOrder (ctx, C_Order_ID, null);
		BigDecimal payAmount = m_Amount;
		if (negateAmt)
			payAmount = m_Amount.negate();
		// Info
		log.config("C_Order_ID=" + C_Order_ID + ", C_Invoice_ID=" + C_Invoice_ID + ", NegateAmt=" + negateAmt);

		/***********************
		 *  CashBook
		 */
		if (newPaymentRule.equals(X_C_Order.PAYMENTRULE_Cash))
		{
			log.fine("Cash");
//			String description = (String)m_mTab.getValue("DocumentNo");

			if (C_Invoice_ID == 0 && order == null)
			{
				log.config("No Invoice!");
//				ADialog.error(m_WindowNo, this, "PaymentError", "CashNotCreated");
				log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
			}
			else
			{
				//  Changed Amount
				if (m_cashLine != null
					&& payAmount.compareTo(m_cashLine.getAmount()) != 0)
				{
					log.config("Changed CashBook Amount");
					m_cashLine.setAmount(payAmount);
					if (m_cashLine.save())
						log.config("CashAmt Changed");
				}
				//	Different Date/CashBook
				if (m_cashLine != null
					&& (newC_CashBook_ID != m_C_CashBook_ID 
						|| !TimeUtil.isSameDay(m_cashLine.getStatementDate(), newDateAcct)))
				{
					log.config("Changed CashBook/Date: " + m_C_CashBook_ID + "->" + newC_CashBook_ID);
					MCashLine reverse = m_cashLine.createReversal();
					if (!reverse.save())
//						ADialog.error(m_WindowNo, this, "PaymentError", "CashNotCancelled");
						log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
					m_cashLine = null;
				}
				
				//	Create new
				if (m_cashLine == null)
				{
					log.config("New CashBook");
					int C_Currency_ID = 0;
					if (invoice != null)
						C_Currency_ID = invoice.getC_Currency_ID();
					if (C_Currency_ID == 0 && order != null)
						C_Currency_ID = order.getC_Currency_ID();
					MCash cash = null;
					if (newC_CashBook_ID != 0)
						cash = MCash.get (ctx, newC_CashBook_ID, newDateAcct, null);
					else	//	Default
						cash = MCash.get (ctx, m_AD_Org_ID, newDateAcct, C_Currency_ID, null);
					if (cash == null || cash.get_ID() == 0)
//						ADialog.error(m_WindowNo, this, "PaymentError", "CashNotCreated");
						log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
					else
					{
						MCashLine cl = new MCashLine (cash);
						if (invoice != null)
							cl.setInvoice(invoice);
						if (order != null)
						{
							cl.setOrder(order, null);
							m_needSave = true;
						}
						if (cl.save())
							log.config("CashCreated");
						else
//							ADialog.error(m_WindowNo, this, "PaymentError", "CashNotCreated");
							log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "CashNotCancelled");
					}
				}
			}	//	have invoice
		}
		/***********************
		 *  Payments
		 */
		if ("KTSD".indexOf(newPaymentRule) != -1)
		{
			log.fine("Payment - " + newPaymentRule);
			//  Set Amount
			m_mPayment.setAmount(m_C_Currency_ID, payAmount);
			if (newPaymentRule.equals(MOrder.PAYMENTRULE_CreditCard))
			{
				m_mPayment.setCreditCard(MPayment.TRXTYPE_Sales, newCCType, kNumberField, "", kExpField);
				m_mPayment.setPaymentProcessor();
			}
			else if (newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDeposit)
				|| newPaymentRule.equals(MOrder.PAYMENTRULE_DirectDebit))
			{
				m_mPayment.setBankACH(newC_BankAccount_ID, m_isSOTrx, newPaymentRule, tRoutingField, tNumberField);
			}
			else if (newPaymentRule.equals(MOrder.PAYMENTRULE_Check))
			{
				m_mPayment.setBankCheck(newC_BankAccount_ID, m_isSOTrx, sRoutingField, sNumberField, sCheckField);
			}
			m_mPayment.setC_BPartner_ID(m_C_BPartner_ID);
			m_mPayment.setC_Invoice_ID(C_Invoice_ID);
			if (order != null)
			{
				m_mPayment.setC_Order_ID(C_Order_ID);
				m_needSave = true;
			}
			m_mPayment.setDateTrx(m_DateAcct);
			m_mPayment.setDateAcct(m_DateAcct);
			m_mPayment.save();
			
			//  Save/Post
			if (MPayment.DOCSTATUS_Drafted.equals(m_mPayment.getDocStatus()))
			{
				boolean ok = m_mPayment.processIt(DocAction.ACTION_Complete);
				m_mPayment.save();
				if (ok)
//					ADialog.info(m_WindowNo, this, "PaymentCreated", m_mPayment.getDocumentNo());
					log.log(Level.INFO, Msg.translate(ctx, "PaymentCreated"));
				else
//					ADialog.error(m_WindowNo, this, "PaymentError", "PaymentNotCreated");
					log.log(Level.SEVERE, Msg.translate(ctx, "PaymentError") + "PaymentNotCreated");
			}
			else
				log.fine("NotDraft " + m_mPayment);
		}

		/**********************
		 *	Save Values to mTab
		 */
		log.config("Saving changes");
		//
		if (!newPaymentRule.equals(m_PaymentRule))
			m_mTab.setValue("PaymentRule", newPaymentRule);
		//
		if (!newDateAcct.equals(m_DateAcct))
			m_mTab.setValue("DateAcct", newDateAcct);
		//
		if (newC_PaymentTerm_ID != m_C_PaymentTerm_ID)
			m_mTab.setValue("C_PaymentTerm_ID", new Integer(newC_PaymentTerm_ID));
		//	Set Payment
		if (m_mPayment.getC_Payment_ID() != m_C_Payment_ID)
		{
			if (m_mPayment.getC_Payment_ID() == 0)
				m_mTab.setValue("C_Payment_ID", null);
			else
				m_mTab.setValue("C_Payment_ID", new Integer(m_mPayment.getC_Payment_ID()));
		}
		//	Set Cash
		if (newC_CashLine_ID != m_C_CashLine_ID)
		{
			if (newC_CashLine_ID == 0)
				m_mTab.setValue("C_CashLine_ID", null);
			else
				m_mTab.setValue("C_CashLine_ID", new Integer(newC_CashLine_ID));
		}
		return true;
	}	//	saveChanges

	/**
	 *  Check Mandatory
	 *  @return true if all mandatory items are OK
	 */
/*
	private boolean checkMandatory()
	{
		String PaymentRule = paymentComboSelected;
		//  only Payment Rule
		if (m_onlyRule)
			return true;

		Timestamp DateAcct = m_DateAcct;
		int C_PaymentTerm_ID = m_C_PaymentTerm_ID;
		int C_CashBook_ID = m_C_CashBook_ID;
		String CCType = m_CCType;
		//
		int C_BankAccount_ID = 0;

		/***********************
		 *	Mandatory Data Check
		 */
/*
		boolean dataOK = true;
		//	B (Cash)		(Currency)
		if (PaymentRule.equals(MOrder.PAYMENTRULE_Cash))
		{
		}

		//	K (CreditCard)  Type, Number, Exp, Approval
		else if (PaymentRule.equals(MOrder.PAYMENTRULE_CreditCard))
		{
			String selVal = kTypeComboSelected;
			if (selVal != null && selVal.length() > 0)
				CCType = selVal;
			//
			String error = MPaymentValidate.validateCreditCardNumber(kNumberField, CCType);
			if (error.length() != 0)
			{
				if (error.indexOf("?") == -1)
				{
					ADialog.error(m_WindowNo, this, error);
					dataOK = false;
				}
				else    //  warning
				{
					if (!ADialog.ask(m_WindowNo, this, error))
						dataOK = false;
				}
			}
			error = MPaymentValidate.validateCreditCardExp(kExpField.getText());
			if(error.length() != 0)
			{
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
		}

		//	T (Transfer)	BPartner_Bank
		else if (PaymentRule.equals(MOrder.PAYMENTRULE_DirectDeposit)
			|| PaymentRule.equals(MOrder.PAYMENTRULE_DirectDebit))
		{
			String error = MPaymentValidate.validateRoutingNo(tRoutingField);
			if (error.length() != 0)
			{
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
			error = MPaymentValidate.validateAccountNo(tNumberField);
			if (error.length() != 0)
			{
				tNumberField.setBackground(CompierePLAF.getFieldBackground_Error());
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
		}

		//	P (PaymentTerm)	PaymentTerm
		else if (PaymentRule.equals(MOrder.PAYMENTRULE_OnCredit))
		{
			String selVal = pTermComboSelected;
			if (selVal != null && selVal.length() > 0)
				C_PaymentTerm_ID = Integer.parseInt(selVal);
		}

		//	S (Check)		(Currency) CheckNo, Routing
		else if (PaymentRule.equals(MOrder.PAYMENTRULE_Check))
		{
		//	sCurrencyCombo.getSelectedItem();
			String selVal = sBankAccountComboSelected;
			if (selVal != null && selVal.length() > 0)
				C_BankAccount_ID = Integer.parseInt(selVal);
			String error = MPaymentValidate.validateRoutingNo(sRoutingField);
			if (error.length() != 0)
			{
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
			error = MPaymentValidate.validateAccountNo(sNumberField);
			if (error.length() != 0)
			{
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
			error = MPaymentValidate.validateCheckNo(sCheckField);
			if (error.length() != 0)
			{
				ADialog.error(m_WindowNo, this, error);
				dataOK = false;
			}
		}
		else
		{
			log.log(Level.SEVERE, "Unknown PaymentRule " + PaymentRule);
			return false;
		}

		//  find Bank Account if not qualified yet
		if ("KTSD".indexOf(PaymentRule) != -1 && C_BankAccount_ID == 0)
		{
			String tender = MPayment.TENDERTYPE_CreditCard;
			if (PaymentRule.equals(MOrder.PAYMENTRULE_DirectDeposit))
				tender = MPayment.TENDERTYPE_DirectDeposit;
			else if (PaymentRule.equals(MOrder.PAYMENTRULE_DirectDebit))
				tender = MPayment.TENDERTYPE_DirectDebit;
			else if (PaymentRule.equals(MOrder.PAYMENTRULE_Check))
				tender = MPayment.TENDERTYPE_Check;
			//	ACH & Check must have a bank account
			if (C_BankAccount_ID == 0 && "TS".indexOf(PaymentRule) != -1)
			{
				ADialog.error(m_WindowNo, this, "PaymentNoProcessor");
				dataOK = false;
			}
		}

		log.config("OK=" + dataOK);
		return dataOK;
	}   //  checkMandatory
*/
	/**
	 *  Get Invoice ID for Order
	 *  @param C_Order_ID order
	 *  @return C_Invoice_ID or 0 if not found
	 */
	private static int getInvoiceID (int C_Order_ID)
	{
		int retValue = 0;
		String sql = "SELECT C_Invoice_ID FROM C_Invoice WHERE C_Order_ID=? "
			+ "ORDER BY C_Invoice_ID DESC";     //  last invoice
		try
		{
			PreparedStatement pstmt = DB.prepareStatement(sql, null);
			pstmt.setInt(1, C_Order_ID);
			ResultSet rs = pstmt.executeQuery();
			if (rs.next())
				retValue = rs.getInt(1);
			rs.close();
			pstmt.close();
		}
		catch (SQLException e)
		{
			log.log(Level.SEVERE, sql, e);
		}
		return retValue;
	}   //  getInvoiceID

	/**************************************************************************
	 *  Process Online (sales only) - if approved - exit
	 */
/*
	private void processOnline()
	{
		log.config("");
		if (!checkMandatory())
			return;

		boolean approved = false;
		String info = "";
		//
		ValueNamePair vp = (ValueNamePair)paymentCombo.getSelectedItem();
		String PaymentRule = vp.getValue();

		//  --  CreditCard
		if (PaymentRule.equals(X_C_Order.PAYMENTRULE_CreditCard))
		{
			vp = (ValueNamePair)kTypeCombo.getSelectedItem();
			String CCType = vp.getValue();

			m_mPayment.setCreditCard(MPayment.TRXTYPE_Sales, CCType,
				kNumberField.getText(), "", kExpField.getText());
			m_mPayment.setAmount(m_C_Currency_ID, m_Amount);
			m_mPayment.setPaymentProcessor();
			m_mPayment.setC_BPartner_ID(m_C_BPartner_ID);
			//
			int C_Invoice_ID = Env.getContextAsInt(ctx, m_WindowNo, "C_Invoice_ID");
			if (C_Invoice_ID == 0 && m_DocStatus.equals("CO"))
			{
				int C_Order_ID = Env.getContextAsInt(ctx, m_WindowNo, "C_Order_ID");
				C_Invoice_ID = getInvoiceID (C_Order_ID);
			}
			m_mPayment.setC_Invoice_ID(C_Invoice_ID);
			m_mPayment.setDateTrx(m_DateAcct);
			//  Set Amount
			m_mPayment.setAmount(m_C_Currency_ID, m_Amount);

			approved = m_mPayment.processOnline();
			info = m_mPayment.getR_RespMsg() + " (" + m_mPayment.getR_AuthCode()
				+ ") ID=" + m_mPayment.getR_PnRef();
			boolean saved = m_mPayment.save();

			if (approved)
			{
				boolean ok = m_mPayment.processIt(DocAction.ACTION_Complete);
				m_mPayment.save();
				if (ok)
					ADialog.info(m_WindowNo, this, "PaymentProcessed", info + "\n" + m_mPayment.getDocumentNo());
				else
					ADialog.error(m_WindowNo, this, "PaymentError", "PaymentNotCreated");
				saveChanges();
				dispose();
			}
			else
			{
				ADialog.error(m_WindowNo, this, "PaymentNotProcessed", info);
			}
		}
		else
			ADialog.error(m_WindowNo, this, "PaymentNoProcessor");
	}   //  online
*/
	/**
	 * 	Need Save record (payment with waiting order)
	 *	@return true if payment with waiting order
	 */
	public boolean needSave()
	{
		return m_needSave;
	}	//	needSave

	/**
	 *	Fill m_Values with Ref_List values
	 *  @param AD_Reference_ID reference
	 */
	private Map readReference( int AD_Reference_ID)
	{
		HashMap<String,String> values = new HashMap<String,String>();
		String SQL;
		if (Env.isBaseLanguage(Env.getCtx(), "AD_Ref_List"))
			SQL = "SELECT Value, Name FROM AD_Ref_List WHERE AD_Reference_ID=?";
		else
			SQL = "SELECT l.Value, t.Name FROM AD_Ref_List l, AD_Ref_List_Trl t "
				+ "WHERE l.AD_Ref_List_ID=t.AD_Ref_List_ID"
				+ " AND t.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "'"
				+ " AND l.AD_Reference_ID=?";

		try
		{
			PreparedStatement pstmt = DB.prepareStatement(SQL, null);
			pstmt.setInt(1, AD_Reference_ID);
			ResultSet rs = pstmt.executeQuery();
			while (rs.next())
			{
				String value = rs.getString(1);
				String name = rs.getString(2);
				values.put(value, name);
			}
			rs.close();
			pstmt.close();
		}
		catch (SQLException e)
		{
			log.log(Level.SEVERE, SQL, e);
		}

		return values;
	}	//	readReference
}	//	VPayment
