Prevent over receipt for mass receipt

I’m following this post : Prevent Over Receipts for a PO Release - #5 by ckrusen
and created 2 BPMs to stop the user from over receiving a PO.

The BPM is working as expected when the user add the receipt line by line.

However, the user will hit the exception “The table ds.RcvDtl has more than one record” when they are doing mass receipt and trying to change the receiving qty to more or less than the PO qty.
image

How do I resolve this ?

If you have multiple records then in my experience you have to use a custom code block to iterate over each record and apply your business logic to each.

foreach (var RcvDtlRecord in ds.RcvDtl)
{
   // Apply business logic to check for over receipt here
   // if (inputOurQty > RcvDtlRecord.OurQty)
   //     throw new ArgumentOutOfRangeException(RcvDtlRecord.PackSlip + " " + RcvDtlRecord.PackLine + " had qty of " + RcvDtlRecord.OurQty + " which user entry exceeded");
}

Though as mentioned in the other post - it’s probably better to leave this as a warning to prevent issues further downstream.

1 Like

Thanks for responding. Writing custom C# code is a challenge for me as I’m not very proficient in it. :sweat:

Another question, from which method directive should this custom code block be triggered?

Welcome !

What does your trace show?

I’ve placed my custom code at the following methods’ Pre-Processing
Erp.BO.Receipt.CommitRcvDtl
Erp.BO.Receipt.OnChangeDtlReceived
Erp.BO.Receipt.OnChangeHdrReceived
Erp.BO.Receipt.UpdateMaster

The code is working as expected when the user is receiving the packline 1 by 1.
But it’s not working when the user entered a batch of receiving lines and received all. :thinking:

// Initialize a variable to store the previous receipts quantity.
decimal prevrcpts = 0;

// Iterate over each receiving detail that meets PO conditions.
foreach (var rdtl in (from rdtlrow in ds.RcvDtl
                     where (string.Equals(rdtlrow.RowMod, IceRow.ROWSTATE_UPDATED, StringComparison.OrdinalIgnoreCase)
                            || string.Equals(rdtlrow.RowMod, IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase))
                            && rdtlrow.PONum != 0
                            && rdtlrow.POLine != 0
                     select rdtlrow))
{
    // Retrieve the purchase order release related to the current receiving detail.
    var prel = Db.PORel.FirstOrDefault(p => p.Company == rdtl.Company && p.PONum == rdtl.PONum && p.POLine == rdtl.POLine && p.PORelNum == rdtl.PORelNum);

    if (prel != null)
    {
        // Determine the previous receipts quantity based on the RowMod of the receiving detail.
        if (rdtl.RowMod == "A")
            prevrcpts = prel.ReceivedQty;
        if (rdtl.RowMod == "U")
            prevrcpts = prel.ReceivedQty - rdtl.OurQty;

        // Check if the sum of the current and previous receipts quantity exceeds the PO release quantity.
        if ((rdtl.InputOurQty + prevrcpts) > (prel.XRelQty * 1))
        {
            // Throw an exception if over receiving is detected.
            if (prevrcpts > 0)
                throw new Ice.BLException("Over receiving detected at Line [" + rdtl.PackLine + "] ! Entered quantity of " + rdtl.InputOurQty + " plus previous receipts of " + Math.Round(prevrcpts, 2) + " is more than the PO release quantity of " + Math.Round(prel.XRelQty, 2) + "  [BPM]");
            else
                throw new Ice.BLException("Over receiving detected at Line [" + rdtl.PackLine + "] ! Entered quantity of " + rdtl.InputOurQty + " is more than the PO release quantity of " + Math.Round(prel.XRelQty, 2) + "  [BPM]");
        }
    }
}

Managed to solve my issue with the help of ChatGPT :wink:
Below is my code.

// Initialize variable to store previously received quantity for the current PO release.
decimal prevReceipts = 0;

// Loop through each receiving detail where records are either updated or newly added, and PO details are valid.
foreach (var receivingDetail in from detail in ds.RcvDtl
                                where (string.Equals(detail.RowMod, IceRow.ROWSTATE_UPDATED, StringComparison.OrdinalIgnoreCase) ||
                                       string.Equals(detail.RowMod, IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase)) &&
                                      detail.PONum != 0 &&
                                      detail.POLine != 0
                                select detail)
{
    // Fetch the matching PO release for the current receiving detail.
    var purchaseOrderRelease = Db.PORel.FirstOrDefault(p => p.Company == receivingDetail.Company && 
                                                             p.PONum == receivingDetail.PONum && 
                                                             p.POLine == receivingDetail.POLine && 
                                                             p.PORelNum == receivingDetail.PORelNum);

    if (purchaseOrderRelease != null)
    {
        // Calculate the total previously received quantity for this PO release, excluding the current receipt to avoid double-counting.
        var totalReceivedQuantity = Db.RcvDtl
            .Where(p => p.Company == receivingDetail.Company && 
                        p.PONum == receivingDetail.PONum && 
                        p.POLine == receivingDetail.POLine && 
                        p.PORelNum == receivingDetail.PORelNum && 
                        p.SysRowID != receivingDetail.SysRowID) // Exclude current receipt row
            .Sum(x => (decimal?)x.OurQty) ?? 0;

        // Set prevReceipts to the already received quantity for this PO release.
        prevReceipts = purchaseOrderRelease.ReceivedQty;

        // Round quantities to two decimal places for consistency and to prevent precision issues.
        var enteredQuantity = Math.Round(receivingDetail.InputOurQty, 2);
        var totalQuantityIncludingCurrent = Math.Round(enteredQuantity + totalReceivedQuantity, 2);
        var roundedXRelQty = Math.Round(purchaseOrderRelease.XRelQty, 2);

        // Check if the total quantity (including the current entry) exceeds the allowed release quantity.
        if (totalQuantityIncludingCurrent > roundedXRelQty)
        {
            // If over-receiving is detected, throw an exception with details for handling.
            var exceptionMessage = prevReceipts > 0
                ? $"Over receiving detected at Line [{receivingDetail.POLine}]! Entered quantity of {enteredQuantity} plus previous receipts of {Math.Round(prevReceipts, 2)} exceeds PO release quantity of {roundedXRelQty} "
                : $"Over receiving detected at Line [{receivingDetail.POLine}]! Entered quantity of {enteredQuantity} exceeds PO release quantity of {roundedXRelQty}";

            throw new Ice.BLException(exceptionMessage);
        }
    }
}