1

It's possible that in Adempiere, Inventory Stock become negative. One of the way to make it become negative is when we put Quantity in Internal Use Inventory more than Available Stock in Warehouse.

Example

Product Info
------------
Product    || Qty
Fertilizer || 15

It's shown in Product Info that the current Qty of Fertilizer is 15. Then I make Internal Use Inventory document

Internal Use Inventory
----------------------
Product    || Qty
Fertilizer || 25

When I complete it, Quantity will be -10. How can I prevent Internal Use Inventory being completed when Quantity is more than Available Stock so that I can avoid negative stock ?

Jedi Codestar
  • 189
  • 2
  • 14
  • it will be a better practice, if you can share if the solutions are inappropirate or if they worked voting them up and marking them as correct will help future users for same question.. – JavaDragon Oct 29 '15 at 06:31

3 Answers3

2

This is purposely designed functionality of Adempiere. Under some scenarios the Inventory is allowed to become negative because it is felt in those scenarios it is better to allow the process to complete but being negative it highlights a problem that must be addressed. In the case of the Internal Use the user is warned, that the stock will go negative if they proceed.

To change this standard functionality you need to modify the

org.compiere.model.MInventory.completeIt()

But if you change the code directly, it will make it more difficult to keep your version in sync with the base Adempiere or even just applying patches.

The recommended approach would be to add a Model Validator. This is a mechanism that watches the underlying data model and enables additional code/logic to be injected when a specific event occurs.

The event you want is the Document Event TIMING_BEFORE_COMPLETE. You would create a new model validator as described in the link, register it in Adempiere's Application Dictionary and since you want your code to trigger when Inventory Document Type is executed you would add a method something like this

public String docValidate (PO po, int timing)
{
   if (timing == TIMING_BEFORE_COMPLETE) {
      if (po.get_TableName().equals(MInventory.Table_Name))
      {
          // your code to be executed 
          // it is executed just before any (internal or physical)
          // Inventory is Completed
      }
   }
   return null;
} //    docValidate

A word of warning; the Internal Use functionality is the same used by Physical Inventory (i.e. a stock count) functionality! They just have different windows in Adempiere. So be sure to test both functionalities after any change is applied. From the core org.compiere.model.MInventory there is a hint as how you might differentiate the two.

//Get Quantity Internal Use
BigDecimal qtyDiff = line.getQtyInternalUse().negate();
//If Quantity Internal Use = Zero Then Physical Inventory  Else Internal Use Inventory
if (qtyDiff.signum() == 0)
Colin Rooney
  • 439
  • 7
  • 15
0

In order to prevent the stock from being negative you can use two methods

  1. Callout in Code
  2. BeforeSave Method
  1. In order to apply it in Callout you need to create a Callout class and get the current Stock Qty at the locator there and then subtract the qty from entered Qty and if the result is less than 0 , display the error. Apply this on the Qty field and you will get the desired result.

  2. This is slightly better way , as this doesn't needs creating a new class in code altogether and will consume less memory altogether , Search for MInventoryLine class in code and then search for beforesave() in it. Add the same code (getting the stock and then subtracting it from the entered Qty). The Code in beforesave() will be like this

        if (your condition here)    {   log.saveError("Could Not Save - Error", "Qty less than 0");  return false;      }
    

Now I am assuming that you know the basic code to create a Callout and design a condition, If you need any help , let me know.

JavaDragon
  • 431
  • 1
  • 6
  • 17
  • 2
    The problem with using a Callout for something like this is it only executes in the UI - so if the stock were being reduced by a process (in the background) then the rule would not be enforced. Use Callouts when you want to impact the UI -Model Validators when you want to enforce a new rule of your implementation. – Colin Rooney Oct 19 '15 at 08:47
0

Adding to the answer of Mr. Colin, please see the below code to restrict the negative inventory from the M_Inventory based transaction. You can consider the same concept in M_Inout and M_Movement Tables also.

for (MInventoryLine line : mInventory.getLines(true))
            {           
                String blockNegativeQty = MSysConfig.getValue("BLOCK_NEGATIVE_QUANTITY_IN_MATERIAL_ISSUE","Y", getAD_Client_ID());
                if(blockNegativeQty.equals("Y"))
                {           

                    //Always check the on Hand Qty not Qty Book Value. The old Drafted Qty Book Value may be changed alredy. 
                    String sql = "SELECT adempiere.bomqtyonhand(?, ?, ?)"; 
                    BigDecimal onhandQty = DB.getSQLValueBD(line.get_TrxName(), sql, line.getM_Product_ID(), mInventory.getM_Warehouse_ID()
                                    , line.getM_Locator_ID());

                    BigDecimal QtyMove = onhandQty.subtract(line.getQtyInternalUse());

                    if(QtyMove.compareTo(Env.ZERO) < 0)
                    {
                        log.warning("Negative Movement Quantity is restricted. Qty On Hand = " + onhandQty 
                                + " AND Qty Internal Use = " + line.getQtyInternalUse() 
                                + ". The Movement Qty is = " + QtyMove);

                        negativeCount ++;

                        negativeProducts = negativeProducts + line.getM_Product().getName() + ": " + onhandQty + " ; ";                         
                    }
                }                   
            }
            if(negativeCount > 0)
            {
                m_processMsg = "Negative Inventory Restricted. "
                        + " Restriction has been enabled through the client level system configurator."
                        + " Products : " + negativeProducts;
            }
            return m_processMsg;
Sajeev
  • 401
  • 1
  • 4
  • 15