Solved

Seeking advice on creating a "Lockout" utility for processes performed by my application

  • 3 February 2023
  • 5 replies
  • 72 views

Userlevel 6
Badge +3

I don’t have an actual issue/error here.  I am just seeking advice on an alternative way to accomplish my goal.

In my Merge Tools application, you can merge customers and vendors (at this time...I hope to add other merge options later).  However, I want to prevent a user from merging vendors and customers simultaneously as this will surely cause errors as common tables are being modified by both processes in transactions and I don’t want changes from one process overwriting changes from the other process.

Each merge process has it’s own screen.  I currently have it so that you cannot process a merge unless the users are locked out.

The merge buttons are disabled unless the system is locked out.  Here they are enabled as I have the Lockout turned on.

To prevent a user from performing merges on more than one option, I may need to create a screen to do something similar to the Apply Updates screen.

 

I am thinking of creating a form that the user can select the current merge process being performed.  But that adds a bit more complexity.  I would like to have the program simply lock out other merge tools when one merge tool is being used. 

A custom “lockout” screen would work as I could use a custom table to track if a merge is being processed (similar to the UPLock table). 

I would prefer to have a way to put a record in the table to “lock” other merge processes when one merge process is underway by adding a record to a lock table.  First, the page would check to see if any other merges are happening.  Then, after the merge customer process is done, it removes the lock record from the custom table.  The issue I see is that if a user is processing a customer merge and simply closes the screen, the table would not be updated to show that the customer merge is no longer being processed.  This would lock out any other merge processors even if nothing is being done any longer.  

I think I will need to create a new screen and table to manage which merge process is being performed.  

Does anyone have any other ideas?  

 

icon

Best answer by Leonardo Justiniano 3 February 2023, 22:50

View original

5 replies

Userlevel 6
Badge +4

Hi @joe21 

Do you really need everybody but Administrators out of the system? LOCKOUT function kicks out all users when is in effect. Having said that, How many users have the Administrator role assigned?

Apply Updates Help

Are those Administrator users running your merging tool with different scenarios concurrently?

 

I suggest to implement a sort of queue for this process so anyone that tries to run a merge, get the request captured to be process later by a scheduled process. But in the end is just a suggestion. 

 

Userlevel 6
Badge +3

Hi @Leonardo Justiniano 

Good question.  This merge program makes updates to over 100 tables potentially depending on which modules you are using.  It updates customer history statistics for instance.  If you are in the middle of a merge and someone posts an invoice to a customer that is being removed (merged into another customer), the “FROM” customer may be  removed from the system during the time that invoice is being posted.  When the other users transaction attempts to post, it may fail because by the time that transaction is posting, the customer may be in a deleted status.

The merge tools I am creating are not for casual use.  They should only be used by a user (Admin) who has authority to make very pervasive changes to the system and all precautions should be taken to prevent possible data corruption.  All merges are wrapped in a transaction so if something goes wrong, the merge will fail with no negative impact.  But it needs to be performed in isolation.   

It may not seem obvious, but merging customers or vendors requires changes to tables that the Acumatica framework does not allow such as changing the value of a key field in a DAC.  Acumatica will not let you change the customer ID (BAccountID) on an ARTran record for example.  You would have to delete the original and create a new one which may change the history of who created the record and a whole host of other implications.  That is just a simple example.  I’ve worked for months on this application developing and testing exhaustively.  So, I am really aware of what can happen if it is not used cautiously.  Hence, only use it when nobody else can.  :-)  Even merging customers on one tab and vendors on another tab can cause errors because many of the same tables are updated for both.  You might not think that a shipping table will have customers and vendors in it, but there are a ton of crossovers in location id’s etc.

If anyone wants to know why Acumatica doesn’t yet provide the ability to merge customers or vendors, I think I would consider myself an expert as to why they don’t.  😉

Thanks for the comment though.  I can always count on you for support and great advice.  You saved my bacon on the modifying the customer number description display value on the customer lookup field!

Thanks!

Joe

 

Userlevel 6
Badge +3

I created a solution to my locking issue.  I doubt highly that anyone would need to know what I did, but if you do, please let me know and I will post the code.

 

Userlevel 6
Badge +3

I was asked to provide my solution.  Hope you like it! 😊

I created a “setup” table to hold one record showing which module is currently processing merges.  It only has two fields, CompanyID and Module.

	#region ICSMergeLock
[Serializable]
[PXCacheName("ICSMergeLock")]
public class ICSMergeLock : IBqlTable
{
#region Module
[PXDBString(2, IsFixed = true)]
[PXDefault(MergeType.MergeTypeAvailable)]
public virtual string Module { get; set; }
public abstract class module : PX.Data.BQL.BqlInt.Field<module> { }
#endregion
}
#endregion

