Bpm for requisition

Hi All,

Can anyone help me to set a BPM for Auto approve if cost is less then $2000.

I am new with this and not able to do It.
I write a code in method directive as follow: (BUT THIS CODE TAKE TOO LONG TIME AND I am getting error ) I am using EPICOR KINITIC CLOUD

in ERP.BO.Req.Update:

here is a code

// Threshold per line
decimal lineApprovalLimit = 100m;

// Get ReqSvc service
var reqSvc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.ReqSvcContract>(Db);

// Loop through all requisition headers in the BPM tableset
foreach (var head in Db.ReqHead)
{
    int reqNum = head.ReqNum;

    // Get all detail lines for this requisition
    var lines = Db.ReqDetail.Where(d => d.ReqNum == reqNum).ToList();

    bool allLinesBelowLimit = true;

    // Check each line total
    foreach (var line in lines)
    {
        decimal lineTotal = line.XOrderQty * line.DocUnitCost;
        if (lineTotal > lineApprovalLimit)
        {
            allLinesBelowLimit = false;
            break;
        }
    }

    // Skip if any line exceeds limit
    if (!allLinesBelowLimit)
        continue;

    // Load full tableset from service
    var ts = reqSvc.GetByID(reqNum);

    // Approve the header
    ts.ReqHead[0].StatusType = "A";

    string reqNumStr = reqNum.ToString();   // Convert int to string
    bool includeSendToPMCond = true;

    // Build Req Actions List
    var actions = reqSvc.BuildReqActionsList(reqNumStr, includeSendToPMCond);

    // Prepare ReqActionID
    string reqActionID = "APPROVE";

    // Declare out variable before calling method (C# 6 compatible)
    string currDispatcherID;

    // Execute action
    reqSvc.BuildNextDispatcher(reqNumStr, out reqActionID, out currDispatcherID);


    // 3. Save changes
    reqSvc.Update(ref ts);

}

What is the error?

Looks like your BPM is looping through each item in the database. That might be the reason for taking a long time.

1 Like

Hi All,

Can anyone help me to set a BPM for Auto approve if cost is less then $2000.

I am new with this and not able to do It.
I write a code in method directive as follow: (BUT THIS CODE TAKE TOO LONG TIME AND I am getting error ) I am using EPICOR KINITIC CLOUD

in ERP.BO.Req.Update:

// Threshold per line
decimal lineApprovalLimit = 100m;

// Get ReqSvc service
var reqSvc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.ReqSvcContract>(Db);

// Loop through all requisition headers in the BPM tableset
foreach (var head in Db.ReqHead)
{
    int reqNum = head.ReqNum;

    // Get all detail lines for this requisition
    var lines = Db.ReqDetail.Where(d => d.ReqNum == reqNum).ToList();

    bool allLinesBelowLimit = true;

    // Check each line total
    foreach (var line in lines)
    {
        decimal lineTotal = line.XOrderQty * line.DocUnitCost;
        if (lineTotal > lineApprovalLimit)
        {
            allLinesBelowLimit = false;
            break;
        }
    }

    // Skip if any line exceeds limit
    if (!allLinesBelowLimit)
        continue;

    // Load full tableset from service
    var ts = reqSvc.GetByID(reqNum);

    // Approve the header
    ts.ReqHead[0].StatusType = "A";

    string reqNumStr = reqNum.ToString();   // Convert int to string
    bool includeSendToPMCond = true;

    // Build Req Actions List
    var actions = reqSvc.BuildReqActionsList(reqNumStr, includeSendToPMCond);

    // Prepare ReqActionID
    string reqActionID = "APPROVE";

    // Declare out variable before calling method (C# 6 compatible)
    string currDispatcherID;

    // Execute action
    reqSvc.BuildNextDispatcher(reqNumStr, out reqActionID, out currDispatcherID);


    // 3. Save changes
    reqSvc.Update(ref ts);

}

First, i would replace the first 19 lines of your code with one line that makes this BPM much more efficient. Let SQL do the work. You dont need to load the entire list of items for you to then for/next through the list to find a problem… let SQL do the math and find out if there are any that meet your needs. This can be done by putting your criteria into the where statement, and then using the .ANY concept… this will return either a true or false if it finds “any” that meet your criteria.
Then you should probably put one other if statement to make sure that the returned value is not null.
ALSO, I dislike “break” statements and “return” statements in the middle of anything. I would rather wrap the entire logic in a big if statement. This way you can follow all the logic without have to look for breaks and returns.
Here is some revised code (Untested of course).
OH… my first line uses your variable “lineAprovalLimit” but i am not sure where you got that from… just pointing out that this is an unknown in your logic.

