Update Closed Packing Slip via Customization

I’m looking to update a custom field on the ShipDtl table that is used for on-time delivery tracking. I have this working on the ShipHead table with another field, but that affects all lines when I use it. It’s great when the entire pack is considered late incorrectly, but when there’s multiple lines and only 1 of them needs to have its on-time status reversed, that doesn’t work out very well. I’ve applied the same logic for the new ShipDtl field, OTDlineSwap_c, but get an error when trying to use it.

image

If I click yes, I get this:

image

Here’s the control field on the Lines tab in Customer Shipment Entry:

I get the native control references at the beginning of my code, I used the event wizard to add the functions. All is fine for my field on ShipHead, the issue is the field on ShipDtl.

I’m using the EpiDataViews and coding the field updates instead of using EpiBindings so I can use these fields if the pack is closed or not.

private void edvShipDtl_EpiViewNotification(EpiDataView view, EpiNotifyArgs args)
	{
		// ** 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))
		{

			if ((args.Row > -1))
			{
			if(view.dataView[view.Row]["OTDlineSwap_c"].ToString() == "True" )
			{
			cbotdline.Checked = true;
			}
			else
			{
			cbotdline.Checked = false;
			}
			}
	}

private void cbotdline_CheckedChanged(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **

				if(cbotdline.Checked == false)
				{

				EpiDataView view = (EpiDataView)(oTrans.EpiDataViews["ShipDtl"]);
					
				view.dataView[view.Row].BeginEdit();
				view.dataView[view.Row]["OTDlineSwap_c"] = 0;
				view.dataView[view.Row].EndEdit();
		
				}
		
				if(cbotdline.Checked == true)
				{
					

				EpiDataView view = (EpiDataView)(oTrans.EpiDataViews["ShipDtl"]);
					
				view.dataView[view.Row].BeginEdit();
				view.dataView[view.Row]["OTDlineSwap_c"] = 1;
				view.dataView[view.Row].EndEdit();

					
				}
	}

I’m doing essentially the same thing for the field on ShipHead, and it works. Here’s the code for that:

	private void edvShipHead_EpiViewNotification(EpiDataView view, EpiNotifyArgs args)
	{
		// ** 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))
		{
			
			if ((args.Row > -1))
			{
			if(view.dataView[view.Row]["OTDswap_c"].ToString() == "True" )
			{
			cbontime.Checked = true;
			}
			else
			{
			cbontime.Checked = false;
			}
			}
	      }
     }

private void cbontime_CheckedChanged(object sender, System.EventArgs args)
	{
		// ** Place Event Handling Code Here **

				if(cbontime.Checked == false)
				{

				EpiDataView view = (EpiDataView)(oTrans.EpiDataViews["ShipHead"]);
					
				view.dataView[view.Row].BeginEdit();
				view.dataView[view.Row]["OTDswap_c"] = 0;
				view.dataView[view.Row].EndEdit();
		
				}
		
				if(cbontime.Checked == true)
				{
					

				EpiDataView view = (EpiDataView)(oTrans.EpiDataViews["ShipHead"]);
					
				view.dataView[view.Row].BeginEdit();
				view.dataView[view.Row]["OTDswap_c"] = 1;
				view.dataView[view.Row].EndEdit();

					
				}
	}

Is what I’m trying to accomplish even possible?

Appreciate any help or advice!

If the record is locked, you won’t be able to update it with the user interface (which runs the Update BO server-side).

For the shipping screen, if it isn’t invoiced, you can uncheck Shipped and reopen the line. That unlocks the record and your customization should work. If it is invoiced, you can’t reopen it.

Perhaps you could do it writing directly to the DB, instead of with the save button on the UI (Update BO). I haven’t done that with a customization so someone else could better help with that. It isn’t too hard with a BPM. Search for “DB.Validate()” for examples.

I would do this from an updatable dashboard so you can set more shipments faster and it is a future way to do this and not in a classic customization.

@TerryR, thanks, that’s not the answer I was looking for but is the answer I expected unfortunately. I may write a quick external app to handle the database updates with a nice UI flow.

@gpayne, true, but I don’t think an updatable dashboard will work around the BO logic that is preventing me from modifying a closed pack.

I’m going to keep thinking on this for now, I’ll update if I find a solution or make one.

@CasterConcepts It’s a ud field write an advance update in the ubaq.

2 Likes

@gpayne, You just blew my mind lol… Never messed with the advance update in a ubaq before, was not aware this was possible. Much appreciated!

2 Likes

Question on “write an advance update in the ubaq”… does it require custom code (because invoking a BO will bring with it all the logic)?

I cannot custom code anything, but, like @CasterConcepts, need to update a UD field (two, actually) after the pack line is closed.

The end goal is to schedule a function to run the UBAQ each night to make the updates (thanks to @caleb.grundmeier in this post for that part).

But… before I go waste anyone’s time on the “here is what I have tried, please tell me what I am missing” path, I need to make sure that it is do-able with widgets.

Thanks.

Yes, custom code was required for my implementation, in the BPM Directives Configuration. I think advanced ubaq is going to require code for what you are trying to do.

1 Like

That is what I was afraid of. Thanks. :frowning:

@skhayatt Coding yes, but something I feel you can do. Here is what I use and it is simple. Make sure to add the SysRowID of the table or tables you want to update to the baq. bonus if the BAQ has the data calculated you want to add. I literally had this for ShipDtl and updated all ShipDtl to ShipHead and changed the field I was updating. I have done this tons of times.

If you are going to schedule a function to run the baq then this code goes post processing on GetList

/* update ShipHead */

foreach (var ttResultsRow in result.Results) //.Where(row=> !row.Unchanged()))
{
    
    var ShipHead = Db.ShipHead.Where(row=> row.Company == CompanyID && row.SysRowID == ttResultsRow.ShipHead_SysRowID).FirstOrDefault();
    if (ShipHead != null)
    {
        ShipHead.SetUDField<System.String>("ShortChar01",ttResultsRow.Customer_Name);
     
    }
}

3 Likes

Hi Greg…

Thanks. Will give it shot and see what happens. And yes, I do have the UD field values stored in BAQ calculated fields.

Stay tuned…

2 Likes

@skhayatt I edited to take the where out so this will fire on getlist.

1 Like

More questions – if you have time. This may be more trouble than you bargained for.

The function runs without error but nothing gets updated.

Post-processing on GetList:

Code:

foreach (var ttResultsRow in result.Results)
{
    
    var ShipDtl = Db.ShipDtl.Where(row=> row.Company == CompanyID && row.SysRowID == ttResultsRow.ShipDtl_SysRowID).FirstOrDefault();
    if (ShipDtl != null)
    {
        ShipDtl.SetUDField<System.Boolean>("ShipDtl_RRList_c",ttResultsRow.Calculated_tempRRList_c);
        ShipDtl.SetUDField<System.Int32>("ShipDtl_ShipTimeCode_c",ttResultsRow.Calculated_tempShipTimeCode_c);
     
    }
}

At first I got an error that there was no Update method, so I created a Base Processing method that also sets the two fields:
image


queryResultDatasetResultsRow.Calculated_tempRRList_c


queryResultDatasetResultsRow.Calculated_tempShipTimeCode_c

Two questions:
1 - in the code block that you created is CompanyID supposed to be CompanyID or should it be changed to “mycompanyID”?

2 - is there any way to test it without running the function? I was expecting to see the updated values populated in the BAQ after I ran GetList.

Thanks for your help.

No worries. We will get this going.

For the update error I usually just make a custom code block with a comment /* */
CompanyID is short for Session.CompanyID which is the current company.

I test by using the ubaq and would probably use just one pack. You can also add Ice.Diagnostics.Log.WriteEntry in the code to see the value that should be set.

You may have to add a Db.Validate();

2 Likes

Am only working with 5 records…

Changed the Base method for Update to /* */
Added Db.Validate(); as a third line in the middle section

Added a message box to the Post-Processing directive on GetList to see if it is firing. It is.

Updates still not happening. The values are present in the calculated temp fields, but not copied to the table field.

How/where do I add the Ice.Diagnostics.Log.WriteEntry statement? I tried to add it under DbValidate(); but got this message: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

Question on SetUDField: the fields that I am setting are not the built-in Epicor fields (ShortChar01, etc.) but are created through UD column maintenance. Shoudl I be using ShipDtl_UD as the table name in SetUDField?

Thanks again.

This works with or without the Validate in 21.2. I will copy it to a 23.2 instance and test there.

/* update */
foreach (var ttResultsRow in result.Results)
{
    
    var ShipDtl = Db.ShipDtl.Where(row=> row.Company == Session.CompanyID && row.SysRowID == ttResultsRow.ShipDtl_SysRowID).FirstOrDefault();
    if (ShipDtl != null)
    {
    Ice.Diagnostics.Log.WriteEntry($"In the update Company is {ttResultsRow.ShipDtl_Company}");
        ShipDtl.SetUDField<System.String>("Character01",ttResultsRow.ShipDtl_Company);
        //ShipDtl.SetUDField<System.Boolean>("ShipDtl_RRList_c",ttResultsRow.Calculated_tempRRList_c);
        //ShipDtl.SetUDField<System.Int32>("ShipDtl_ShipTimeCode_c",ttResultsRow.Calculated_tempShipTimeCode_c);
      //Db.Validate();
    }
}

23.2.8 took a bit more to get it to update. I added your fields since this was a temp dev. the below does update for me.

/* update */



using (System.Transactions.TransactionScope txScope = IceDataContext.CreateDefaultTransactionScope())//start the transaction
{  

    foreach (var ttResultsRow in result.Results)
    {
    
    
        var ShipDtl = Db.ShipDtl.With(LockHint.UpdLock).Where(row=> row.Company == Session.CompanyID && row.SysRowID == ttResultsRow.ShipDtl_SysRowID).FirstOrDefault();
        if (ShipDtl != null)
        {
            Ice.Diagnostics.Log.WriteEntry($"In the update TimeCode is {ttResultsRow.Calculated_tempShipTimeCode_c}");
        
            ShipDtl.SetUDField<System.Boolean>("ShipDtl_RRList_c",ttResultsRow.Calculated_tempRRList_c);
            ShipDtl.SetUDField<System.Int32>("ShipDtl_ShipTimeCode_c",ttResultsRow.Calculated_tempShipTimeCode_c);
            
            Db.Validate(ShipDtl);
        }
        
    
    }
     
txScope.Complete();
}


image

1 Like

Thank you so much.

We are on 2023.1.7. so I did a cut and paste of the 2023 version. When I checked the syntax (in the custom code editor, I got this message:

The type ‘TransactionScope’ is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Transactions.Local, Version=6.0.0.0, Culture=neutral,

1 Like

This is an error on your local machine with your .net sdk / runtime, save and it should still go through.

1 Like
1 Like