BPM expressions

Hiya

I am creating a BPM to check the total order QTY entered each Product Group. I want to add this in my conditions that if total order QTY for each prodgroup is > 20 then pop up message will pull through.
I have created what I like to achieve in BAQ but it’s really hard for me to translate it to c# / linq. Any help will be much appreciated.

image

Cheers

You can find information on Linq/Lambda Group by with summary here: c# - LINQ Lambda Group By with Sum - Stack Overflow

Its going to do something like this (untested and not validated):

var ttoDtl = ttOrderDtl.Where(x => x.RowMod != "").FirstOrDefault(); //the current line item record being edited.

var oTotalByProdGroup = Db.OrderDtl.Where(x => x.Company == ttoDtl.Company && x.OrderNum == ttoDtl.OrderNum)
    .GroupBy(x => x.OrderNum, x.ProdCode)
    .Select(x => new { x.OrderNum, x.ProdCode, totalQty = Sum(x.SellingQuantity) });

foreach (var pg in oTotalByProdGroup.Where(x => x.totalQty > 20)) {
    //print your error message here!
    //pg.OrderNum contains the order number
    //pg.ProdCode contains the product code that has greater than 20
    //pg.totalQty contains the total quantity for the order/prodcode
}
1 Like

Thank you for you quick reply @timshuwy , Really appreciate it. I am still learning Epicor in general so forgive me for asking much questions.

For this one, do I need to use “The specified argument/variable is equal to the specified expression”? If yes, then I need to declare a variable then?

image

Hmmm… I may have lead you down a bad path… I just tried to validate the code, and it has several problems…
Anyone else out there have a clue?

Here’s an updated version of the code that compiles, but I haven’t tested it in action.
Marianne, this code would go in a BPM’s ‘Custom Code’ block.

//Get the current line item record being edited.
var ttoDtl = ttOrderDtl.Where(orderDtl => orderDtl.RowMod != "").FirstOrDefault(); 

// Get the total order quantities by product group.
var oTotalByProdGroup = Db.OrderDtl.Where(orderDtl => orderDtl.Company == ttoDtl.Company && orderDtl.OrderNum == ttoDtl.OrderNum)
    .GroupBy(orderDtl => new {orderDtl.OrderNum, orderDtl.ProdCode})
    .Select(grp => new { grp.Key.OrderNum, grp.Key.ProdCode, TotalQty = grp.Sum(orderDtl => orderDtl.SellingQuantity) });

// If any product group has a Total Qty > 20, do something.
if (oTotalByProdGroup.Any(item => item.TotalQty > 20))
{
  //print your error message here!
}

// Or loop through the product groups that have a Total Qty > 20 and do something for each one.

foreach (var productGroup in oTotalByProdGroup.Where(item => item.TotalQty > 20)) {
    //print your error message here!
    //productGroup.OrderNum contains the order number
    //productGroup.ProdCode contains the product code that has greater than 20
    //productGroup.totalQty contains the total quantity for the order/prodcode
}
2 Likes

Hey @andrew.johnson

Thanks for looking into this. I tried to use your code. Syntax OK, but it is not doing anything when I enter orders in my order entry.

Here’s my BPM look like:
Condition to check that customer is equal to a specific customer and just selecting specific prodcode

And then added a custom code which is your code, just removed the loop codes
image

And then, I created a new order, entered two partNum that is under the same prodcode but when I saved it, Nothing happens.

Thank you so much. Really appreciate your help. I am a newbie, this is my first time creating bpms and still exploring Epicor so forgive me if I am asking too much.

Cheers
Marianne

@mriannejoy Some steps that I have learned thru trial and error for bpms.

  1. Just add an info message to make sure the method is getting hit. Test
  2. Add your conditions before the info message to be sure they are good. Lots of times you think the record is being added, but by the time a bpm sees it is being updated. Test until this comes up.

Then add the custom code and test.

1 Like

Marianne,
I can’t verify the conditions you’re using due to the blanked out values in your screenshot, but if we take them out of the picture and focus only on the custom code:

The code Tim and I posted earlier was reading the Order Lines from the database, but because you are checking the quantity before the save to the database is complete (Pre-Processing), the quantity of the new line isn’t included, and doesn’t exceed your threshold of 20 so never shows the message. Now that I better understand what you’re trying to do based on your screenshots, I’ve updated the code. It is simpler now in that it’s only focusing on the product group for the row being added (if a row is being added). No grouping is necessary anymore.

//Get the Order Line being added.
var ttoDtl = ttOrderDtl.FirstOrDefault(orderDtl => orderDtl.RowMod == "A"); 


if (ttoDtl != null) // If a new Order Line is being added...
{
  // Get the existing Order Lines for this Sales Order where the Product Group is the same as the Product Group of the new line.
  // Exclude the current Order Line, just in case. (Though this shouldn't be necessary if the line is new)
  var orderLines = Db.OrderDtl.Where(orderDtl => orderDtl.Company == ttoDtl.Company && orderDtl.OrderNum == ttoDtl.OrderNum && orderDtl.ProdCode == ttoDtl.ProdCode && orderDtl.OrderLine != ttoDtl.OrderLine);
  
  // Get the sum of Selling Quantity from existing Order Lines (or 0 if no lines exist)
  // plus the Selling Quantity of the new line.
  var totalQuantity = (orderLines.Any() ? orderLines.Sum(orderDtl => orderDtl.SellingQuantity) : 0) + ttoDtl.SellingQuantity;
  
  // If the total quantity exceeds 20, show a message.
  if (totalQuantity > 20)
  {
    var message = ttoDtl.ProdCodeDescription + " quantity is greater than 20!";
    
    this.PublishInfoMessage(message, Ice.Common.BusinessObjectMessageType.Information, Ice.Bpm.InfoMessageDisplayMode.Individual, "", "");
  }
}

Success!

Also, since you’re using a custom code block anyway, I personally would move the conditional logic into the code. You can tweak it all in one place.

2 Likes

@andrew.johnson

This is working and I am crying right now. :smiley:
By any chance, do you have any good resource that I can use for learning this codes? I sort of understand what the codes does but I’d love to have a deeper understanding on this so I can create codes as well. This is a good start for our team to start looking into BPMs for our customer service as they are currently doing manual computations. Thank you so so much!
image

1 Like

Thanks, @gpayne . Will surely do this good practice!

Sorry I don’t have any specific resources for BPM coding. I would just recommend learning the C# language, and then apply it more specifically to Epicor by experience. I already had C# experience before working with Epicor. This epiusers.help site is a great resource when you run into problems.

There is a little bit of information about the Custom Code block in the Epicor ICE Tools User Guide. You can find this in the documentation section of your EpicWeb portal.

1 Like

Yes, definitely agree. This site has been helping me heaps in learning Epicor.

I guess I have one more question…
Here’s what they want to happen now. If my total qty is <= 12, the BPM needs to update the OrderDtl.DiscountPercent to 5.00%. I tried working on that and it sort of working but then, when I have 2 partNums entered for that specific ProdCode and the total QTY is now greater than 12 it gives me the pop up message, DiscountPercent is 0 but just for the 2nd line. The 1st line still remains to 5%. Is there a possible way to update the DiscountPercent to all lines with the same prodcode when the totalqty becomes greater than 12?

image

Cheers
Marianne

That is a little more complicated. The Pre-Processing BPM directive only contains data for the rows that are added, updated, or deleted. So you don’t have access to the other Order Lines in that case.

A Post-Processing directive contains the entire dataset. You will need to create a Post-Processing directive and loop through the rows in ttOrderDtl there, applying the discount percent. You can enable the Post-Processing directive from your Pre-Processing directive if certain conditions are met. If those conditions are not met, the Post-Processing directive is not triggered.

Pre-Processing Directive

image

image

Post-Processing (Enabled from your Pre-Processing directive)

Custom Code to modify the Discount %

foreach(var orderDtl in ttOrderDtl)
{
  orderDtl.DiscountPercent = 0;
}

Update the dataset again to commit the modified Discount %
image
image

You may need to create two different Post-Processing directives and enable them based on whether you are trying to apply or clear the discount. You also may need to consider both Added (RowMod == “A”) and Updated (RowMod == “U”) records in your pre-processing instead of only Added.

1 Like

Hey @andrew.johnson

Sorry for the late response, I was on holiday. :slight_smile:

I tried following your instructions. And I am getting 0% discount now in the OrderDtl.DiscountPercent for all the lines. However the discount in the Summary does not seem to be updated at all.

image

And when I re-open the orderNum, I am seeing 5% discount again for the previous lines.