Página 1 de 1

Alteração ValidatorInOut.java

Enviado: Sex Dez 04, 2009 6:37 pm
por edilsondneto
Estou com mais um problema....

Estava tentando não fazer uma expedição do produto que não tivesse em estoque.
Verificando a variavel no system configurator, alterei para "Y". Com isso não era para entregar produto sem estoque, mas se o produto tiver atributo o sistema entrega memso sem estoque, pois ele faz a contagem do produto versos localizador e eu penso que o correto seria produto&localizador%&atributo

exemplo, tenho a quantidade em estoque de 10 envelopes, sendo 5 com atributo branco e 5 com atributo azul, na hora da entrega se eu tentar entregar 7 azuis ou 7 brancos o sistema permite pois leva em conta o produto&localizador.

ai eu tentando resolver esse problema, fiz o sistema verificar tambem pelo atributo no validador de entrada e saida "ValidatorInOut.java", apos a alteracao testando o sistema ele fez tudo como esperado levou em consideração o atributo, mas a nivel de codigo não sei se fiz correto, pelos padroes do AD e queria ajuda de voces.


como nao eh permitido anexo vou colar o codigo e as alterações estao abaixo dos comentarios
/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/
--->>.
/******************************************************************************
* Product: ADempiereLBR - ADempiere Localization Brazil *
* 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. *
*****************************************************************************/
package org.adempierelbr.validator;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.apps.search.Info_Column;
import org.compiere.model.MClient;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MMovement;
import org.compiere.model.MMovementLine;
import org.compiere.model.MOrderLine;
import org.compiere.model.MProduct;
import org.compiere.model.MSysConfig;
import org.compiere.model.ModelValidationEngine;
import org.compiere.model.ModelValidator;
import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

/**
* ValidatorInOut, inclui as validações de outras tabelas que
* manipulam materiais, como Movimentações, Inventário e Entrada/Saída.
*
* @author Ricardo Santana (ralexsander)
* @version $Id: ValidatorInOut.java, 04/01/2008 15:56:00 ralexsander
*/
public class ValidatorInOut implements ModelValidator
{
/**
* Constructor.
* The class is instanciated when logging in and client is selected/known
*/
public ValidatorInOut ()
{
super ();
} //ValidatorInOut

/** Logger */
private static CLogger log = CLogger.getCLogger(ValidatorInOut.class);
/** Client */
private int m_AD_Client_ID = -1;


/**
* Initialize Validation
* @param engine validation engine
* @param client client
*/
public void initialize (ModelValidationEngine engine, MClient client)
{
m_AD_Client_ID = client.getAD_Client_ID();

log.info(client.toString());

// DocValidate
engine.addDocValidate("M_InOut", this);
engine.addDocValidate("M_Movement", this);
engine.addDocValidate("M_Inventory", this);

} // initialize

/**
* Get Client to be monitored
* @return AD_Client_ID client
*/
public int getAD_Client_ID()
{
return m_AD_Client_ID;
} // getAD_Client_ID

/**
* User Login.
* Called when preferences are set
* @param AD_Org_ID org
* @param AD_Role_ID role
* @param AD_User_ID user
* @return error message or null
*/
public String login (int AD_Org_ID, int AD_Role_ID, int AD_User_ID)
{
log.info("AD_User_ID=" + AD_User_ID);
return null;
} // login

/**
* Model Change of a monitored Table.
* Called after PO.beforeSave/PO.beforeDelete
* when you called addModelChange for the table
* @param po persistent object
* @param type TYPE_
* @return error message or null
* @exception Exception if the recipient wishes the change to be not accept.
*/
public String modelChange (PO po, int type) throws Exception
{
return null;
} // modelChange

/**
* Validate Document.
* Called as first step of DocAction.prepareIt
* when you called addDocValidate for the table.
* Note that totals, etc. may not be correct.
* @param po persistent object
* @param timing see TIMING_ constants
* @return error message or null
*/
public String docValidate (PO po, int timing)
{
if (po.get_TableName().equalsIgnoreCase("M_InOut"))
return docValidate((MInOut) po, timing);

else if (po.get_TableName().equalsIgnoreCase("M_Movement"))
return docValidate((MMovement) po, timing);

else if (po.get_TableName().equalsIgnoreCase("M_Inventory"))
return docValidate((MInventory) po, timing);


return null;
} // docValidate

/**
* Quantity On Hand.
*
* @param M_Product_ID
* @param M_Locator_ID
* @param M_AttributeSetInstance_ID
* @return BigDecimal quantity of product
*/
private BigDecimal getQtyOnHand(int M_Product_ID, int M_Locator_ID, int M_AttributeSetInstance_ID)
// private BigDecimal getQtyOnHand(int M_Product_ID, int M_Locator_ID)
{
BigDecimal QtyOnHand = Env.ZERO;
String sql = "SELECT SUM(QtyOnHand) FROM M_Storage "
+ "WHERE M_Product_ID=?"; // 1
if(M_Locator_ID > 0)
sql = sql + " AND M_Locator_ID=?"; // 2

/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

if(M_AttributeSetInstance_ID > 0)
sql = sql + " AND M_AttributeSetInstance_ID=?"; // 3
/***/

PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(sql, null);
pstmt.setInt(1, M_Product_ID);
if(M_Locator_ID > 0)
pstmt.setInt(2, M_Locator_ID);

/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

if (M_AttributeSetInstance_ID != 0)
pstmt.setInt (3, M_AttributeSetInstance_ID);
/***/

rs = pstmt.executeQuery();
if (rs.next())
{
QtyOnHand = rs.getBigDecimal(1);
}
}
catch (SQLException e)
{
log.log(Level.SEVERE, sql, e);
return Env.ZERO;
}
finally{
DB.close(rs, pstmt);
}

if(QtyOnHand != null)
return QtyOnHand;

return Env.ZERO;
} // QtyOnHand

/**
* Validate Movement.
*
* @param MMovement movement
* @param timing see TIMING_ constants
* @return error message or null
*/
private String docValidate(MMovement mov, int timing)
{
Properties ctx = mov.getCtx();

if (timing == TIMING_BEFORE_COMPLETE)
{
MMovementLine[] lines = mov.getLines(true);
ArrayList<String> prod = new ArrayList<String>();

if(lines == null
|| lines.length <= 0)
return Msg.getMsg(ctx, "NoLines");

for(MMovementLine line : lines)
{
if(line.getM_Product_ID() <=0
|| line.getM_Locator_ID() <=0)
return "Produto ou Localizador inválido";


/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

if (line.getM_AttributeSetInstance_ID() != 0)
{
if(((line.getM_Product_ID() > 0) && (line.getM_Locator_ID() > 0))
&& (line.getM_AttributeSetInstance_ID() <=0))

return "Atributo Necessário";
}
/***/


if(line.getMovementQty().equals(Env.ZERO))
return "Itens com qtd zero";

if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
return "Duas linhas usando o mesmo produto na mesma posição";
else
prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());

/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

// BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID());
BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(), line.getM_AttributeSetInstance_ID() );
/***/

if(qtdOnHand.compareTo(line.getMovementQty()) == -1)
return "Sem saldo na linha=" + line.getLine();
}
}