On the graph, I use a field in the Filter to show the value from the ICSMergeLock table.  If it is available, the process buttons are enabled.  I also created a LockoutStatus field to show if the site is locked out.  If the site is not locked out, the process buttons are disabled.

These are the field on the graph Filter

			#region MergeAvailability
[PXString(2, IsFixed = true)]
[PXUIField(DisplayName = "Processor Availability", Enabled = false)]
[MergeAvailability]
public virtual string MergeAvailability { get; set; }
public abstract class mergeAvailability :
PX.Data.BQL.BqlString.Field<mergeAvailability> { }
#endregion

#region LockoutStatus
[PXString(2, IsFixed = true)]
[PXUIField(DisplayName = "Lockout Status", Enabled = false)]
[LockoutStatus]
public virtual string LockoutStatus { get; set; }
public abstract class lockoutStatus :
PX.Data.BQL.BqlString.Field<lockoutStatus>
{ }
#endregion

 

When a user opens a merge processor, say the Customer merge processor, the Lockout status and Merge status fields are checked and the buttons are enabled/disabled accordingly.

This is the custom attribute applled to the LockoutStatus and MergeAvailability fields.  They are disabled dropdown lists on the form.

	public class LockoutStatusAttribute : PXStringListAttribute
{
public LockoutStatusAttribute() : base(
new string[]
{
LockoutStatus.NotLockedOut,
LockoutStatus.LockedOut
},
new string[]
{
ICSMessages.UsersNotLockedOut,
ICSMessages.UsersLockedOut
})
{ }
}

public class MergeAvailabilityAttribute : PXStringListAttribute
{
public MergeAvailabilityAttribute() : base(
new string[]
{
MergeType.MergeTypeAvailable,
MergeType.MergeTypeCustomer,
MergeType.MergeTypeVendor
},
new string[]
{
ICSMessages.MergingAvailable,
ICSMessages.CurrentlyMergingCustomers,
ICSMessages.CurrentlyMergingVendors
})
{ }
}

I did this part in the RowSelected of the Filter.  I know I am not supposed to do updates in the RowSelected, but there are only a couple hits to that method so it won’t impact performance.  I honestly don’t know where else I could do it.

		protected void _(Events.RowSelected<RecordsToProcessFilter> e)
{
bool showButton = true;
RecordsToProcessFilter row = e.Row;
if (row == null) return;

UPLock locked = LockedOut.Select();

if (locked == null)
{
showButton = false;
}

ICSMergeLock mergeLocked = MergeLockStatus.Select();

if (mergeLocked == null)
{
var setupGraph = PXGraph.CreateInstance<ICSMergeLockSetup>();
ICSMergeLock newRow = new ICSMergeLock();
newRow.Module = MergeType.MergeTypeAvailable;
// Acuminator disable once PX1044 ChangesInPXCacheInEventHandlers [this can only fire one time. Ever.]
setupGraph.Setup.Insert(newRow);
// Acuminator disable once PX1043 SavingChangesInEventHandlers [this can only fire one time. Ever.]
setupGraph.Actions.PressSave();
setupGraph.Clear();
}

mergeLocked = MergeLockStatus.Select();

if (mergeLocked.Module != MergeType.MergeTypeCustomer && mergeLocked.Module != MergeType.MergeTypeAvailable) showButton = false;

if (Filter.Current != null)
{
Filter.Current.MergeAvailability = mergeLocked.Module;
if (locked == null)
{
Filter.Current.LockoutStatus = ICSMergeTools.Helper.LockoutStatus.NotLockedOut;
}
else
{
Filter.Current.LockoutStatus = ICSMergeTools.Helper.LockoutStatus.LockedOut;
}
}

CustomersToMerge.SetProcessAllEnabled(showButton);
CustomersToMerge.SetProcessEnabled(showButton);
}

Where I check if mergeLocked == null, the record is added to the ICSMergeLock table.  I didn’t want to use a DB script to prepopulate this field when the project is published because I have no way of knowing what the CompanyID is for the tenant.  That code will only fire one time (ever).

In the event that one processor does not close out gracefully I added a PXButton on the screen to allow the user to manually reset the merge availability status.  The screen looks like this:

When the Process button is clicked, the ICSMergeLock table is updated to indicate which merge processor is active.  At the end of the process, it resets that field back to available.

If anyone wants any more details or clarification, just let me know.

Userlevel 6
Badge +3

One more thing, I created a graph just to handle the saving to the ICSMergeLock table.

I also just noticed the bad grammar in my print screen above... 

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved