/***********************************************************************
 * This file is part of iDempiere ERP Open Source                      *
 * http://www.idempiere.org                                            *
 *                                                                     *
 * Copyright (C) Contributors                                          *
 *                                                                     *
 * This program is free software; you can redistribute it and/or       *
 * modify it under the terms of the GNU General Public License         *
 * as published by the Free Software Foundation; either version 2      *
 * of the License, or (at your option) any later version.              *
 *                                                                     *
 * This program 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 General Public License for more details.                        *
 *                                                                     *
 * You should have received a copy of the GNU General Public License   *
 * along with this program; if not, write to the Free Software         *
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,          *
 * MA 02110-1301, USA.                                                 *
 *                                                                     *
 * Contributors:                                                       *
 * - Carlos Ruiz                                                       *
 **********************************************************************/
package org.idempiere.process;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.logging.Level;

import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.MAllocationHdr;
import org.compiere.model.MAllocationLine;
import org.compiere.model.MDocType;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MPeriod;
import org.compiere.model.MProcessPara;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfoParameter;
import org.compiere.process.SvrProcess;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

/**
 *	IDEMPIERE-918 Generate credit memo from invoice
 * 	@author 	Carlos Ruiz - globalqss
 */
@org.adempiere.base.annotation.Process
public class InvoiceCreateCreditMemo extends SvrProcess {
	/* The document type for the credit memo */
	private int p_C_DocType_ID = 0;
	/* Invoice Date */
	private Timestamp p_DateInvoiced = null;
	/* Accounting Date */
	private Timestamp p_DateAcct = null;
	/* Document Action */
	private String p_DocAction = null;
	/* Create allocation between the invoice and the credit memo */
	private boolean p_IsCreateAllocation = false;
	/* The invoice being credited */
	private MInvoice  invoice = null;

	/**
	 *  Prepare - e.g., get Parameters.
	 */
	protected void prepare() {
		for (ProcessInfoParameter para : getParameter()) {
			String name = para.getParameterName();
			switch (name) {
			case "C_DocType_ID":
				p_C_DocType_ID = para.getParameterAsInt();
				break;
			case "DateInvoiced":
				p_DateInvoiced = para.getParameterAsTimestamp();
				break;
			case "DateAcct":
				p_DateAcct = para.getParameterAsTimestamp();
				break;
			case "DocAction":
				p_DocAction = para.getParameterAsString();
				break;
			case "IsCreateAllocation":
				p_IsCreateAllocation = para.getParameterAsBoolean();
				break;
			default:
				MProcessPara.validateUnknownParameter(getProcessInfo().getAD_Process_ID(), para);
			}
		}
		invoice = new MInvoice(getCtx(), getRecord_ID(), get_TrxName());
	}	//	prepare

	/**
	 *  Perform process.
	 *  @return Message
	 *  @throws Exception
	 */
	protected String doIt() throws Exception {
		if (log.isLoggable(Level.INFO)) log.info("C_Invoice_ID" + invoice.getC_Invoice_ID() + ", C_DocType_ID=" + p_C_DocType_ID + ", DateInvoiced=" + p_DateInvoiced
				+ ", DateAcct=" + p_DateAcct + ", DocAction=" + p_DocAction);

		if (p_IsCreateAllocation) {
			// validate - cannot create allocation if the invoice is already paid or partially paid
			MAllocationHdr[] allocs = MAllocationHdr.getOfInvoice(getCtx(), invoice.getC_Invoice_ID(), get_TrxName());
			if (allocs.length > 0) {
				throw new AdempiereException(Msg.getMsg(getCtx(), "InvoiceHasAllocations"));
			}
		}
		// validate just for invoices, not for credit memos
		if (invoice.isCreditMemo()) {
			throw new AdempiereException(Msg.getMsg(getCtx(), "CannotCreateCreditMemoFromCreditMemo"));
		}
		// Validate if there is already another credit memo for this invoice
		int id = DB.getSQLValueEx(get_TrxName(), "SELECT C_Invoice_ID FROM C_Invoice WHERE RelatedInvoice_ID = ?", invoice.getC_Invoice_ID());

		if (id > 0) {
			MInvoice actualCreditMemo = MInvoice.get(getCtx(), id);
			MDocType dtc = MDocType.get(getCtx(), actualCreditMemo.getC_DocTypeTarget_ID());
			addLog(0, null, null, dtc.getName() + " " + actualCreditMemo.getDocumentNo(), MInvoice.Table_ID, actualCreditMemo.getC_Invoice_ID());
			throw new AdempiereException(Msg.getMsg(getCtx(), "CreditMemoAlreadyExists"));
		}

		MInvoice creditMemo = credit();
		if (creditMemo != null) {
			MDocType dtc = MDocType.get(getCtx(), creditMemo.getC_DocTypeTarget_ID());
			addLog(0, null, null, dtc.getName() + " " + creditMemo.getDocumentNo(), MInvoice.Table_ID, creditMemo.getC_Invoice_ID());
		}

		return "@OK@";
	}	//	doIt