return null;
}

/**
* Validate Inventory.
*
* @param MInventory inventory
* @param timing see TIMING_ constants
* @return error message or null
*/
private String docValidate(MInventory inv, int timing)
{
Properties ctx = inv.getCtx();

if (timing == TIMING_AFTER_COMPLETE)
{
MInventoryLine[] lines = inv.getLines(true);
ArrayList<String> prod = new ArrayList<String>();

if(lines == null
|| lines.length <= 0)
return Msg.getMsg(ctx, "NoLines");

Boolean isInternalUse = (Boolean) inv.get_Value("z_IsInternalUse");

if(isInternalUse != null && isInternalUse)
{
for(MInventoryLine line : lines)
{
if(line.getM_Product_ID() <=0
|| line.getM_Locator_ID() <=0)
return "Produto ou Localizador inválido";

if(line.getMovementQty().equals(Env.ZERO))
return "Itens com qtd zero";

if(line.getC_Charge_ID() == 0)
return "Sem conta de destino";

if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
return "Duas linhas usando o mesmo produto na mesma posição";
else
prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());
/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

// BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID());
BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(),line.getM_AttributeSetInstance_ID());
/***/

if(qtdOnHand.compareTo(line.getQtyInternalUse()) == -1)
return "Sem saldo na linha=" + line.getLine();
}
}
}

return null;
}

/**
* Validate Shipment/Receipt.
*
* @param MInOut inventory
* @param timing see TIMING_ constants
* @return error message or null
*/
private String docValidate(MInOut inOut, int timing)
{
Properties ctx = inOut.getCtx();
String trx = inOut.get_TrxName();

if (timing == TIMING_AFTER_COMPLETE)
{
String sql = "SELECT C_DocType_ID FROM C_DocType " +
"WHERE lbr_IsManufactured='Y' AND C_DocType_ID=?";

/**
* Verifica se é industrialização
* */
if(DB.getSQLValue(trx, sql, inOut.getC_DocType_ID()) < 0)
return null;

MInOutLine[] lines = inOut.getLines();

for(MInOutLine line : lines)
{
int C_OrderLine_ID = line.getC_OrderLine_ID();

if(C_OrderLine_ID > 0)
{
MOrderLine oLine = new MOrderLine(ctx, C_OrderLine_ID, trx);
Integer ii = (Integer) oLine.get_Value("M_ProductionLine_ID");
int M_ProductionLine_ID = 0;

if(ii != null)
M_ProductionLine_ID = ii.intValue();
else
continue;

/**
* Atualiza a quantidade entregue de industrialização
* */

DB.executeUpdate("UPDATE M_ProductionLine " +
"SET QtyDelivered=COALESCE(QtyDelivered,0)+ " +
"(SELECT QtyEntered FROM M_InOutLine " +
"WHERE M_InOutLine_ID=" + line.getM_InOutLine_ID() + ") " +
"WHERE M_ProductionLine_ID=" + M_ProductionLine_ID, trx);
}
}
}
else if ((timing == TIMING_BEFORE_COMPLETE || timing == TIMING_BEFORE_REVERSECORRECT))
{
MInOutLine[] lines = inOut.getLines();
ArrayList<Integer> olines = new ArrayList<Integer>();

if (lines.length == 0)
return "Documento sem linhas";

for (int i = 0; i < lines.length; i++)
{
MInOutLine line = lines[i];
int M_Product_ID = line.getM_Product_ID();
int M_Locator_ID = line.getM_Locator_ID();
/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

int M_AttributeSetInstance_ID = line.getM_AttributeSetInstance_ID();

/***/


BigDecimal onHand = Env.ZERO, qtyToShip = Env.ZERO;
MProduct produto = MProduct.get(ctx, M_Product_ID);

if (!produto.isStocked())
continue;

if (M_Locator_ID == 0)
return "Localizador do estoque não definida na linha: #" + line.getLine() + ".";

if (line.getQtyEntered() == Env.ZERO)
return "Item com quantidade ZERO na linha: #" + line.getLine() + ".";

if (!MSysConfig.getBooleanValue("LBR_ALLOW_MM_SHIP_RECEIPT_WITHOUT_ORDER", true, inOut.getAD_Client_ID())
&& line.getC_OrderLine_ID() == 0)
return "Ordem de Compra não disponível.";

MOrderLine oline = new MOrderLine(ctx, line.getC_OrderLine_ID(), trx);

if (timing == TIMING_BEFORE_REVERSECORRECT
&& !MSysConfig.getBooleanValue("LBR_ALLOW_REVERSE_SHIP_RECEIT_WITH_OPEN_INVOICE", true, inOut.getAD_Client_ID())
&& DB.getSQLValue(null, "SELECT COUNT(*) FROM C_InvoiceLine il, C_Invoice i WHERE i.C_Invoice_ID=il.C_Invoice_ID AND i.DocStatus IN ('CO','CL') AND il.M_InOutLine_ID=?", line.getM_InOutLine_ID()) > 0)
return "Fatura(s) em aberto. Impossível continuar com o estorno.";

int C_OrderLine_ID = line.getC_OrderLine_ID();
if (C_OrderLine_ID != 0)
{
if(!MSysConfig.getBooleanValue("LBR_ALLOW_DUPLICATED_ORDERLINE_ON_SHIP_RECEIPT", true, inOut.getAD_Client_ID())
&& olines.contains("" + line.getC_OrderLine_ID()))
return "Linha #" + line.getLine() + " duplicada.";
else
olines.add(line.getC_OrderLine_ID());
}

/**
* FIXME: QtyDelivered é na UDM padrão, QtyEntered pode ser outra,
* com isso a comparação, pode não funcionar corretamente.
*
*/

log.info("Delivered: " + oline.getQtyDelivered() + " Entered: " + oline.getQtyEntered() + " Trying: " + line.getQtyEntered());
if (timing == TIMING_BEFORE_COMPLETE
&& MSysConfig.getBooleanValue("LBR_MATCH_SHIPMENT_RECEIPT_AND_ORDER_QTY", false, inOut.getAD_Client_ID())
&& oline.getQtyDelivered().add(line.getQtyEntered()).doubleValue() > oline.getQtyEntered().doubleValue())
return "Nao e possivel fazer recebimento maior que o pedido. Linha do pedido #" + line.getLine();

if (M_Product_ID == 0)
continue;
/**
* FIXME: M_AttributeSetInstance_ID,
* com isso verifico quantidade de propduto por instância
* @author Edilson Duarte, Grupo LCR
* @version $Id: ValidatorInOut.java,v 1.7 2009/01/12 05:18:39 edilsoneto Exp $
*/

// onHand = getQtyOnHand(M_Product_ID, M_Locator_ID); //QtyOnHand
onHand = getQtyOnHand(M_Product_ID, M_Locator_ID, M_AttributeSetInstance_ID); //QtyOnHand
/**/

qtyToShip = Env.ZERO;

for (int j = 0; j < lines.length; j++)
{
if(lines[j].getM_Product_ID() == line.getM_Product_ID()
&& lines[j].getM_Locator_ID() == line.getM_Locator_ID())
{
qtyToShip = qtyToShip.add(lines[j].getQtyEntered());
}
}

if (timing == TIMING_BEFORE_COMPLETE
&& !MSysConfig.getBooleanValue("LBR_ALLOW_NEGATIVE_STOCK", true, inOut.getAD_Client_ID())){

String movementType = inOut.getMovementType();

if (movementType.charAt(1) == '-')
{
if (timing == TIMING_BEFORE_COMPLETE
&& onHand.subtract(qtyToShip).doubleValue() < 0)
return "Sem quantidade disponivel na linha #" + line.getLine() + ".";
}
else
{
if (onHand.add(line.getQtyEntered()).doubleValue() < 0)
return "Sem quantidade disponível na linha #" + line.getLine() + ".";
}

}

} // for;
}

return null;
}