// first lets find out if "ANY" of the lines exceed the approval limit
if (double.ReqDetail.Where(x => x.ReqNum == reqNum && x.XOrderQty * x.DocUnitCost > lineApprovalLimit).Any()) {

    // Load full tableset from service
    var ts = reqSvc.GetByID(reqNum);
    if (ts != null) {
        // Approve the header
        ts.ReqHead[0].StatusType = "A";

        string reqNumStr = reqNum.ToString();   // Convert int to string
        bool includeSendToPMCond = true;

        // Build Req Actions List
        var actions = reqSvc.BuildReqActionsList(reqNumStr, includeSendToPMCond);

        // Prepare ReqActionID
        string reqActionID = "APPROVE";

        // Declare out variable before calling method (C# 6 compatible)
        string currDispatcherID;

        // Execute action
        reqSvc.BuildNextDispatcher(reqNumStr, out reqActionID, out currDispatcherID);

        // 3. Save changes
        reqSvc.Update(ref ts);
    }
}
1 Like

Are you running Update inside Update… ?

Not looked too closely, but this may be unneccessary, and you for sure need to be cautious. Either check for a condition or turn off bpm processing in your GetService call.

2 Likes

@klincecum is correct.. what business object is this BPM part of? if you are already in an update BPM, then you simply need to get the temporary record from memory, and then you can modify the data in memory. the system will take care of the actual writing for you.

decimal lineApprovalLimit = 100m;

// Get ReqSvc service
var reqSvc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.ReqSvcContract>(Db);

// Loop through all requisition headers in the BPM tableset
foreach (var head in Db.ReqHead)
{
int reqNum = head.ReqNum;

// Get all detail lines for this requisition
var lines = Db.ReqDetail.Where(d => d.ReqNum == reqNum).ToList();

bool allLinesBelowLimit = true;

// Check each line total
foreach (var line in lines)
{
    decimal lineTotal = line.XOrderQty * line.DocUnitCost;
    if (lineTotal > lineApprovalLimit)
    {
        allLinesBelowLimit = false;
        break;
    }
}

// Skip if any line exceeds limit
if (!allLinesBelowLimit)
    continue;

// Load full tableset from service
var ts = reqSvc.GetByID(reqNum);

// Approve the header
ts.ReqHead[0].StatusType = "A";

string reqNumStr = reqNum.ToString();   // Convert int to string
bool includeSendToPMCond = true;

// Build Req Actions List
var actions = reqSvc.BuildReqActionsList(reqNumStr, includeSendToPMCond);

// Prepare ReqActionID
string reqActionID = "APPROVE";

// Declare out variable before calling method (C# 6 compatible)
string currDispatcherID;

// Execute action
reqSvc.BuildNextDispatcher(reqNumStr, out reqActionID, out currDispatcherID);


// 3. Save changes
reqSvc.Update(ref ts);

This is my whole code.

and yes I am using ERP.BO.Req.Update

how can I do that?

error not showing anything just taking long time and come error screen in right side with blank. nothing in it

OK.. here is the revised code that doesnt use the update within the code. Instead, we just read the memory (the first query of the header) and now we can modify it. Note that I am not getting all the records, but only the modified record.

also notice that this makes the code much simpler.

decimal lineApprovalLimit = 100m;

// Get ReqSvc service
var reqSvc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.ReqSvcContract>(Db);

// Loop through all requisition headers in the BPM tableset - only look at the MODIFIED ones (RowMod != "")
foreach (var myReqHead in Db.ReqHead.Where(x=>x.RowMod != "")) {

    // first lets find out if "ANY" of the lines exceed the approval limit
    if (double.ReqDetail.Where(x => x.ReqNum == myReqHead.reqNum && x.XOrderQty * x.DocUnitCost > lineApprovalLimit).Any()) {

        myReqHead.StatusType = "A";         // Approve the header

        string reqNumStr = myReqHead.reqNum.ToString();   // Convert int to string
        bool includeSendToPMCond = true;

        // Build Req Actions List
        var actions = reqSvc.BuildReqActionsList(reqNumStr, includeSendToPMCond);

        // Prepare ReqActionID
        string reqActionID = "APPROVE";

        // Declare out variable before calling method (C# 6 compatible)
        string currDispatcherID;

        // Execute action
        reqSvc.BuildNextDispatcher(reqNumStr, out reqActionID, out currDispatcherID);
    }
}
1 Like

I think that’s right but If I remove that it not updating table.

BPMs are inserted into the stream of process… as long as you make it “pre-processing”

  1. you press save
  2. System does some data collection and sends it to the Update method
  3. BPM is triggered… you do your stuff (like changing values in teh data stream
  4. once BPM is done, the changed data (still in memory) goes back to the update method, and the update method completes the work, saving the record.

So.. there is no need to do the update yourself, becuase the update method will do it once you return from the BPM.

1 Like

Hi Tim,

Thank you for helping, @knash and @klincecum thank you.

It working now.

the only thing I need to figure out is once it auto prove by BPM it is not going to PO suggestion.

Hi all,

This code run fine, but when dispatcher login and see to do list they cannot see any list.

Please if any one can help.