	private MInvoice credit() {
		Timestamp creditDate = p_DateAcct;
		Timestamp creditDateInvoiced = p_DateInvoiced;
		MPeriod.testPeriodOpen(getCtx(), creditDate, p_C_DocType_ID, invoice.getAD_Org_ID());

		//	Deep Copy
		MInvoice creditMemo = null;
		creditMemo = MInvoice.copyFrom(invoice, creditDateInvoiced, creditDate, p_C_DocType_ID, invoice.isSOTrx(), false, get_TrxName(), true);
		if (creditMemo == null) {
			throw new AdempiereException("Could not create Credit Memo");
		}

		//	Reverse Line Qty
		MInvoiceLine[] oLines = invoice.getLines(false);
		MInvoiceLine[] rLines = creditMemo.getLines(true);
		if (oLines.length != rLines.length) {
			throw new AdempiereException("Credit Memo created with different number of lines than invoice");
		}
		for (int i = 0; i < rLines.length; i++) {
			MInvoiceLine rLine = rLines[i];
			MInvoiceLine oLine = oLines[i];
			rLine.setQtyEntered(oLine.getQtyEntered());
			rLine.setQtyInvoiced(oLine.getQtyInvoiced());
			rLine.setLineNetAmt(oLine.getLineNetAmt());
			rLine.setTaxAmt(oLine.getTaxAmt());
			rLine.setLineTotalAmt(oLine.getLineTotalAmt());
			rLine.setPriceActual(oLine.getPriceActual());
			rLine.setPriceList(oLine.getPriceList());
			rLine.setPriceLimit(oLine.getPriceLimit());
			rLine.setPriceEntered(oLine.getPriceEntered());
			rLine.setC_UOM_ID(oLine.getC_UOM_ID());
			rLine.setM_AttributeSetInstance_ID(oLine.getM_AttributeSetInstance_ID());
			if (!rLine.save(get_TrxName())) {
				throw new AdempiereException("Could not create credit memo line");
			}
		}
		if (MInvoice.PAYMENTRULE_Cash.equals(creditMemo.getPaymentRule())) {
			creditMemo.setPaymentRule(MInvoice.PAYMENTRULE_OnCredit); // avoid creation of automatic payment
		}
		creditMemo.setC_Order_ID(invoice.getC_Order_ID());
		StringBuilder msgadd = new StringBuilder("{->").append(invoice.getDocumentNo()).append(")");
		creditMemo.addDescription(msgadd.toString());
		creditMemo.setPOReference(invoice.getDocumentNo());
		creditMemo.setRelatedInvoice_ID(invoice.getC_Invoice_ID());
		creditMemo.saveEx(get_TrxName());
		//
		if (p_DocAction != null) {
			if (!creditMemo.processIt(p_DocAction)) {
				throw new AdempiereException(Msg.getMsg(getCtx(), "FailedProcessingDocument") + " - " + creditMemo.getProcessMsg());
			}
			if (p_IsCreateAllocation && DocAction.ACTION_Complete.equals(p_DocAction)) {
				//	Create Allocation
				StringBuilder msgall = new StringBuilder().append(Msg.translate(getCtx(), "C_Invoice_ID")).append(": ").append(invoice.getDocumentNo()).append("/").append(creditMemo.getDocumentNo());
				MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, creditDate,
						invoice.getC_Currency_ID(),
						msgall.toString(),
						get_TrxName());
				alloc.setAD_Org_ID(invoice.getAD_Org_ID());
				alloc.saveEx();
				//	Amount
				BigDecimal gt = invoice.getGrandTotal(true);
				if (!invoice.isSOTrx())
					gt = gt.negate();
				//	Invoice Line
				MAllocationLine aLine = new MAllocationLine (alloc, gt, Env.ZERO, Env.ZERO, Env.ZERO);
				aLine.setC_Invoice_ID(invoice.getC_Invoice_ID());
				aLine.saveEx();
				//	Credit Line
				MAllocationLine cLine = new MAllocationLine (alloc, gt.negate(), Env.ZERO, Env.ZERO, Env.ZERO);
				cLine.setC_Invoice_ID(creditMemo.getC_Invoice_ID());
				cLine.saveEx();
				if (!alloc.processIt(DocAction.ACTION_Complete))
					throw new AdempiereException(Msg.getMsg(getCtx(), "FailedProcessingDocument") + " - " + alloc.getProcessMsg());
				// end added
				alloc.saveEx();
			}
		}

		return creditMemo;
	}

}	//	InvoiceCreateCreditMemo