/**
* Update Info Window Columns.
* - add new Columns
* - remove columns
* - change dispay sequence
* @param columns array of columns
* @param sqlFrom from clause, can be modified
* @param sqlOrder order by clause, can me modified
* @return true if you updated columns, sequence or sql From clause
*/
public boolean updateInfoColumns (ArrayList<Info_Column> columns,
StringBuffer sqlFrom, StringBuffer sqlOrder)
{
/** *
int AD_Role_ID = Env.getAD_Role_ID (Env.getCtx()); // Can be Role/User specific
String from = sqlFrom.toString();
if (from.startsWith ("M_Product"))
{
columns.add (new Info_Column("Header", "'sql'", String.class).seq(35));
return true;
}/** */
return false;
} // updateInfoColumns

/**
* String Representation
* @return info
*/
public String toString ()
{
StringBuffer sb = new StringBuffer ("AdempiereLBR - Powered by Kenos & Faire");
return sb.toString ();
} // toString

} //ValidatorInOut

Re: Alteração ValidatorInOut.java

Enviado: Qua Dez 09, 2009 6:07 am
por emontenegro
Dê uma olhada nessa versão do código...

Um abraço,
Eduardo.

Código: Selecionar todos


/******************************************************************************
 * Product: ADempiereLBR - ADempiere Localization Brazil                      *
 * 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.                     *
 *****************************************************************************/
package org.adempierelbr.validator;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.apps.search.Info_Column;
import org.compiere.model.MClient;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MMovement;
import org.compiere.model.MMovementLine;
import org.compiere.model.MOrderLine;
import org.compiere.model.MProduct;
import org.compiere.model.MSysConfig;
import org.compiere.model.ModelValidationEngine;
import org.compiere.model.ModelValidator;
import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

/**
 *   ValidatorInOut, inclui as validações de outras tabelas que
 *   manipulam materiais, como Movimentações, Inventário e Entrada/Saída.
 *     
 *   @author Ricardo Santana (ralexsander)
 *   @version $Id: ValidatorInOut.java, 04/01/2008 15:56:00 ralexsander
 */
public class ValidatorInOut implements ModelValidator
{
   /**
    *   Constructor.
    *   The class is instanciated when logging in and client is selected/known
    */
   public ValidatorInOut ()
   {
      super ();
   }   //ValidatorInOut
   
   /**   Logger         */
   private static CLogger log = CLogger.getCLogger(ValidatorInOut.class);
   /** Client         */
   private int      m_AD_Client_ID = -1;
   
   
   /**
    *   Initialize Validation
    *   @param engine validation engine
    *   @param client client
    */
   public void initialize (ModelValidationEngine engine, MClient client)
   {
      //client = null for global validator
        if (client != null) {
            m_AD_Client_ID = client.getAD_Client_ID();
            log.info(client.toString());
        }
        else  {
            log.info("Initializing global validator: "+this.toString());
        }

      //   DocValidate
      engine.addDocValidate("M_InOut", this);
      engine.addDocValidate("M_Movement", this);
      engine.addDocValidate("M_Inventory", this);

   }   //   initialize

   /**
    *   Get Client to be monitored
    *   @return AD_Client_ID client
    */
   public int getAD_Client_ID()
   {
      return m_AD_Client_ID;
   }   //   getAD_Client_ID

   /**
    *   User Login.
    *   Called when preferences are set
    *   @param AD_Org_ID org
    *   @param AD_Role_ID role
    *   @param AD_User_ID user
    *   @return error message or null
    */
   public String login (int AD_Org_ID, int AD_Role_ID, int AD_User_ID)
   {
      log.info("AD_User_ID=" + AD_User_ID);
      return null;
   }   //   login
   
    /**
     *   Model Change of a monitored Table.
     *   Called after PO.beforeSave/PO.beforeDelete
     *   when you called addModelChange for the table
     *   @param po persistent object
     *   @param type TYPE_
     *   @return error message or null
     *   @exception Exception if the recipient wishes the change to be not accept.
     */
   public String modelChange (PO po, int type) throws Exception
   {
      return null;
   }   //   modelChange

   /**
    *   Validate Document.
    *   Called as first step of DocAction.prepareIt
     *   when you called addDocValidate for the table.
     *   Note that totals, etc. may not be correct.
    *   @param po persistent object
    *   @param timing see TIMING_ constants
     *   @return error message or null
    */
   public String docValidate (PO po, int timing)
   {
      if (po.get_TableName().equalsIgnoreCase("M_InOut"))
         return docValidate((MInOut) po, timing);
      
      else if (po.get_TableName().equalsIgnoreCase("M_Movement"))
         return docValidate((MMovement) po, timing);
      
      else if (po.get_TableName().equalsIgnoreCase("M_Inventory"))
         return docValidate((MInventory) po, timing);

      
      return null;
   }   //   docValidate
   
   /**
    *   Quantity On Hand.
    *   
    *   @param M_Product_ID
    *   @param M_Locator_ID
    *   @param M_AttributeSetInstance_ID
     *   @return BigDecimal quantity of product
    */
   private BigDecimal getQtyOnHand(int M_Product_ID, int M_Locator_ID, int M_AttributeSetInstance_ID)
   {
      BigDecimal QtyOnHand = Env.ZERO;
      String sql = "SELECT SUM(QtyOnHand) FROM M_Storage "
         + "WHERE M_Product_ID=?";   //   1
      if(M_Locator_ID > 0)
         sql = sql + " AND M_Locator_ID=?";   //   2
      if(M_AttributeSetInstance_ID >0)
         sql = sql + " AND M_AttributeSetInstance_ID=?";   //   3
      
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      try
      {
         int i=1;
         pstmt = DB.prepareStatement(sql, null);
         pstmt.setInt(i++, M_Product_ID);
         if(M_Locator_ID > 0)
            pstmt.setInt(i++, M_Locator_ID);
         if(M_AttributeSetInstance_ID > 0)
            pstmt.setInt(i++, M_AttributeSetInstance_ID);
         
         rs = pstmt.executeQuery();
         if (rs.next())
         {
            QtyOnHand = rs.getBigDecimal(1);
         }
      }
      catch (SQLException e)
      {
         log.log(Level.SEVERE, sql, e);
         return Env.ZERO;
      }
      finally{
             DB.close(rs, pstmt);
      }
      
      if(QtyOnHand != null)
         return QtyOnHand;
      
      return Env.ZERO;
   }   //   QtyOnHand
   
