RowMod == "A"/"U" Replacement

Hello all,

I have some code in the system that checks orders line by line and puts an order/customer on credit hold based on certain criteria set in other fields.

For customers with certain job payment terms and multiple projects and jobs with different credit terms entering orders can take a while as things are being checked line by line. It doesn’t affect all customers and only seems to get really bad on customers that order frequently as it is running through a credit check process against the customer and their open invoices and jobs each line entered.

Is there a way to streamline this process or replace the current rowmod usage? Checking orders line by line worked for us at first when Epicor didn’t have as much data in it when we launched but with more and more jobs in the system it can take minutes to input a few lines. Here’s a small example of code used in this process:

if (ttOrderHed.Count() != 0)
    {
        if (ttOrderHed.Any(h => h.RowMod == "U" || h.RowMod == "A"))
        {
            var hed = ttOrderHed.First(h => h.RowMod == "U" || h.RowMod == "A");
            orderNum = hed.OrderNum;
            custNum = hed.CustNum;
            shipToNum = hed.ShipToNum;
            termsCode = hed.TermsCode;
            omniPaymentType_ShortChar06 = hed.UDField<string>("ShortChar06");
            tableType = TableTypeEnum.Hed;
            isApprovedByCreditDepartment = hed.UDField<bool>("CheckBox05");
        }
        else
        {
            return null;
        }

Here is an example of a function being used in the code:

Func<int, int, bool> TrySetHedCheckBox01 =
(int orderNum, int tableType) =>
{
    if (tableType == TableTypeEnum.Hed)
    {
        var hed = ttOrderHed.FirstOrDefault(h => h.RowMod == "U" || h.RowMod == "A");
        if (hed == null)
        {
            return false;
        }

        hed["CheckBox01"] = true;
    }
    else
    {
        var hed = Db.OrderHed.FirstOrDefault(h => h.OrderNum == orderNum);
        if (hed == null)
        {
            return false;
        }

        hed.CheckBox01 = true;
    }

    return true;
};

There is code like this throughout and mostly uses RowMod == “A” || RowMod == “U” to check if any changes are made to the order that will increase amounts so that orders cannot have extra lines added to them after they are released. This particular bulk of code is a little over 1100 lines (the department had requests for many, many different scenarios to be checked against) so forgive me for not posting it in it’s entirety.

You could definitely streamline this, there are 3 calls in a row which almost do the same thing here (check there is a row, check there is a row with condition, get said row)

You could try just doing this:
var hed = ttOrderHed.Where(h => h.Updated() || h.Added()).FirstOrDefault();

Now you just check the hed != null and start processing it

1 Like

Thanks Chris.

I should post the whole first snippet of code to paint a better picture as there are also other calls that are being used:

Func<dynamic> GetHed =
() =>
{
    int orderNum;
    int custNum;
    string shipToNum;
    //string customerCustID;
    string termsCode;
    string omniPaymentType_ShortChar06;
    int tableType;
    decimal unaccountedForAmounts;
    bool isApprovedByCreditDepartment;

    if (ttOrderHed.Count() != 0)
    {
        if (ttOrderHed.Any(h => h.RowMod == "U" || h.RowMod == "A"))
        {
            var hed = ttOrderHed.First(h => h.RowMod == "U" || h.RowMod == "A");
            orderNum = hed.OrderNum;
            custNum = hed.CustNum;
            shipToNum = hed.ShipToNum;
            //customerCustID = hed.CustomerCustID;
            termsCode = hed.TermsCode;
            omniPaymentType_ShortChar06 = hed.UDField<string>("ShortChar06");
            tableType = TableTypeEnum.Hed;
            isApprovedByCreditDepartment = hed.UDField<bool>("CheckBox05");
        }
        else
        {
            return null;
        }
    }
    else if (ttOrderDtl.Count() != 0)
    {
        var dtl = ttOrderDtl.First(d => d.RowMod == "U" || d.RowMod == "A");
        var hed = Db.OrderHed.First(h => h.OrderNum == dtl.OrderNum);
        orderNum = hed.OrderNum;
        custNum = hed.CustNum;
        shipToNum = hed.ShipToNum;
        //customerCustID = string.Empty;
        termsCode = hed.TermsCode;
        omniPaymentType_ShortChar06 = hed.UDField<string>("ShortChar06");
        tableType = TableTypeEnum.Dtl;
        isApprovedByCreditDepartment = hed.UDField<bool>("CheckBox05");
    }
    else if (ttOrderRel.Count() != 0)
    {
        var rel = ttOrderRel.First(r => r.RowMod == "U" || r.RowMod == "A");
        var hed = Db.OrderHed.First(h => h.OrderNum == rel.OrderNum);
        orderNum = hed.OrderNum;
        custNum = hed.CustNum;
        shipToNum = hed.ShipToNum;
        //customerCustID = string.Empty;
        termsCode = hed.TermsCode;
        omniPaymentType_ShortChar06 = hed.UDField<string>("ShortChar06");
        tableType = TableTypeEnum.Rel;
        isApprovedByCreditDepartment = hed.UDField<bool>("CheckBox05");
    }
    else
    {
        return null;
    }

    // Calculate unaccountedForAmounts.
    var totalChangedAmounts = ttOrderDtl
        .Where(d => d.RowMod == "A" || d.RowMod == "U").Select(d => d.DocTotalPrice)
        .DefaultIfEmpty(0).Sum();
    var totalOriginalAmounts = ttOrderDtl
        .Where(d => string.IsNullOrWhiteSpace(d.RowMod)).Select(d => d.DocTotalPrice)
        .DefaultIfEmpty(0).Sum();

    unaccountedForAmounts = totalChangedAmounts - totalOriginalAmounts;

    return new
    {
        OrderNum = orderNum,
        CustNum = custNum,
        ShipToNum = shipToNum,
        //CustomerCustID = customerCustID,
        TermsCode = termsCode,
        OmniPaymentType_ShortChar06 = omniPaymentType_ShortChar06,
        TableType = tableType,
        UnaccountedForAmounts = unaccountedForAmounts,
        IsApprovedByCreditDepartment = isApprovedByCreditDepartment
    };
};

I also did not write this code and am tasked with going through it and cleaning it up as there were some issues with the code itself and not passing through the right variables which I have already fixed.

So at first glance I am little confused about their initial intention. So far, all I see is the getting of records direct from the Db and storing those values. Judging by their liberal use of inline funcs and dynamics, they are probably better programmers than I, I prefer to keep it simple.

If it were me, instead of all that fancy look ups in the Db, I’d instanciate a BO and simply do a GetByID(OrderNum). This will get the head and all of the detail records like they are doing in one single call. At that point, if necessary, you can grab the data from your dataset and assign to those variables as they are doing.

Here is an example of instanciating a BO in a BPM

SO =Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.SalesOrderSvcContract>(Db);
///then you can do
SalesOrderDataSet soDs = SO.GetByID(idhere);
//the entire structure is in ds now, head, details, etc

Assuming this is a BPM method directive:

To see modified records in a BPM, I use a technique like this:

var dtl = ttOrderDtl.Where(r=>!r.Unchanged()).FirstOrDefault();

To join back to the Db context, always set a variable and then use that variable to query the Db:

var myOrderNum = dtl.OrderNum;
var hed = Db.OrderHed.With(LockHint.NoLock).Where(r=>r.Company==callContextClient.CurrentCompany && r.OrderNum == myOrderNum);

Hope that helps!

Tanner

1 Like

I have to get more familiar with BOs in Epicor. Definitely something that I lack knowledge in.

Thanks

@T11

Yes it is a method directive.

That is something I will have to try implementing although I am not too sure of how I am going to cleanly implement it as there are many lines of code written in the old way and references throughout. Rewriting the whole thing might be better at this point which is something I would have hoped to avoid.

I appreciate your insight.