/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.acct;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.logging.Level;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MCostDetail;
import org.compiere.model.MMovement;
import org.compiere.model.MMovementLine;
import org.compiere.model.MMovementLineMA;
import org.compiere.model.MProduct;
import org.compiere.model.ProductCost;
import org.compiere.util.Env;
/**
* Post {@link MMovement} Documents. DOCTYPE_MatMovement.
*
* Table: M_Movement (323)
* Document Types: MMM
*
* @author Jorg Janke
* @author Armen Rizal, Goodwill Consulting
* BF [ 1745154 ] Cost in Reversing Material Related Docs
* @version $Id: Doc_Movement.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $
*/
public class Doc_Movement extends Doc
{
private int m_Reversal_ID = 0;
@SuppressWarnings("unused")
private String m_DocStatus = "";
/**
* Constructor
* @param as accounting schema
* @param rs record
* @param trxName trx
*/
public Doc_Movement (MAcctSchema as, ResultSet rs, String trxName)
{
super (as, MMovement.class, rs, DOCTYPE_MatMovement, trxName);
} // Doc_Movement
/**
* Load Document Details
* @return error message or null
*/
@Override
protected String loadDocumentDetails()
{
setC_Currency_ID(NO_CURRENCY);
MMovement move = (MMovement)getPO();
setDateDoc (move.getMovementDate());
setDateAcct(move.getMovementDate());
m_Reversal_ID = move.getReversal_ID();//store original (voided/reversed) document
m_DocStatus = move.getDocStatus();
// Contained Objects
p_lines = loadLines(move);
if (log.isLoggable(Level.FINE)) log.fine("Lines=" + p_lines.length);
return null;
} // loadDocumentDetails
/**
* Load inventory movement lines
* @param move move
* @return document lines (DocLine_Material)
*/
private DocLine[] loadLines(MMovement move)
{
ArrayList list = new ArrayList();
MMovementLine[] lines = move.getLines(false);
for (int i = 0; i < lines.length; i++)
{
MMovementLine line = lines[i];
DocLine docLine = new DocLine (line, this);
docLine.setQty(line.getMovementQty(), false);
docLine.setReversalLine_ID(line.getReversalLine_ID());
if (log.isLoggable(Level.FINE)) log.fine(docLine.toString());
list.add (docLine);
}
// Return Array
DocLine[] dls = new DocLine[list.size()];
list.toArray(dls);
return dls;
} // loadLines
/**
* Get Balance
* @return balance (ZERO) - always balanced
*/
@Override
public BigDecimal getBalance()
{
BigDecimal retValue = Env.ZERO;
return retValue;
} // getBalance
/**
* Create Facts (the accounting logic) for
* MMM.
*
* Movement
* Inventory DR CR
* InventoryTo DR CR
*
* @param as account schema
* @return Fact
*/
@Override
public ArrayList createFacts (MAcctSchema as)
{
// create Fact Header
Fact fact = new Fact(this, as, Fact.POST_Actual);
setC_Currency_ID(as.getC_Currency_ID());
// Line pointers
FactLine dr = null;
FactLine cr = null;
for (int i = 0; i < p_lines.length; i++)
{
DocLine line = p_lines[i];
BigDecimal costs = null;
if (!isReversal(line))
{
MProduct product = (MProduct) line.getProduct();
String costingLevel = product.getCostingLevel(as);
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(costingLevel) )
{
if (line.getM_AttributeSetInstance_ID() == 0 )
{
MMovementLine mLine = (MMovementLine) line.getPO();
MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), mLine.get_ID(), getTrxName());
if (mas != null && mas.length > 0 )
{
costs = BigDecimal.ZERO;
for (int j = 0; j < mas.length; j++)
{
MMovementLineMA ma = mas[j];
BigDecimal QtyMA = ma.getMovementQty();
if (QtyMA.signum() != line.getQty().signum())
QtyMA = QtyMA.negate();
ProductCost pc = line.getProductCost();
pc.setQty(QtyMA);
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
BigDecimal maCosts = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_MovementLine_ID=? AND IsSOTrx='N'");
costs = costs.add(maCosts);
}
}
}
else
{
costs = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_MovementLine_ID=? AND IsSOTrx='N'");
}
}
else
{
// MZ Goodwill
// if Inventory Move CostDetail exist then get Cost from Cost Detail
costs = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_MovementLine_ID=? AND IsSOTrx='N'");
// end MZ
}
}
else
{
costs = BigDecimal.ZERO;
}
// ** Inventory DR CR
dr = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), costs.negate()); // from (-) CR
if (dr == null)
continue;
dr.setM_Locator_ID(line.getM_Locator_ID());
dr.setQty(line.getQty().negate()); // outgoing
if (isReversal(line))
{
// Set AmtAcctDr from Original Movement
if (!dr.updateReverseLine (MMovement.Table_ID,
m_Reversal_ID, line.getReversalLine_ID(),Env.ONE))
{
p_Error = "Original Inventory Move not posted yet";
return null;
}
}
// ** InventoryTo DR CR
cr = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), costs); // to (+) DR
if (cr == null)
continue;
cr.setM_Locator_ID(line.getM_LocatorTo_ID());
cr.setQty(line.getQty());
if (isReversal(line))
{
// Set AmtAcctCr from Original Movement
if (!cr.updateReverseLine (MMovement.Table_ID,
m_Reversal_ID, line.getReversalLine_ID(),Env.ONE, dr))
{
p_Error = "Original Inventory Move not posted yet";
return null;
}
costs = cr.getAcctBalance(); //get original cost
}
// Only for between-org movements
if (dr.getAD_Org_ID() != cr.getAD_Org_ID())
{
String costingLevel = line.getProduct().getCostingLevel(as);
if (!MAcctSchema.COSTINGLEVEL_Organization.equals(costingLevel))
continue;
//
String description = line.getDescription();
if (description == null)
description = "";
// Cost Detail From
if (!MCostDetail.createMovement(as, dr.getAD_Org_ID(), // locator org
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
costs.negate(), line.getQty().negate(), true,
description + "(|->)", getTrxName()))
{
p_Error = "Failed to create cost detail record";
return null;
}
// Cost Detail To
if (!MCostDetail.createMovement(as, cr.getAD_Org_ID(), // locator org
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
line.get_ID(), 0,
costs, line.getQty(), false,
description + "(|<-)", getTrxName()))
{
p_Error = "Failed to create cost detail record";
return null;
}
}
}
//
ArrayList facts = new ArrayList();
facts.add(fact);
return facts;
} // createFact
/**
* @param line
* @return true if line is for reversal
*/
private boolean isReversal(DocLine line) {
return m_Reversal_ID !=0 && line.getReversalLine_ID() != 0;
}
} // Doc_Movement