   /**
    *   Validate Movement.
    * 
    *   @param MMovement movement
    *   @param timing see TIMING_ constants
     *   @return error message or null
    */
   private String docValidate(MMovement mov, int timing)
   {
      Properties ctx = mov.getCtx();
      
      if (timing == TIMING_BEFORE_COMPLETE)
      {
         MMovementLine[] lines = mov.getLines(true);
         ArrayList<String> prod = new ArrayList<String>();
         
         if(lines == null
               || lines.length <= 0)
            return Msg.getMsg(ctx, "NoLines");
         
         for(MMovementLine line : lines)
         {
            if(line.getM_Product_ID() <=0
                  || line.getM_Locator_ID() <=0)
               return "Producto o Ubicación inválidos";
            
            if(line.getMovementQty().equals(Env.ZERO))
               return "Artículos con cantidad cero";
            
            if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
               return "Existen dos o más líneas repetidas";
            else
               prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());
            
            BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(), line.getM_AttributeSetInstance_ID());
            
            if(qtdOnHand.compareTo(line.getMovementQty()) == -1)
               return "No hay stock del artículo de la línea=" + line.getLine();
         }
      }
      
      return null;
   }
   
   /**
    *   Validate Inventory.
    * 
    *   @param MInventory inventory
    *   @param timing see TIMING_ constants
     *   @return error message or null
    */
   private String docValidate(MInventory inv, int timing)
   {
      Properties ctx = inv.getCtx();
      
      if (timing == TIMING_AFTER_COMPLETE)
      {
         MInventoryLine[] lines = inv.getLines(true);
         ArrayList<String> prod = new ArrayList<String>();
         
         if(lines == null
               || lines.length <= 0)
            return Msg.getMsg(ctx, "NoLines");
         
         Boolean isInternalUse = (Boolean) inv.get_Value("z_IsInternalUse");
         
         if(isInternalUse != null && isInternalUse)
         {
            for(MInventoryLine line : lines)
            {
               if(line.getM_Product_ID() <=0
                     || line.getM_Locator_ID() <=0)
                  return "Producto o Ubicación invalidos";
               
               if(line.getMovementQty().equals(Env.ZERO))
                  return "Artículos con cantidad zero";
               
               if(line.getC_Charge_ID() == 0)
                  return "Sin cuenta para cargar costos";
               
               if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
                  return "Existen dos o más líneas repetidas";
               else
                  prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());
               
               BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(), line.getM_AttributeSetInstance_ID());
               
               if(qtdOnHand.compareTo(line.getQtyInternalUse()) == -1)
                  return "No hay stock del artículo de la línea=" + line.getLine();
            }
         }
      }
      
      return null;
   }
   
   /**
    *   Validate Shipment/Receipt.
    * 
    *   @param MInOut inventory
    *   @param timing see TIMING_ constants
     *   @return error message or null
    */
   private String docValidate(MInOut inOut, int timing)
   {
      Properties ctx = inOut.getCtx();
      String     trx = inOut.get_TrxName();
      
      if (timing == TIMING_AFTER_COMPLETE)
      {
         String sql = "SELECT C_DocType_ID FROM C_DocType " +
               "WHERE lbr_IsManufactured='Y' AND C_DocType_ID=?";
         
         /**
          * Verifica se é industrialização
          * */
         if(DB.getSQLValue(trx, sql, inOut.getC_DocType_ID()) < 0)
            return null;
         
         MInOutLine[] lines = inOut.getLines();
         
         for(MInOutLine line : lines)
         {
            int C_OrderLine_ID = line.getC_OrderLine_ID();
            
            if(C_OrderLine_ID > 0)
            {
               MOrderLine oLine = new MOrderLine(ctx, C_OrderLine_ID, trx);
               Integer ii = (Integer) oLine.get_Value("M_ProductionLine_ID");
               int M_ProductionLine_ID = 0;
               
               if(ii != null)
                  M_ProductionLine_ID = ii.intValue();
               else
                  continue;
               
               /**   
                * Atualiza a quantidade entregue de industrialização
                * */
               
               DB.executeUpdate("UPDATE M_ProductionLine " +
                     "SET QtyDelivered=COALESCE(QtyDelivered,0)+ " +
                     "(SELECT QtyEntered FROM M_InOutLine " +
                     "WHERE M_InOutLine_ID=" + line.getM_InOutLine_ID() + ") " +
                     "WHERE M_ProductionLine_ID=" + M_ProductionLine_ID, trx);
            }
         }   
      }
      else if ((timing == TIMING_BEFORE_COMPLETE || timing == TIMING_BEFORE_REVERSECORRECT))
      {
         MInOutLine[] lines = inOut.getLines();
         ArrayList<Integer> olines = new ArrayList<Integer>();
         
         if (lines.length == 0)
            return "El Documento no contiene ninguna línea";
   
         for (int i = 0; i < lines.length; i++)
         {
            MInOutLine line = lines[i];
            int M_Product_ID = line.getM_Product_ID();
            int M_Locator_ID = line.getM_Locator_ID();
            int M_AttributeSetInstance_ID = line.getM_AttributeSetInstance_ID();
            BigDecimal onHand = Env.ZERO, qtyToShip = Env.ZERO;
            MProduct produto = MProduct.get(ctx, M_Product_ID);
            
            if (!produto.isStocked())
               continue;
            
            if (M_Locator_ID == 0)
               return "Ubicación no especificada en la línea: #" + line.getLine() + ".";
   
            if (line.getQtyEntered() == Env.ZERO)
               return "Articulo con cantidad CERO en la línea: #" + line.getLine() + ".";
            
            if (!MSysConfig.getBooleanValue("LBR_ALLOW_MM_SHIP_RECEIPT_WITHOUT_ORDER", true, inOut.getAD_Client_ID())
                  && line.getC_OrderLine_ID() == 0)
               return "Orden de Compra no disponible";

            if (!MSysConfig.getBooleanValue("LBR_ALLOW_MM_SHIP_RECEIPT_WITHOUT_INVOICE", true, inOut.getAD_Client_ID())
                  && line.isInvoiced())
               return "Factura no disponible";
            
            MOrderLine oline = new MOrderLine(ctx, line.getC_OrderLine_ID(), trx);
            
            if (timing == TIMING_BEFORE_REVERSECORRECT
                  && !MSysConfig.getBooleanValue("LBR_ALLOW_REVERSE_SHIP_RECEIT_WITH_OPEN_INVOICE", true, inOut.getAD_Client_ID())
                  && oline.getQtyDelivered().subtract(oline.getQtyInvoiced()).doubleValue() == 0)
               return "Facturas pendientes. No es posible revertir";
            
            int C_OrderLine_ID = line.getC_OrderLine_ID();
            if (C_OrderLine_ID != 0)
            {
               if(!MSysConfig.getBooleanValue("LBR_ALLOW_DUPLICATED_ORDERLINE_ON_SHIP_RECEIPT", true, inOut.getAD_Client_ID())
                     && olines.contains("" + line.getC_OrderLine_ID()))
                  return "Línea #" + line.getLine() + " duplicada.";
               else
                  olines.add(line.getC_OrderLine_ID());
            }
            
            /**
             *  FIXME: QtyDelivered é na UDM padrão, QtyEntered pode ser outra,
             *  com isso a comparação, pode não funcionar corretamente.
             * 
             */
            
            log.info("Delivered: " + oline.getQtyDelivered() + " Entered: " + oline.getQtyEntered() + " Trying: " + line.getQtyEntered());
            if (timing == TIMING_BEFORE_COMPLETE
                  && MSysConfig.getBooleanValue("LBR_MATCH_SHIPMENT_RECEIPT_AND_ORDER_QTY", false, inOut.getAD_Client_ID())
                  && oline.getQtyDelivered().add(line.getQtyEntered()).doubleValue() > oline.getQtyEntered().doubleValue())
               return "La Cantidad recibida es mayor que la cantidad de la Orden de Compra. Linea de la Orden #" + line.getLine();
            
            onHand = getQtyOnHand(M_Product_ID, M_Locator_ID, M_AttributeSetInstance_ID); //QtyOnHand
            
            qtyToShip = Env.ZERO;
            
            for (int j = 0; j < lines.length; j++)
            {
               if(lines[j].getM_Product_ID() == line.getM_Product_ID()
                     && lines[j].getM_Locator_ID() == line.getM_Locator_ID())
               {
                  qtyToShip = qtyToShip.add(lines[j].getQtyEntered());
               }
            }
            
            if (timing == TIMING_BEFORE_COMPLETE){
                           
               //Checks if negative stock is allowed
               boolean negativeStockAllowed = true;
               if(!MSysConfig.getBooleanValue("LBR_ALLOW_NEGATIVE_STOCK", true, inOut.getAD_Client_ID())){
                  //Before returning, check if the product can be shipped without qty
                  String allow = line.getProduct().get_ValueAsString("LBR_AllowSaleWithoutStock");
                  if(allow == null || allow.equals("false")){ // era &&
                     negativeStockAllowed = false;
                     //return null;
                  }      
               }
               String movementType = inOut.getMovementType();
               
               if (movementType.charAt(1) == '-')
               {
                  if (timing == TIMING_BEFORE_COMPLETE
                        && (onHand.subtract(qtyToShip).doubleValue() < 0 && !negativeStockAllowed))
                     return "No hay stock del artículo de la línea #" + line.getLine() + ".";
               }
               else
               {
                  if (onHand.add(line.getQtyEntered()).doubleValue() < 0 && !negativeStockAllowed)
                     return "No hay stock del artículo de la línea #" + line.getLine() + ".";
               }
            
            }
            
         } // for;
      }
      
      return null;
   }
      
   /**
    *    Update Info Window Columns.
    *    - add new Columns
    *    - remove columns
    *    - change dispay sequence
    *   @param columns array of columns
    *   @param sqlFrom from clause, can be modified
    *   @param sqlOrder order by clause, can me modified
    *   @return true if you updated columns, sequence or sql From clause
    */
   public boolean updateInfoColumns (ArrayList<Info_Column> columns,
      StringBuffer sqlFrom, StringBuffer sqlOrder)
   {
      /**      *
      int AD_Role_ID = Env.getAD_Role_ID (Env.getCtx());   // Can be Role/User specific
      String from = sqlFrom.toString();
      if (from.startsWith ("M_Product"))
      {
         columns.add (new Info_Column("Header", "'sql'", String.class).seq(35));
         return true;
      }/**   */
      return false;
   }   //   updateInfoColumns
   
   /**
    *    String Representation
    *   @return info
    */
   public String toString ()
   {
      StringBuffer sb = new StringBuffer ("AdempiereLBR - Powered by Kenos & Faire");
      return sb.toString ();
   }   //   toString

} //ValidatorInOut


