IDEMPIERE-5812 Reversal of Material Receipt for a Closed PO leave quantity ordered of product in a bad state (#1964)
This commit is contained in:
parent
472032fa44
commit
e46d57e1ea
|
@ -1643,8 +1643,8 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved()
|
if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved()
|
||||||
+ ", Delivered=" + oLine.getQtyDelivered());
|
+ ", Delivered=" + oLine.getQtyDelivered());
|
||||||
}
|
}
|
||||||
|
boolean orderClosed = oLine != null && DocAction.STATUS_Closed.equals(oLine.getParent().getDocStatus());
|
||||||
|
|
||||||
// Load RMA Line
|
// Load RMA Line
|
||||||
MRMALine rmaLine = null;
|
MRMALine rmaLine = null;
|
||||||
|
|
||||||
|
@ -1726,7 +1726,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Storage - see also VMatch.createMatchRecord
|
// Update Storage - see also Match.createMatchRecord
|
||||||
if (!MStorageOnHand.add(getCtx(),
|
if (!MStorageOnHand.add(getCtx(),
|
||||||
sLine.getM_Locator_ID(),
|
sLine.getM_Locator_ID(),
|
||||||
sLine.getM_Product_ID(),
|
sLine.getM_Product_ID(),
|
||||||
|
@ -1760,8 +1760,8 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oLine!=null && mtrx!=null &&
|
if (oLine!=null && mtrx!=null && !orderClosed &&
|
||||||
((!isReversal() && oLine.getQtyReserved().signum() > 0) || (isReversal() && oLine.getQtyOrdered().signum() > 0)))
|
((!isReversal() && oLine.getQtyReserved().signum() > 0) || (isReversal() && oLine.getQtyOrdered().signum() > 0)))
|
||||||
{
|
{
|
||||||
if (sLine.getC_OrderLine_ID() != 0 && oLine.getM_Product_ID() > 0)
|
if (sLine.getC_OrderLine_ID() != 0 && oLine.getM_Product_ID() > 0)
|
||||||
|
@ -1847,7 +1847,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
if (dateMPolicy == null)
|
if (dateMPolicy == null)
|
||||||
dateMPolicy = getMovementDate();
|
dateMPolicy = getMovementDate();
|
||||||
|
|
||||||
// Fallback: Update Storage - see also VMatch.createMatchRecord
|
// Fallback: Update Storage - see also Match.createMatchRecord
|
||||||
if (pendingQty.signum() != 0 &&
|
if (pendingQty.signum() != 0 &&
|
||||||
!MStorageOnHand.add(getCtx(),
|
!MStorageOnHand.add(getCtx(),
|
||||||
sLine.getM_Locator_ID(),
|
sLine.getM_Locator_ID(),
|
||||||
|
@ -1859,7 +1859,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError;
|
m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError;
|
||||||
return DocAction.STATUS_Invalid;
|
return DocAction.STATUS_Invalid;
|
||||||
}
|
}
|
||||||
if (oLine!=null && oLine.getM_Product_ID() > 0 &&
|
if (oLine!=null && oLine.getM_Product_ID() > 0 && !orderClosed &&
|
||||||
((!isReversal() && oLine.getQtyReserved().signum() > 0) || (isReversal() && oLine.getQtyOrdered().signum() > 0)))
|
((!isReversal() && oLine.getQtyReserved().signum() > 0) || (isReversal() && oLine.getQtyOrdered().signum() > 0)))
|
||||||
{
|
{
|
||||||
IReservationTracer tracer = null;
|
IReservationTracer tracer = null;
|
||||||
|
@ -1903,7 +1903,7 @@ public class MInOut extends X_M_InOut implements DocAction, IDocsPostProcess
|
||||||
} // stock movement
|
} // stock movement
|
||||||
|
|
||||||
// Correct Order Line
|
// Correct Order Line
|
||||||
if (product != null && oLine != null) // other in VMatch.createMatchRecord
|
if (product != null && oLine != null && !orderClosed) // other in Match.createMatchRecord
|
||||||
{
|
{
|
||||||
if (oLine.getQtyOrdered().signum() >= 0)
|
if (oLine.getQtyOrdered().signum() >= 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.adempiere.base.IProductPricing;
|
||||||
import org.adempiere.exceptions.AdempiereException;
|
import org.adempiere.exceptions.AdempiereException;
|
||||||
import org.adempiere.exceptions.ProductNotOnPriceListException;
|
import org.adempiere.exceptions.ProductNotOnPriceListException;
|
||||||
import org.adempiere.model.ITaxProvider;
|
import org.adempiere.model.ITaxProvider;
|
||||||
|
import org.compiere.process.DocAction;
|
||||||
import org.compiere.util.CLogger;
|
import org.compiere.util.CLogger;
|
||||||
import org.compiere.util.DB;
|
import org.compiere.util.DB;
|
||||||
import org.compiere.util.Env;
|
import org.compiere.util.Env;
|
||||||
|
@ -897,6 +898,20 @@ public class MOrderLine extends X_C_OrderLine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//sync qtyordered and qtylostsales for closed order
|
||||||
|
if (!newRecord && DocAction.STATUS_Closed.equals(getParent().getDocStatus()) && is_ValueChanged(COLUMNNAME_QtyDelivered)
|
||||||
|
&& !getParent().is_ValueChanged(MOrder.COLUMNNAME_DocStatus)) {
|
||||||
|
if (getQtyOrdered().compareTo(getQtyDelivered()) > 0)
|
||||||
|
{
|
||||||
|
setQtyLostSales(getQtyLostSales().add(getQtyOrdered().subtract(getQtyDelivered())));
|
||||||
|
setQtyOrdered(getQtyDelivered());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setQtyLostSales(Env.ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // beforeSave
|
} // beforeSave
|
||||||
|
|
||||||
|
|
|
@ -960,4 +960,92 @@ public class MatchPOTest extends AbstractTestCase {
|
||||||
receipt.load(getTrxName());
|
receipt.load(getTrxName());
|
||||||
assertEquals(0, receipt.getC_Order_ID(), "Material receipt: order not clear after void of purchase order");
|
assertEquals(0, receipt.getC_Order_ID(), "Material receipt: order not clear after void of purchase order");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReverseReceiptAfterClosePO() {
|
||||||
|
MBPartner bpartner = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.TREE_FARM.id);
|
||||||
|
MProduct product = MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
|
||||||
|
//Create PO of 4
|
||||||
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
|
order.setBPartner(bpartner);
|
||||||
|
order.setIsSOTrx(false);
|
||||||
|
order.setC_DocTypeTarget_ID();
|
||||||
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
order.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
order.saveEx();
|
||||||
|
|
||||||
|
MOrderLine orderLine = new MOrderLine(order);
|
||||||
|
orderLine.setLine(10);
|
||||||
|
orderLine.setProduct(product);
|
||||||
|
orderLine.setQty(new BigDecimal("4"));
|
||||||
|
orderLine.saveEx();
|
||||||
|
|
||||||
|
BigDecimal qtyOrdered = MStorageReservation.getQty(product.get_ID(), order.getM_Warehouse_ID(), 0, false, getTrxName());
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
|
||||||
|
|
||||||
|
BigDecimal qtyOrdered1 = MStorageReservation.getQty(product.get_ID(), order.getM_Warehouse_ID(), 0, false, getTrxName());
|
||||||
|
assertEquals(4, qtyOrdered1.subtract(qtyOrdered).intValue(), "QtyOrdered not increase as expected");
|
||||||
|
|
||||||
|
//Create MR
|
||||||
|
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_RECEIPT.id, order.getDateOrdered()); // MM Receipt
|
||||||
|
receipt.saveEx();
|
||||||
|
|
||||||
|
MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID());
|
||||||
|
int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
|
||||||
|
|
||||||
|
MInOutLine receiptLine = new MInOutLine(receipt);
|
||||||
|
receiptLine.setOrderLine(orderLine, M_Locator_ID, new BigDecimal("2"));
|
||||||
|
receiptLine.setLine(10);
|
||||||
|
receiptLine.setQty(new BigDecimal("2"));
|
||||||
|
receiptLine.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
|
||||||
|
|
||||||
|
qtyOrdered1 = MStorageReservation.getQty(product.get_ID(), order.getM_Warehouse_ID(), 0, false, getTrxName());
|
||||||
|
assertEquals(qtyOrdered.intValue()+2, qtyOrdered1.intValue(), "QtyOrdered not release as expected");
|
||||||
|
|
||||||
|
orderLine.load(getTrxName());
|
||||||
|
assertEquals(2, orderLine.getQtyDelivered().intValue(), "Unexpected QtyDelivered");
|
||||||
|
assertEquals(2, orderLine.getQtyReserved().intValue(), "Unexpected QtyReserved");
|
||||||
|
|
||||||
|
//Close PO
|
||||||
|
order.load(getTrxName());
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Close);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Closed, order.getDocStatus());
|
||||||
|
orderLine.load(getTrxName());
|
||||||
|
assertEquals(4, orderLine.getQtyEntered().intValue(), "Unexpected QtyEntered");
|
||||||
|
assertEquals(2, orderLine.getQtyDelivered().intValue(), "Unexpected QtyDelivered");
|
||||||
|
assertEquals(2, orderLine.getQtyOrdered().intValue(), "Unexpected QtyOrdered");
|
||||||
|
assertEquals(2, orderLine.getQtyLostSales().intValue(), "Unexpected QtyLostSales");
|
||||||
|
assertEquals(0, orderLine.getQtyReserved().intValue(), "Unexpected QtyReserved");
|
||||||
|
|
||||||
|
qtyOrdered1 = MStorageReservation.getQty(product.get_ID(), order.getM_Warehouse_ID(), 0, false, getTrxName());
|
||||||
|
assertEquals(qtyOrdered.intValue(), qtyOrdered1.intValue(), "Unexpected change in QtyOrdered");
|
||||||
|
|
||||||
|
//Reverse MR
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, receipt.getDocStatus());
|
||||||
|
|
||||||
|
orderLine.load(getTrxName());
|
||||||
|
assertEquals(4, orderLine.getQtyEntered().intValue(), "Unexpected QtyEntered");
|
||||||
|
assertEquals(0, orderLine.getQtyDelivered().intValue(), "Unexpected QtyDelivered");
|
||||||
|
assertEquals(0, orderLine.getQtyOrdered().intValue(), "Unexpected QtyOrdered");
|
||||||
|
assertEquals(4, orderLine.getQtyLostSales().intValue(), "Unexpected QtyLostSales");
|
||||||
|
assertEquals(0, orderLine.getQtyReserved().intValue(), "Unexpected QtyReserved");
|
||||||
|
|
||||||
|
qtyOrdered1 = MStorageReservation.getQty(product.get_ID(), order.getM_Warehouse_ID(), 0, false, getTrxName());
|
||||||
|
assertEquals(qtyOrdered.intValue(), qtyOrdered1.intValue(), "Unexpected change in QtyOrdered");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue