UI customization to add order lines programmatically?

TL;DR: Can I activate a menu item programmatically in a UI customization? (But this may be an XY problem.)

I’ve been tasked with making the following customizations to the Order Entry screen:

  1. When certain customers are selected, automatically check Counter Sale.
  2. When Counter Sale is checked, automatically check Pack and Invoice and add a new order line.

At first I tried to do all of this with method directives. I found that the UI is not entirely driven by the dataset it receives. For example, if I call ChangeCounterSale from a post-processing directive on ChangeCustomer, it puts the UI into an inconsistent state where Counter Sale is checked but the Lines tab is still visible and the Counter Sale Detail tab doesn’t work. It seems that checking Counter Sale changes the UI state in some way, even before it calls ChangeCounterSale.

I worked around this by checking Counter Sale in an After Field Changed event handler for the customer ID field. This correctly puts the UI into counter sale mode and triggers the call to ChangeCounterSale.

But the UI is still not prepared to receive a dataset that includes an order line. I think the “new line” menu item alters the UI state in some other way. Is it possible to activate the menu item programmatically? Or perhaps find out how it changes the UI state and make those changes myself?

Yes, you can programmatically click the stuff. It uses something like tools[“menuItem”] syntax. There are examples around here if you search enough for clicking refresh and probably other buttons. That should get you what you need.

@KevinK , have you tried using dsHolder.Attach() in your method directives? From what I understand that is supposed to refresh the UI, so maybe that will fix the inconsistent states you’re seeing in the UI?

1 Like

I hadn’t, but I tried just now and it makes no difference. I think dsHolder.Attach() allows you to replace ds with a different instance. In my case, I’m calling BO methods that operate on the original instance.

Darn. One more trick that’s worked for me in the past was to do your thing in a method directive and then in the UI customization have an AfterAdapterMethod event handler that does an oTrans.Refresh(). But things start to get hacky going down that path to the point where you may be better off doing the whole thing in client side Customization…

I figured out that SalesOrderForm.MainToolManager.Tools is an instance of RootToolsCollection. Unfortunately, there doesn’t seem to be any way to enumerate its string indexer keys. Attempting reflection throws a NRE, possibly because I’m on Cloud. Calling ToString on the collection produces the name “114 keys”. I guess I’ll use the int indexer and try all 114. :upside_down_face:

Edit: now we’re getting somewhere!

var s = new System.Collections.Generic.List<string>();
var t = SalesOrderForm.MainToolManager.Tools;
for(var i = 0; i < t.Count; ++i)
{
	s.Add(t[i].ToString());
}
var msg = string.Join(", ", s);
throw new UIException(msg);