Re: Alteração ValidatorInOut.java

Enviado: Qua Dez 09, 2009 9:01 am
por edilsondneto
Obrigado Eduardo,
eh isso mesmo, mas teria como fazer o upload para Trunk.
Achei outro erro e estou tentendo resolver. No ADEMPIERE mesmo com esse validator ao completar um recebimento ou envio com um produto com mesmo localizador em duas linhas o sistema nao faz nenhuma critica, você ja testou isso? Estava olhando que tem uma restrição nesse validator mas não fuciona....

Re: Alteração ValidatorInOut.java

Enviado: Qua Dez 09, 2009 2:12 pm
por ralexsander
Edilson,

Você chegou a fazer os testes habilitando as validações no System Configurator?

Ricardo Santana

Re: Alteração ValidatorInOut.java

Enviado: Qua Dez 09, 2009 9:56 pm
por edilsondneto
Ricardo, ja verifiquei todas as variaveis aqui no nesse validator. Ele só controla o mesmo produto em duas linhas diferentes na movimentação e no inventario "estoque fisico". No envio e no recebimento não faz esse controle, mas ja resolvi criei uma variavel System Configurator e codifique o validator para o controlar o pedido, apenas estou fazendo uns testes e ate o final da semana faço o post aqui.


Obrigado desde entao..

Re: Alteração ValidatorInOut.java

Enviado: Ter Dez 15, 2009 11:38 am
por edilsondneto
Alterei novamente essa "classe" e fiz alguns testes e deu certo!

Criei tambem uma variavel no system configuration LBR_ALLOW_TWOLINE_INOUT, para não deixar receber ou entregar um mesmo produtudo em duas linhas diferentes. aqui o codigo

Código: Selecionar todos

/******************************************************************************
 * Product: ADempiereLBR - ADempiere Localization Brazil                      *
 * 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.                     *
 *****************************************************************************/
package org.adempierelbr.validator;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.logging.Level;

import org.compiere.apps.search.Info_Column;
import org.compiere.model.MClient;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInventory;
import org.compiere.model.MInventoryLine;
import org.compiere.model.MMovement;
import org.compiere.model.MMovementLine;
import org.compiere.model.MOrderLine;
import org.compiere.model.MProduct;
import org.compiere.model.MSysConfig;
import org.compiere.model.ModelValidationEngine;
import org.compiere.model.ModelValidator;
import org.compiere.model.PO;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Msg;

/**
 *   ValidatorInOut, inclui as validações de outras tabelas que
 *   manipulam materiais, como Movimentações, Inventário e Entrada/Saída.
 *     
 *   @author Ricardo Santana (ralexsander)
 *   @contributor Edilson Duarte (Grupo LCR)
 *   @version $Id: ValidatorInOut.java, 14/12/2009 10:00:00 edilsondneto
 */
