Advice on how to prevent changes within the price break EpiUltraGrid on the Quote Form

So I figured I would share and ask for any pointers or advice this is to prevent changes within the price break EpiUltraGrid on the Quote Form. I added a custom EpiTextBox and tied it to QuoteDtl.Configured to watch for the field value to keep them from changing the info within price break area due to how our configurator returns the configured part. Thanks again for all the useful info and help I have found here as this is an amazing community!

// **************************************************
// Custom code for QuoteForm
// Created: 7/30/2020 10:58:48 AM
// **************************************************

extern alias Erp_Contracts_BO_QuoteDtlSearch;
extern alias Erp_Contracts_BO_Quote;
extern alias Erp_Contracts_BO_Customer;
extern alias Erp_Contracts_BO_AlternatePart;
extern alias Erp_Contracts_BO_Part;
extern alias Erp_Contracts_BO_Vendor;
extern alias Erp_Contracts_BO_VendorPPSearch;
extern alias Erp_Contracts_BO_ShipTo;

using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using Erp.Adapters;
using Erp.UI;
using Ice.Lib;
using Ice.Adapters;
using Ice.Lib.Customization;
using Ice.Lib.ExtendedProps;
using Ice.Lib.Framework;
using Ice.Lib.Searches;
using Ice.UI.FormFunctions;
using System.Linq;

public class Script
	{
	// ** Wizard Insert Location - Do Not Remove 'Begin/End Wizard Added Module Level Variables' Comments! **
	// Begin Wizard Added Module Level Variables **

	// End Wizard Added Module Level Variables **

	// Add Custom Module Level Variables Here **

	private EpiDataView edvQuoteQty;
	private EpiDataView edvQuoteHed;
	private EpiDataView edvQuoteDtl;
	private string[] bypassGroups = { "^OE", "Sys" };

	public void InitializeCustomCode()
		{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Variable Initialization' lines **
		// Begin Wizard Added Variable Initialization

		// End Wizard Added Variable Initialization

		// Begin Wizard Added Custom Method Calls

		// End Wizard Added Custom Method Calls

		this.txtIsConfiguredPart.ValueChanged += new System.EventHandler(this.txtIsConfiguredPart_ValueChanged);
		this.edvQuoteDtl = ((EpiDataView)(this.oTrans.EpiDataViews["QuoteDtl"]));
		this.edvQuoteHed = ((EpiDataView)(this.oTrans.EpiDataViews["QuoteHed"]));
		this.edvQuoteQty = ((EpiDataView)(this.oTrans.EpiDataViews["QuoteQty"]));
		this.edvQuoteHed.EpiViewNotification += new EpiViewNotification(this.edvQuoteHed_EpiViewNotification);
		this.edvQuoteQty.EpiViewNotification += new EpiViewNotification(this.edvQuoteQty_EpiViewNotification);
		}

	public void DestroyCustomCode()
		{
		// ** Wizard Insert Location - Do not delete 'Begin/End Wizard Added Object Disposal' lines **
		// Begin Wizard Added Object Disposal

		// End Wizard Added Object Disposal

		// Begin Custom Code Disposal

		// End Custom Code Disposal

		this.edvQuoteQty.EpiViewNotification -= new EpiViewNotification(this.edvQuoteQty_EpiViewNotification);
		this.edvQuoteHed.EpiViewNotification -= new EpiViewNotification(this.edvQuoteHed_EpiViewNotification);
		this.edvQuoteQty = null;
		this.edvQuoteHed = null;
		this.edvQuoteDtl = null;
		}
	private bool bAuthedBypass(params string[] args)
		{
		if ( args != null ) 
			{
			string userID = ((EpiDataView) this.oTrans.EpiDataViews["CallContextClientData"]).dataView[0]["CurrentUserId"].ToString();
			
			try
				{
				UserFileAdapter adapterUserFile = new UserFileAdapter(this.oTrans);
				adapterUserFile.BOConnect();
				if (Convert.ToBoolean(adapterUserFile.GetByID(userID)))
					{
					foreach (string uGroup in args) 
						{ 
						if ( Convert.ToString(adapterUserFile.UserFileData.UserFile[0]["GroupList"]).ToUpper().Split('~').ToList().Contains( uGroup.ToUpper()) )
							{
							// MessageBox.Show("User belongs to " + uGroup + " Security Group.");    
							adapterUserFile.Dispose();
							return true; 
							}
						}
					}
				adapterUserFile.Dispose();
				}
			catch (System.Exception ex)
				{
				ExceptionBox.Show(ex);
				}
			}
		return false;
		}

	// Description: Triggered Action for Configured Item : Price Fields Locked as RO if user does not belong to one of the bypassGroups defined above.
	private void SetRoForUnitsAndPrice(bool bIsConfigured)
		{
		// This method takes the arrray { bypassGroups } defined above checked against user's security groups.
		bool bReadOnly = true;
		bool bAuthBypass = bAuthedBypass( bypassGroups );
		if ( ( bAuthBypass ) || ( !bIsConfigured ) ) bReadOnly = false; 
		string[] aQuoteDtlCols = { "OrderUnitPrice" , "ExpUnitPrice" , "DocExpUnitPrice" , "InExpUnitPrice" , "DspExpUnitPrice" , "DocInExpUnitPrice" , "DocDspExpUnitPrice" };
		string sEugQuoteQtys = "b4888395-8a78-4e5b-bd43-63b324f1f4b5";
		foreach (string ugCol in aQuoteDtlCols) 
			{ if (edvQuoteDtl.dataView.Table.Columns.Contains(ugCol))
				{
				edvQuoteDtl.dataView.Table.Columns[ugCol].ExtendedProperties["ReadOnly"] = bReadOnly;
				}
			}
         ((EpiUltraGrid)csm.GetNativeControlReference(sEugQuoteQtys)).ReadOnly = bReadOnly;
		}

	private void txtIsConfiguredPart_ValueChanged(object sender, System.EventArgs args)
		{
		// ** Place Event Handling Code Here **
		try {
			bool bIsConfiguredPart = ( Convert.ToString( txtIsConfiguredPart.Value ) == "On" ? true : false);
			this.chkIsAuthOrBypassUser.Checked = bAuthedBypass( bypassGroups );
			SetRoForUnitsAndPrice( bIsConfiguredPart );
			}
		catch (System.Exception ex)
			{
			ExceptionBox.Show(ex);
			}
		}
	private void edvQuoteHed_EpiViewNotification(EpiDataView view, EpiNotifyArgs args)
		{
		// This is to catch new Quotes and set the Default Event value
		// ** Argument Properties and Uses **
		// view.dataView[args.Row]["FieldName"]
		// args.Row, args.Column, args.Sender, args.NotifyType
		// NotifyType.Initialize, NotifyType.AddRow, NotifyType.DeleteRow, NotifyType.InitLastView, NotifyType.InitAndResetTreeNodes
		if ( (args.NotifyType == EpiTransaction.NotifyType.Initialize) || (args.NotifyType == EpiTransaction.NotifyType.AddRow) )
			{
			if ((args.Row > -1))
				{
				//view.dataView(args.Row)("Key2") = "Default"
				//MessageBox.Show("New Row; MktEvent: " + Convert.ToString(edvQuoteHed.dataView[edvQuoteHed.Row]["MktgEvntSeq"]));
				if ( Convert.ToString(edvQuoteHed.dataView[edvQuoteHed.Row]["MktgEvntSeq"]) == "0" ) 
					{
					edvQuoteHed.dataView[edvQuoteHed.Row]["MktgEvntSeq"] = 1;
					edvQuoteHed.Notify(new EpiNotifyArgs(oTrans,edvQuoteHed.Row,edvQuoteHed.Column));
					}
				this.txtIsConfiguredPart.Visible = false;  // Keep my field hidden 
				}
			}
		}

	
	// Added this notification to catch new/updated records grid was becoming writable and only catching row changes & initial loads
	private void edvQuoteQty_EpiViewNotification(EpiDataView view, EpiNotifyArgs args)
		{
		// React to update on the PriceBreak table updates
		// ** Argument Properties and Uses **
		// view.dataView[args.Row]["FieldName"]
		// args.Row, args.Column, args.Sender, args.NotifyType
		// NotifyType.Initialize, NotifyType.AddRow, NotifyType.DeleteRow, NotifyType.InitLastView, NotifyType.InitAndResetTreeNodes
		// Next line commented as I want to react to changes in this grid.
		// if ((args.NotifyType == EpiTransaction.NotifyType.AddRow))
			{
			if ((args.Row > -1))
				{
				try {
					bool bIsConfiguredPart = ( Convert.ToString( txtIsConfiguredPart.Value ) == "On" ? true : false);
					this.chkIsAuthOrBypassUser.Checked = bAuthedBypass( bypassGroups );
					SetRoForUnitsAndPrice( bIsConfiguredPart );
					this.txtIsConfiguredPart.Visible = false;  // Keep my field hidden 
					}
				catch (System.Exception ex)
					{
					ExceptionBox.Show(ex);
					}
				}
			}
		}
	}