[FileMenu] - PopupMenuTool, [EditMenu] - PopupMenuTool, [HelpMenu] - PopupMenuTool, [NewTool] - ButtonTool, [SaveTool] - ButtonTool, [DeleteTool] - ButtonTool, [ExitTool] - ButtonTool, [CutTool] - ButtonTool, [CopyTool] - ButtonTool, [PasteTool] - ButtonTool, [AboutTool] - ButtonTool, [PrintTool] - ButtonTool, [PrintPreviewTool] - ButtonTool, [ToolsMenu] - PopupMenuTool, [SaveCustomTool] - ButtonTool, [logoTool] - ButtonTool, [TransUIUtilTool] - ButtonTool, [RefreshTranslationTool] - ButtonTool, [OptionsTool] - ButtonTool, [PrimarySearchTool] - ButtonTool, [NewMenuTool] - PopupMenuTool, [NavigationTool] - ControlContainerTool, [ViewMenu] - PopupMenuTool, [FavoritesMenu] - PopupMenuTool, [ActionsMenu] - PopupMenuTool, [SaveMenuTool] - PopupMenuTool, [UndoTool] - ButtonTool, [CloseOrderTool] - ButtonTool, [CloseLineTool] - ButtonTool, [CloseReleaseTool] - ButtonTool, [ReopenOrderTool] - ButtonTool, [ReopenLineTool] - ButtonTool, [ReopenReleaseTool] - ButtonTool, [OrderLineTool] - PopupMenuTool, [OrderHeaderTool] - PopupMenuTool, [DiscountTool] - ButtonTool, [GetQuoteTool] - ButtonTool, [OrderJobWizardTool] - ButtonTool, [SendICPOSugTool] - ButtonTool, [RemovePOLinkTool] - ButtonTool, [IncomingICPOSugTool] - ButtonTool, [JobManagerTool] - ButtonTool, [OrderReleaseTool] - PopupMenuTool, [CapPromiseTool] - ButtonTool, [AddContractsTool] - ButtonTool, [SalesOrderPrintTool] - ButtonTool, [AuthorizeTool] - ButtonTool, [DepositTool] - ButtonTool, [SaleTool] - ButtonTool, [ClearCCTool] - ButtonTool, [VoidTool] - ButtonTool, [CreditCard] - PopupMenuTool, [ReserveOrderTool] - ButtonTool, [ReserveLineTool] - ButtonTool, [ReserveRelTool] - ButtonTool, [CreditTranTool] - ButtonTool, [CopyOrder] - ButtonTool, [TaxIntegrationTool] - PopupMenuTool, [TaxConnectEnabled] - StateButtonTool, [ValidateAddrTool] - ButtonTool, [CalculateTaxTool] - ButtonTool, [ViewTaxTool] - ButtonTool, [BuildFromHistoryTool] - ButtonTool, [CreateDemandTool] - ButtonTool, [RetInvQtyPrefTool] - StateButtonTool, [PrintProFormaInvc] - ButtonTool, [Auto Retrieve] - PopupMenuTool, [AutoRetrieveRelJobsTool] - StateButtonTool, [OrderAutomationExceptionsTool] - ButtonTool, [ValidateRelAddrTool] - ButtonTool, [ClearTool] - ButtonTool, [RefreshTool] - ButtonTool, [AttachmentTool] - ButtonTool, [CallLogTool] - ButtonTool, [MemoTool] - ButtonTool, [AuditLogTool] - ButtonTool, [ChangeLogTool] - ButtonTool, [HelpTool] - ButtonTool, [FieldHelpTool] - ButtonTool, [HelpTutorialTool] - ButtonTool, [_sbtCurrencyToggle] - ComboBoxTool, [EpicorMenu] - PopupMenuTool, [CustomerCenterTool] - ButtonTool, [OnlineSupportTool] - ButtonTool, [ShellPrevTool] - ButtonTool, [ShellNextTool] - ButtonTool, [ShellHomeTool] - ButtonTool, [VideoHelpTool] - ButtonTool, [EpiAddNewnewUPSEmail] - ButtonTool, [EpiAddNewnewOrdRelTax] - ButtonTool, [EpiAddNewnewRelease] - ButtonTool, [EpiAddNewnewLine] - ButtonTool, [EpiAddNewnewSalesKit] - ButtonTool, [EpiAddNewnewOrder] - ButtonTool, [EpiAddNewnewHeaderMiscCharge] - ButtonTool, [EpiAddNewnewLineMiscCharge] - ButtonTool, [SendMenu] - PopupMenuTool, [SendRecordEmailTool] - ButtonTool, [SendRecordDeskTopTool] - ButtonTool, [SendRecordFileTool] - ButtonTool, [SendListEmailTool] - ButtonTool, [SendListDesktopTool] - ButtonTool, [SendListFileTool] - ButtonTool, [CustomizeTool] - ButtonTool, [PersonalizeTool] - ButtonTool, [LaunchXmlHelpTool] - ButtonTool, [LaunchFieldHelpTool] - ButtonTool, [SaveLayoutsTool] - ButtonTool, [MRUTool] - ListTool, [ContractLineTool] - ButtonTool, [QuoteLineTool] - ButtonTool, [ExportFormTool] - ButtonTool, [ResetLayoutsToBaseTool] - ButtonTool, [ResetLayoutsToLastSavedTool] - ButtonTool