public class ValidatorInOut implements ModelValidator
{
   /**
    *   Constructor.
    *   The class is instanciated when logging in and client is selected/known
    */
   public ValidatorInOut ()
   {
      super ();
   }   //ValidatorInOut
   
   /**   Logger         */
   private static CLogger log = CLogger.getCLogger(ValidatorInOut.class);
   /** Client         */
   private int      m_AD_Client_ID = -1;
   
   
   /**
    *   Initialize Validation
    *   @param engine validation engine
    *   @param client client
    */
   public void initialize (ModelValidationEngine engine, MClient client)
   {
      m_AD_Client_ID = client.getAD_Client_ID();

      log.info(client.toString());

      //   DocValidate
      engine.addDocValidate("M_InOut", this);
      engine.addDocValidate("M_Movement", this);
      engine.addDocValidate("M_Inventory", this);

   }   //   initialize

   /**
    *   Get Client to be monitored
    *   @return AD_Client_ID client
    */
   public int getAD_Client_ID()
   {
      return m_AD_Client_ID;
   }   //   getAD_Client_ID

   /**
    *   User Login.
    *   Called when preferences are set
    *   @param AD_Org_ID org
    *   @param AD_Role_ID role
    *   @param AD_User_ID user
    *   @return error message or null
    */
   public String login (int AD_Org_ID, int AD_Role_ID, int AD_User_ID)
   {
      log.info("AD_User_ID=" + AD_User_ID);
      return null;
   }   //   login
   
    /**
     *   Model Change of a monitored Table.
     *   Called after PO.beforeSave/PO.beforeDelete
     *   when you called addModelChange for the table
     *   @param po persistent object
     *   @param type TYPE_
     *   @return error message or null
     *   @exception Exception if the recipient wishes the change to be not accept.
     */
   public String modelChange (PO po, int type) throws Exception
   {
      return null;
   }   //   modelChange

   /**
    *   Validate Document.
    *   Called as first step of DocAction.prepareIt
     *   when you called addDocValidate for the table.
     *   Note that totals, etc. may not be correct.
    *   @param po persistent object
    *   @param timing see TIMING_ constants
     *   @return error message or null
    */
   public String docValidate (PO po, int timing)
   {
      if (po.get_TableName().equalsIgnoreCase("M_InOut"))
         return docValidate((MInOut) po, timing);
      
      else if (po.get_TableName().equalsIgnoreCase("M_Movement"))
         return docValidate((MMovement) po, timing);
      
      else if (po.get_TableName().equalsIgnoreCase("M_Inventory"))
         return docValidate((MInventory) po, timing);

      
      return null;
   }   //   docValidate
   
   /**
    *   Quantity On Hand.
    *   
    *   @param M_Product_ID
    *   @param M_Locator_ID
    *  @param M_AttributeSetInstance_ID
     *   @return BigDecimal quantity of product
    */
   private BigDecimal getQtyOnHand(int M_Product_ID, int M_Locator_ID, int M_AttributeSetInstance_ID)

   {
      BigDecimal QtyOnHand = Env.ZERO;
      String sql = "SELECT SUM(QtyOnHand) FROM M_Storage "
         + "WHERE M_Product_ID=?";   //   1
      if(M_Locator_ID > 0)
         sql = sql + " AND M_Locator_ID=?";   //   2


      if(M_AttributeSetInstance_ID > 0)
         sql = sql + " AND M_AttributeSetInstance_ID=?";   //   3
      /***/
      
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      try
      {
         pstmt = DB.prepareStatement(sql, null);
         pstmt.setInt(1, M_Product_ID);
         if(M_Locator_ID > 0)
            pstmt.setInt(2, M_Locator_ID);
         
         if (M_AttributeSetInstance_ID != 0)
            pstmt.setInt (3, M_AttributeSetInstance_ID);
         
         rs = pstmt.executeQuery();
         if (rs.next())
         {
            QtyOnHand = rs.getBigDecimal(1);
         }
      }
      catch (SQLException e)
      {
         log.log(Level.SEVERE, sql, e);
         return Env.ZERO;
      }
      finally{
             DB.close(rs, pstmt);
      }
      
      if(QtyOnHand != null)
         return QtyOnHand;
      
      return Env.ZERO;
   }   //   QtyOnHand
   
   /**
    *   Validate Movement.
    * 
    *   @param MMovement movement
    *   @param timing see TIMING_ constants
     *   @return error message or null movimentação de material
    */
   private String docValidate(MMovement mov, int timing)
   {
      Properties ctx = mov.getCtx();
      
      if (timing == TIMING_BEFORE_COMPLETE)
      {
         MMovementLine[] lines = mov.getLines(true);
         ArrayList<String> prod = new ArrayList<String>();
         
         if(lines == null
               || lines.length <= 0)
            return Msg.getMsg(ctx, "NoLines");
         
         for(MMovementLine line : lines)
         {
            if(line.getM_Product_ID() <=0
                  || line.getM_Locator_ID() <=0)
               return "Produto ou Localizador inválido";
            
            if (line.getM_AttributeSetInstance_ID() != 0)
            {
              if(((line.getM_Product_ID() > 0) && (line.getM_Locator_ID() > 0))
                 && (line.getM_AttributeSetInstance_ID() <=0))
                  
               return "Atributo Necessário";
            }
         
                        
            if(line.getMovementQty().equals(Env.ZERO))
               return "Itens com qtd zero";
            
            
            if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
               return "Duas linhas usando o mesmo produto na mesma posição";
            else
               prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());
                     
         
            if (line.getM_AttributeSetInstance_ID() != 0)
            {
               if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" +  line.getM_AttributeSetInstance_ID()))
                  return "Duas linhas usando o mesmo produto na mesma posição";
               else
                  prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" +  line.getM_AttributeSetInstance_ID());
            }   
            
            BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(), line.getM_AttributeSetInstance_ID() );

            
            if(qtdOnHand.compareTo(line.getMovementQty()) == -1)
               return "Sem saldo na linha=" + line.getLine();
         }
      }
      
      return null;
   }
   
   
   /**
    *   Quantity On line in Shipment/Receipt
    *
    *   @param M_InOut_id   
    *   @param M_Product_ID
    *   @param M_Locator_ID
    *  @param M_AttributeSetInstance_ID
     *   @return BigDecimal quantity of product
    */
   private BigDecimal getQtyinLine(int M_InOut_id, int M_Product_ID, int M_Locator_ID, int M_AttributeSetInstance_ID)
   {
      BigDecimal QtyinLine = Env.ZERO;

      String sql =   "SELECT count(*) FROM  M_InOutLine il, M_InOut i where "
                       + "i.M_InOut_id=il.M_InOut_id " //AND "i.DocStatus IN ('CO','CL')"
                   + "AND il.M_InOut_id =? "
                  + "AND il.M_Product_ID =? ";
                
                   if(M_Locator_ID > 0)
                     sql = sql + "AND il.M_Locator_ID =? "; //2
                  
                  if(M_AttributeSetInstance_ID > 0)
                     sql = sql + "AND il.M_attributesetinstance_ID =? ";//3
                  
                  sql =  sql +  "group by   i.M_InOut_id,  il.M_Product_ID,  il.M_Locator_ID,"
                              +  "il.M_AttributeSetInstance_ID";
       
       
      
      PreparedStatement pstmt = null;
      ResultSet rs = null;
      try
      {
         pstmt = DB.prepareStatement(sql, null);
         
         pstmt.setInt(1, M_InOut_id);
         
         pstmt.setInt(2, M_Product_ID);
         
         if(M_Locator_ID > 0)
            pstmt.setInt(3, M_Locator_ID);
         
         
         if (M_AttributeSetInstance_ID > 0 )
            pstmt.setInt (4, M_AttributeSetInstance_ID);
         
         
         rs = pstmt.executeQuery();
         if (rs.next())
         {
            QtyinLine = rs.getBigDecimal(1);
         }
      }
      catch (SQLException e)
      {
         log.log(Level.SEVERE, sql, e);
         return Env.ZERO;
      }
      finally{
             DB.close(rs, pstmt);
      }
      
      if(QtyinLine != null)
         return QtyinLine;
      
      return Env.ZERO;
   }   //   QtyinLine
   
   


   
   
   /**
    *   Validate Inventory.
    * 
    *   @param MInventory inventory
    *   @param timing see TIMING_ constants
     *   @return error message or null 2 inventario
    */
   private String docValidate(MInventory inv, int timing)
   {
      Properties ctx = inv.getCtx();
      
      if (timing == TIMING_AFTER_COMPLETE)
      {
         MInventoryLine[] lines = inv.getLines(true);
         ArrayList<String> prod = new ArrayList<String>();
         
         if(lines == null
               || lines.length <= 0)
            return Msg.getMsg(ctx, "NoLines");
         
         Boolean isInternalUse = (Boolean) inv.get_Value("z_IsInternalUse");
         
         if(isInternalUse != null && isInternalUse)
         {
            for(MInventoryLine line : lines)
            {
               if(line.getM_Product_ID() <=0
                     || line.getM_Locator_ID() <=0)
                  return "Produto ou Localizador inválido";
               
               if(line.getMovementQty().equals(Env.ZERO))
                  return "Itens com qtd zero";
               
               if(line.getC_Charge_ID() == 0)
                  return "Sem conta de destino";

               
               
               if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))
                  return "Duas linhas usando o mesmo produto na mesma posição";
               else
                  prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());

               if (line.getM_AttributeSetInstance_ID() != 0)
               {                              
                  if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" +  line.getM_AttributeSetInstance_ID()))
                     return "Duas linhas usando o mesmo produto na mesma posição";
                  else
                     prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" +  line.getM_AttributeSetInstance_ID());
               }

               BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(),line.getM_AttributeSetInstance_ID());

               
               if(qtdOnHand.compareTo(line.getQtyInternalUse()) == -1)
                  return "Sem saldo na linha=" + line.getLine();
            }
         }
      }
      
      return null;
   }
   
   /**
    *   Validate Shipment/Receipt.
    * 
    *   @param MInOut inventory
    *   @param timing see TIMING_ constants
     *   @return error message or null expedição e recebimento 3
    */
   private String docValidate(MInOut inOut, int timing)
   {
      Properties ctx = inOut.getCtx();
      String     trx = inOut.get_TrxName();
      
      if (timing == TIMING_AFTER_COMPLETE)
      {
         String sql = "SELECT C_DocType_ID FROM C_DocType " +
               "WHERE lbr_IsManufactured='Y' AND C_DocType_ID=?";
         
         /**
          * Verifica se é industrialização
          * */
         if(DB.getSQLValue(trx, sql, inOut.getC_DocType_ID()) < 0)
            return null;
         
         MInOutLine[] lines = inOut.getLines();
         
         for(MInOutLine line : lines)
         {
            int C_OrderLine_ID = line.getC_OrderLine_ID();
            
            if(C_OrderLine_ID > 0)
            {
               MOrderLine oLine = new MOrderLine(ctx, C_OrderLine_ID, trx);
               Integer ii = (Integer) oLine.get_Value("M_ProductionLine_ID");
               int M_ProductionLine_ID = 0;
               
               if(ii != null)
                  M_ProductionLine_ID = ii.intValue();
               else
                  continue;
               
               /**   
                * Atualiza a quantidade entregue de industrialização
                * */
               
               DB.executeUpdate("UPDATE M_ProductionLine " +
                     "SET QtyDelivered=COALESCE(QtyDelivered,0)+ " +
                     "(SELECT QtyEntered FROM M_InOutLine " +
                     "WHERE M_InOutLine_ID=" + line.getM_InOutLine_ID() + ") " +
                     "WHERE M_ProductionLine_ID=" + M_ProductionLine_ID, trx);
            }
         }   
      }
      else if ((timing == TIMING_BEFORE_COMPLETE || timing == TIMING_BEFORE_REVERSECORRECT))
      {
         MInOutLine[] lines = inOut.getLines();
         ArrayList<Integer> olines = new ArrayList<Integer>();
         
         if (lines.length == 0)
            return "Documento sem linhas";
   
         for (int i = 0; i < lines.length; i++)
         {
            MInOutLine line = lines[i];
            int M_Product_ID = line.getM_Product_ID();
            int M_Locator_ID = line.getM_Locator_ID();
            int M_AttributeSetInstance_ID = line.getM_AttributeSetInstance_ID();
               
            
            
            BigDecimal onHand = Env.ZERO, qtyToShip = Env.ZERO;
            MProduct produto = MProduct.get(ctx, M_Product_ID);
            
            if (!produto.isStocked())
               continue;
            
            
         
            if (M_Locator_ID == 0)
               return "Localizador do estoque não definida na linha: #" + line.getLine() + ".";
   
            if (line.getQtyEntered() == Env.ZERO)
               return "Item com quantidade ZERO na linha: #" + line.getLine() + ".";
            
            if (!MSysConfig.getBooleanValue("LBR_ALLOW_MM_SHIP_RECEIPT_WITHOUT_ORDER", true, inOut.getAD_Client_ID())
                  && line.getC_OrderLine_ID() == 0)
               return "Ordem de Compra não disponível.";
            
            MOrderLine oline = new MOrderLine(ctx, line.getC_OrderLine_ID(), trx);
            
            if (timing == TIMING_BEFORE_REVERSECORRECT
                  && !MSysConfig.getBooleanValue("LBR_ALLOW_REVERSE_SHIP_RECEIT_WITH_OPEN_INVOICE", true, inOut.getAD_Client_ID())
                  && DB.getSQLValue(null, "SELECT COUNT(*) FROM C_InvoiceLine il, C_Invoice i WHERE i.C_Invoice_ID=il.C_Invoice_ID AND i.DocStatus IN ('CO','CL') AND il.M_InOutLine_ID=?", line.getM_InOutLine_ID()) > 0)
               return "Fatura(s) em aberto. Impossível continuar com o estorno.";

      
                        
                        
            
            int C_OrderLine_ID = line.getC_OrderLine_ID();
            if (C_OrderLine_ID != 0)
            {
               if(!MSysConfig.getBooleanValue("LBR_ALLOW_DUPLICATED_ORDERLINE_ON_SHIP_RECEIPT", true, inOut.getAD_Client_ID())
                     && olines.contains("" + line.getC_OrderLine_ID()))
                  return "Linha #" + line.getLine() + " duplicada da mesma ordem.";
               else
                  olines.add(line.getC_OrderLine_ID());
            }
            
            /**
             *  FIXME: QtyDelivered é na UDM padrão, QtyEntered pode ser outra,
             *  com isso a comparação, pode não funcionar corretamente.
             * 
             */
            
            log.info("Delivered: " + oline.getQtyDelivered() + " Entered: " + oline.getQtyEntered() + " Trying: " + line.getQtyEntered());
            if (timing == TIMING_BEFORE_COMPLETE
                  && MSysConfig.getBooleanValue("LBR_MATCH_SHIPMENT_RECEIPT_AND_ORDER_QTY", false, inOut.getAD_Client_ID())
                  && oline.getQtyDelivered().add(line.getQtyEntered()).doubleValue() > oline.getQtyEntered().doubleValue())
               return "Nao e possivel fazer recebimento maior que o pedido. Linha do pedido #" + line.getLine();
            
            if (M_Product_ID == 0)
               continue;

            onHand = getQtyOnHand(M_Product_ID, M_Locator_ID, M_AttributeSetInstance_ID); //QtyOnHand
            
            
            
            qtyToShip = Env.ZERO;
            
            for (int j = 0; j < lines.length; j++)
            {
               if(lines[j].getM_Product_ID() == line.getM_Product_ID()
                     && lines[j].getM_Locator_ID() == line.getM_Locator_ID()
                     && lines[j].getM_AttributeSetInstance_ID() == line.getM_AttributeSetInstance_ID())
               {
                  qtyToShip = qtyToShip.add(lines[j].getQtyEntered());
               }
            }
            

            BigDecimal qtdInLine = getQtyinLine(line.getM_InOut_ID(), line.getM_Product_ID(), line.getM_Locator_ID(),line.getM_AttributeSetInstance_ID());

            if (!MSysConfig.getBooleanValue("LBR_ALLOW_TWOLINE_INOUT", true, inOut.getAD_Client_ID())
                  && (qtdInLine.intValue() > 1))
               return "Linha #" + line.getLine() + " duplicada.";
            

            
            
            if (timing == TIMING_BEFORE_COMPLETE
                  && !MSysConfig.getBooleanValue("LBR_ALLOW_NEGATIVE_STOCK", true, inOut.getAD_Client_ID())){
                           
               String movementType = inOut.getMovementType();
               
               if (movementType.charAt(1) == '-')
               {
                  if (timing == TIMING_BEFORE_COMPLETE
                        && onHand.subtract(qtyToShip).doubleValue() < 0)
                     return "Sem quantidade disponivel na linha #" + line.getLine() + ".";
               }
               else
               {
                  if (onHand.add(line.getQtyEntered()).doubleValue() < 0)
                     return "Sem quantidade disponível na linha #" + line.getLine() + ".";
               }
               
                  
               
               
            
            }
            
            
         } // for;
      }
      
      return null;
   }
      
   /**
    *    Update Info Window Columns.
    *    - add new Columns
    *    - remove columns
    *    - change dispay sequence
    *   @param columns array of columns
    *   @param sqlFrom from clause, can be modified
    *   @param sqlOrder order by clause, can me modified
    *   @return true if you updated columns, sequence or sql From clause
    */
   public boolean updateInfoColumns (ArrayList<Info_Column> columns,
      StringBuffer sqlFrom, StringBuffer sqlOrder)
   {
      /**      *
      int AD_Role_ID = Env.getAD_Role_ID (Env.getCtx());   // Can be Role/User specific
      String from = sqlFrom.toString();
      if (from.startsWith ("M_Product"))
      {
         columns.add (new Info_Column("Header", "'sql'", String.class).seq(35));
         return true;
      }/**   */
      return false;
   }   //   updateInfoColumns
   
   /**
    *    String Representation
    *   @return info
    */
   public String toString ()
   {
      StringBuffer sb = new StringBuffer ("AdempiereLBR - Powered by Kenos & Faire");
      return sb.toString ();
   }   //   toString

} //ValidatorInOut

