We are in the process of upgrading to Kinetic 2025.2.5 and using the new UI screens.
Most of our custom is now developed but we are struggling with how to develop in our quote screen, we currently have a custom where we type in a markup, click a button and epicor runs through all quote quantities and using the ‘QuoteQty.CalcUPCommMarkup’ it applies the markup as a percentage and populates the ‘QuoteQty.WQUnitPrice’ - this is done in the epidataview as ‘QuoteQty.CalcUPCommMarkup’ is not a database field.
We have tried to put this in application studio but can not seem to work out how to loop.
We have also tried BPM’s & Functions but there seems to be a limitation of not being able to access the dataview field.
Could anyone help please or advise.
This is the main part of the code in c#
private void btnKMFMarkup_Click(object sender, System.EventArgs args)
{
// ** Place Event Handling Code Here **
DialogResult dialog = MessageBox.Show("Are you sure you wish to apply KMF Markup to all quote quantities?", "Continue", MessageBoxButtons.YesNo);
if (dialog == DialogResult.No)
{
MessageBox.Show("KMF Markup action cancelled", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else if (dialog == DialogResult.Yes)
{
EpiDataView edvQuoteHed = ((EpiDataView)(this.oTrans.EpiDataViews["QuoteHed"]));
string message = "Results of KMF Markup being Applied:\n";
foreach (DataRow hrow in edvQuoteHed.dataView.Table.Rows)
{
EpiDataView edvQuoteQty = ((EpiDataView)(this.oTrans.EpiDataViews["QuoteQty"]));
foreach (DataRow row in edvQuoteQty.dataView.Table.Rows)
{
if (Convert.ToDecimal(row["CalcUPCommMarkup"]) == Convert.ToDecimal("0.00000"))
{
message += "Line: " + row["QuoteLine"].ToString() + ", Qty: " + row["SellingQuantity"].ToString() + " - Zero cost, unable to apply Markup\n";
}
else
{
Session epiSession = default(Session);
epiSession = (Session)QuoteForm.Session;
row["WQUnitPrice"] = Math.Round(((Convert.ToDecimal(row["CalcUPCommMarkup"]) / 100) * Convert.ToDecimal(hrow["KMFMarkup_c"])) + Convert.ToDecimal(row["CalcUPCommMarkup"]), 2, MidpointRounding.AwayFromZero);
row["KMFMarkup_c"] = hrow["KMFMarkup_c"];
row["KMFMarkUpDate_c"] = hrow["ChangeDate"];
row["KMFMarkUpBy_c"] = epiSession.UserID;
oTrans.Update();
}
}
if (message.Contains("unable to apply"))
{
message += "\nAll quote quantity markup's applied with exception of the above";
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
message += "\nAll quote quantity markup's applied";
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
Or, to avoid that hassle, if you really want the values client-side without a server trip, you could try using a javascript IIFE expression in a console-write action like so:
In case you wanted to try again (or for future reference), the code converted into a function would look something like the example below (coded off the top of my head, didn’t test). Same flow, different interfaces. Would need the quote service added as a reference.
/*
Inputs:
quoteNum: System.Int
Outputs:
ds: Ice.Tablesets.QuoteTableset
message: System.String
*/
CallService<QuoteSvcContract>((svc) =>
{
ds = svc.GetByID(quoteNum);
var hrow = ds.QuoteHed.First();
message = "Results of KMF Markup being Applied:\n";
foreach (var row in ds.QuoteQty)
{
if (Convert.ToDecimal(row["CalcUPCommMarkup"]) == 0m)
{
// Don't use Convert.ToDecimal("0.00000"), just use 0m. The m suffix tells c# the number is a decimal.
message += "Line: " + row["QuoteLine"].ToString() + ", Qty: " + row["SellingQuantity"].ToString() + " - Zero cost, unable to apply Markup\n";
}
else
{
row["WQUnitPrice"] = Math.Round(((Convert.ToDecimal(row["CalcUPCommMarkup"]) / 100) * Convert.ToDecimal(hrow["KMFMarkup_c"])) + Convert.ToDecimal(row["CalcUPCommMarkup"]), 2, MidpointRounding.AwayFromZero);
row["KMFMarkup_c"] = hrow["KMFMarkup_c"];
row["KMFMarkUpDate_c"] = hrow["ChangeDate"];
row["KMFMarkUpBy_c"] = Session.UserID;
row.RowMod = "U";
svc.Update(ref ds);
}
}
if (message.Contains("unable to apply"))
{
message += "\nAll quote quantity markup's applied with exception of the above";
}
else
{
message += "\nAll quote quantity markup's applied";
}
});
Two things would have to happen when completed; display the message and refresh the data set with the result. The message handler in app studio would replace the MessageBox.Show calls. The data set needs refreshed afterwards to avoid “Row was modified by another user” errors.
Functions are server side, they can’t “see” anything in application studio. You would have to send the quote num to the function, and the function would have to pull up that quote dataset to iterate through it. Or you could pass the entire dataview to the function as one of the input parameters. There are plenty of threads showing both of these methods on the forum. Personally I would not even try to do this with a dataview condition. Have had them blow up on me and/or behave inconsistently too many times.
Dataview conditions, while work, in my limited experience they can be unstable and if you happen to make a change after you have created the event, there is a high probability that you can break it, causing to delete all steps up to and including the condition and redoing. @aosemwengie1 functions method works reliably.
I have the code in a function which I am testing using REST to input the quote number and nothing is updating (the code compiles ok, I just need to add a using: using Erp.Contracts;.
When the dataset is returned as an output the WQUnitPrice field and CalcUPCommMarkup fields are not there as they are dataview fields, not database fields - i think this is why it’s not working.
Is it possible to use dataview fields in a function? reading them and/or updating them?
Ah, I would just add extra parameters to the function in your case.
If you added those fields in app studio to the header’s data view, you can just pass them alongside the quote number as inputs. Then modify the function and replace those row[...] references with the new inputs.
You could pass a data set into the function, there’s a caveat in that you need to explicitly pass it back as an output (e.g. dsOut = dsIn). There are posts here on passing in / out data sets from functions. You’d also have to modify the function a little bit, such as getting rid of the GetByID call since you pass the row over anyways.