1 Like

Since you are doing your processing in post processing to add the line etc, like @TomAlexander said, you should once you re done adding your line etc.
Call GetByID to get a “fresh” Copy of the dataset, then use dsHolder.Attach() to replace the dataset in question in the UI.

This should force the UI to refresh correctly and clear out any gremlins.

I’m not adding the line to the ds myself. I originally tried this in post on ChangeCounterSale (other calls imitating the trace log omitted):

var _ds = ds; // because you can't pass a property as ref
CallService<Erp.Contracts.SalesOrderSvcContract>(s => s.OrderDtlGetNewCounterSale(ref _ds, _ds.OrderHed.Single().OrderNum));

This modifies the ds that’s already attached. Calling dsHolder.Attach() does nothing. Just to be sure, I confirmed empirically that calling it makes no difference in the returned ds or the UI behavior. The same is true if I try calling ChangeCounterSale in post on ChangeCustomer. The UI is not prepared to receive a ds where OrderHed.CounterSale is true. It seems that the UI is not fully bound to its data model. The layout changes that occur between normal mode and counter sale mode can only be initiated by UI interactions.

Here’s what works:

private static readonly string[] gWalkIns = new string[] { "301", "302", "303", "304", "305", "310", "312", "314", "TEST" };

private void OrderHed_AfterFieldChange(object sender, DataColumnChangeEventArgs args)
{
	// https://docs.microsoft.com/en-us/dotnet/api/system.data.datacolumnchangeeventargs
	switch (args.Column.ColumnName)
	{
		case "CustomerCustID":
			var custId = args.ProposedValue.ToString().ToUpper();
			if(System.Linq.Enumerable.Contains(gWalkIns, custId))
			{
				// https://www.epiusers.help/t/clicking-on-off-a-checkbox/47726/8
				EpiDataView edv = oTrans.Factory("OrderHed");
				edv.dataView[edv.Row].BeginEdit();
				edv.dataView[edv.Row]["CounterSale"] = true;
				edv.dataView[edv.Row].EndEdit();
				edv.Notify(new EpiNotifyArgs(this.oTrans, edv.Row, edv.Column));
			}
			break;

		case "CounterSale":
			// TODO only do this conditionally
			SalesOrderForm.MainToolManager.Tools["EpiAddNewnewLine"].SharedProps.Shortcut = System.Windows.Forms.Shortcut.ShiftF9;
			System.Windows.Forms.SendKeys.Send("+{F9}");
			// couldn't get anything else to work - SalesOrderForm has no AppControlPanel
			//SalesOrderForm.AppControlPanel.HandleToolClick("EpiAddNewnewLine", new Infragistics.Win.UltraWinToolbars.ToolClickEventArgs(SalesOrderForm.MainToolManager.Tools["EpiAddNewnewLine"], null));
			break;
	}
}

This simulates a click on the Counter Sale checkbox when the customer ID changes to one of our counter sale customers. This calls ChangeCounterSale. When the Counter Sale checkbox is updated from the ds returned by ChangeCounterSale, it activates the EpiAddNewnewLine menu item.

Now I just need a condition to not always do this…

1 Like

Well, that sounds like a bug. Could be worth opening a ticket, to at least give Epicor a data point on the problem - even though you probably won’t get anywhere with it…

Are there any examples/tutorials so that I can read and learn from?
Completely oblivious in this area…

Just to be extra sure, I tested what happens if I call GetByID to refresh the dataset before calling dsHolder.Attach() with the dataset from GetByID. This requires calling Update first to assign an OrderNum, since GetByID requires it. But it makes no difference. The UI mode still doesn’t change when checking Counter Sale wasn’t initiated from the UI.

I feel like I have to pick my battles with what I bother reporting, and I don’t even really consider this a bug. I wouldn’t expect the Infragistics UI toolkit to allow data bindings to drive UI layout changes, just individual field values. My expectations of of Infragistics are set by comparison to Java Swing from the late '90s, which had much more flexible layouts.