Re: Alteração ValidatorInOut.java

Enviado: Seg Jan 17, 2011 9:22 am
por paulo_dantas13
Bom dia à todos,

alterei o código do Edílson, pois verifiquei que ao realizar uma movimentação de estoque possuíndo o mesmo produto em mais de uma linha, não era completado.

Ao debugar o código, realizei a mudança no método Validate Movement na classe ValidatorInOut:


/**
* Validate Movement.
*
* @param MMovement movement
* @param timing see TIMING_ constants
* @return error message or null movimentação de material
*/
private String docValidate(MMovement mov, int timing)
{
Properties ctx = mov.getCtx();

if (timing == TIMING_BEFORE_COMPLETE)
{
MMovementLine[] lines = mov.getLines(true);
ArrayList<String> prod = new ArrayList<String>();

if(lines == null
|| lines.length <= 0)
return Msg.getMsg(ctx, "NoLines");

for(MMovementLine line : lines)
{
if(line.getM_Product_ID() <=0
|| line.getM_Locator_ID() <=0)
return "Produto ou Localizador inválido";

if (line.getM_AttributeSetInstance_ID() != 0)
{
if(((line.getM_Product_ID() > 0) && (line.getM_Locator_ID() > 0))
&& (line.getM_AttributeSetInstance_ID() <=0))

return "Atributo Necessário";
}


if(line.getMovementQty().equals(Env.ZERO))
return "Itens com qtd zero";


if (line.getM_AttributeSetInstance_ID() != 0)
{
if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" + line.getM_AttributeSetInstance_ID()))
return "Duas linhas usando o mesmo produto na mesma posição";
else
prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID() + "|" + line.getM_AttributeSetInstance_ID());
} else {
if(prod.contains("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID()))//Código que localizava-se antes do if (line.getM_AttributeSetInstance_ID() != 0)
return "Duas linhas usando o mesmo produto na mesma posição";
else
prod.add("" + line.getM_Product_ID() + "|" + line.getM_Locator_ID());

}

BigDecimal qtdOnHand = getQtyOnHand(line.getM_Product_ID(), line.getM_Locator_ID(), line.getM_AttributeSetInstance_ID() );


if(qtdOnHand.compareTo(line.getMovementQty()) == -1)
return "Sem saldo na linha=" + line.getLine();
}
}

return null;
}

Ele estava comparando duas vezes a mesma coisa, porém, quando coloquei um else no attributesetinstance !=0 com o código antes deste if, deu certo.

Alguma observação no código?

Grato pela atenção.