Thanks again @markdamen, @hkeric.wci & @Mark_Wonsil

1 Like

Well first of all, best practice is to work with the DataView not the Controls themselves. You could add a BeforeFieldChange Event and capture any column before its changed and stop it.

Example

private void UD100A_BeforeFieldChange(object sender, DataColumnChangeEventArgs args)
{
	// ** Argument Properties and Uses **
	// args.Row["FieldName"]
	// args.Column, args.ProposedValue, args.Row
	// Add Event Handler Code
	switch (args.Column.ColumnName)
	{
		case "ChildKey3": // JobNum
			if (args.ProposedValue.ToString() != string.Empty && this.CheckRecordExistsByJobNum(args.ProposedValue.ToString()) == true)
			{
				EpiMessageBox.Show("Job already exists on this Batch.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				throw new UIException("Job already exists on this Batch");
			}
			break;

		case "ChildKey4": // PartNum
			break;
	}
}

You should be able to find the Event in the Script Editor

Replace that with:

((Ice.Core.Session)oTrans.Session).UserID

If you just want to make a column read-only or not based on another variable in your Script Editor go to the Row Rules section and setup a Rule and Epicor will take care of it all for you. You can also make it hidden, red, blue etc.

You can also do a Custom Row Rule Action and in there check something and throw a message.

1 Like

Well in this case, I am preventing changes by setting the price break grid as read only when a configured part is the current line item because of the way our configurator and pricing is done for them otherwise the wrong price will appear.

Sent from my Samsung Note 9

I just saw this is an old post, funny it showed up as new for me :slight_smile: so by now you prob have figured it out :smiley:

It was keeping them from making pricing changes on our configured trailers in the Quote / Opportunity Form as these changes reset configured product to it’s base price :smiley: