Consegui resolver o problema e parece que esta OK, preciso completar mais alguns testcases mas a principio funcionou, ficou bem específico, não sei se serve para todos os casos.
A implementação ficou apenas no MOrder.reserveStock e eu inclui uma nova coluna M_AttributeSetInstanceTo_ID (integer, default 0), com os métodos get e set no Model do PO, na C_OrderLine para comparar com a M_AttributeSetInstance_ID, se os valores forem validos e distintos na comparação das duas colunas, eu chamo o MStorage.add para os dois Atributos de Instância. Segue o código completo:
private boolean reserveStock (MDocType dt, MOrderLine[] lines)
{
if (dt == null)
dt = MDocType.get(getCtx(), getC_DocType_ID());
// Binding
boolean binding = !dt.isProposal();
// Not binding - i.e. Target=0
if (DOCACTION_Void.equals(getDocAction())
// Closing Binding Quotation
|| (MDocType.DOCSUBTYPESO_Quotation.equals(dt.getDocSubTypeSO())
&& DOCACTION_Close.equals(getDocAction()))
) // || isDropShip() )
binding = false;
boolean isSOTrx = isSOTrx();
log.fine("Binding=" + binding + " - IsSOTrx=" + isSOTrx);
// Force same WH for all but SO/PO
int header_M_Warehouse_ID = getM_Warehouse_ID();
if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO())
|| MDocType.DOCBASETYPE_PurchaseOrder.equals(dt.getDocBaseType()))
header_M_Warehouse_ID = 0; // don't enforce
BigDecimal Volume = Env.ZERO;
BigDecimal Weight = Env.ZERO;
// Always check and (un) Reserve Inventory
for (int i = 0; i < lines.length; i++)
{
MOrderLine line = lines[i];
// Check/set WH/Org
if (header_M_Warehouse_ID != 0) // enforce WH
{
if (header_M_Warehouse_ID != line.getM_Warehouse_ID())
line.setM_Warehouse_ID(header_M_Warehouse_ID);
if (getAD_Org_ID() != line.getAD_Org_ID())
line.setAD_Org_ID(getAD_Org_ID());
}
// Binding
BigDecimal target = binding ? line.getQtyOrdered() : Env.ZERO;
BigDecimal difference = target
.subtract(line.getQtyReserved())
.subtract(line.getQtyDelivered());
if (difference.signum() == 0 && !chkAttribute(line)) // add chkAttribute
{
MProduct product = line.getProduct();
if (product != null)
{
Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered()));
Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered()));
}
continue;
}
log.fine("Line=" + line.getLine()
+ " - Target=" + target + ",Difference=" + difference
+ " - Ordered=" + line.getQtyOrdered()
+ ",Reserved=" + line.getQtyReserved() + ",Delivered=" + line.getQtyDelivered());
// Check Product - Stocked and Item
MProduct product = line.getProduct();
if (product != null)
{
if (product.isStocked())
{
BigDecimal ordered = isSOTrx ? Env.ZERO : difference;
BigDecimal reserved = isSOTrx ? difference : Env.ZERO;
int M_Locator_ID = getLocator(line, product);
// Update Storage
if (!updStorage(line, reserved, ordered, M_Locator_ID, line.getM_AttributeSetInstance_ID()))
return false;
} // stockec
// Release reserved if have an old Attribute
// and update for the new one
if (chkAttribute(line))
{
BigDecimal reserved = line.getQtyReserved();
int M_Locator_ID = getLocator(line, product);
// Update Storage Old
if (!updStorage(line, reserved.negate(), Env.ZERO, M_Locator_ID, line.getM_AttributeSetInstanceTo_ID()))
return false;
// Update Storage New
if (!updStorage(line, reserved, Env.ZERO, M_Locator_ID, line.getM_AttributeSetInstance_ID()))
return false;
// Save the value of M_AttributeSetInstance_ID into M_AttributeSetInstanceTo_ID
if (!updAttribute(line))
return false;
}
// update line
line.setQtyReserved(line.getQtyReserved().add(difference));
if (!line.save(get_TrxName()))
return false;
//
Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered()));
Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered()));
} // product
} // reverse inventory
setVolume(Volume);
setWeight(Weight);
return true;
} // reserveStock