How can I manipulate the Audit screen (SM205510) through code - acumatica

I'm trying to manipulate the Audit screen (SM205510) through code, using a graph object. The operation of the screen has processes that seem to work when a screen ID is selected in the header. This is my code to create a new record:
Using PX.Data;
Using PX.Objects.SM;
var am = PXGraph.CreateInstance<AUAuditMaintenance>();
AUAuditSetup auditsetup = new AUAuditSetup();
auditsetup.ScreenID = "GL301000";
auditsetup = am.Audit.Insert(auditsetup);
am.Actions.PressSave();
Now, when I execute the code above, it creates a record in the AUAuditSetup table just fine - but it doesn't automatically create the AUAuditTable records the way they are auto-generated in the screen (I realize that the records aren't in the database yet) - but how can I get the graph object to auto-generate the AUAuditTable records in the cache the way they are in the screen?
I've tried looking at the source code for the Audit screen - but it just shows blank, like there's nothing there. I look in the code repository in Visual Studio and I don't see any file for AUAuditMaintenance either, so I can't see any process that I could run in the graph object that would populate those AUAuditTable records.
Any help would be appreciated.
Thanks...

If I had such a need, to manipulate Audit screen records, I'd rather create my own graph and probably generate DAC class. Also I'd add one more column UsrIsArtificial and set it to false by default. And then manage them as ordinary records, but each time I'll add something, I'd set field UsrIsArtificial to false.
You can hardly find how that records are managed at graph level because that records are created and handled on on Graph level, but on framework level. Also think twice or even more about design, as direct writing into Audit history may cause confusion for users in the system of what was caused by user, and what was caused by your code. From that point of view I would rather add one more additional table, then add confusion to existing one.

Acumatica support provided this solution, which works beautifully (Hat tip!):
var screenID = "GL301000"; //"SO303000";
var g = PXGraph.CreateInstance<AUAuditMaintenance>();
//Set Header Current
g.Audit.Current = g.Audit.Search<AUAuditSetup.screenID>(screenID);
if (g.Audit.Current == null) //If no Current then insert
{
var header = new AUAuditSetup();
header.ScreenID = screenID;
header.Description = "Test Audit";
header = g.Audit.Insert(header);
}
foreach (AUAuditTable table in g.Tables.Select())
{
table.IsActive = true;
//Sets Current for Detail
g.Tables.Current = g.Tables.Update(table);
foreach (AUAuditField field in g.Fields.Select())
{
field.IsActive = false;
g.Fields.Update(field);
}
}
g.Actions.PressSave();

Related

Restore Fixed Assets records with custom field and related table records

In Fixed Assets (FA303000), I have customization that contains two custom fields and one custom table which is a child table referenced with FixedAsset's AssetID column.
Now, for some reason, we have to delete half of our fixed assets and put it back into Acumatica again. We are not creating the whole snapshot to create and restore the process. We are processing half of fixed assets records by removing it and put it back.
My initial thought was I just needed to put those records into some temporary table using (select * into duplicate_FixedAsset from FixedAsset.) then, delete those records from FixedAsset and put it back into FixedAsset.
Custom Fields record from Fixed Assets will be back, and for my custom child table that is linked via AssetID, I can use AssetCD to get AssetID and link it back again and everything should be fine.
But, I got wrong when inserting this record didn't appear back into the Fixed Assets page.
Upon inspecting Row_Deleting event, I found below code snippet.
protected virtual void FixedAsset_RowDeleting(PXCache sender, PXRowDeletingEventArgs e)
{
FixedAsset asset = (FixedAsset)e.Row;
if (asset == null) return;
if (null != (FATran)PXSelect<FATran, Where<FATran.assetID, Equal<Current<FixedAsset.assetID>>, And<FATran.batchNbr, IsNotNull>>>.SelectSingleBound(this, new object[] { asset }))
{
throw new PXSetPropertyException(Messages.BalanceRecordCannotBeDeleted);
}
this.EnsureCachePersistence(typeof(FARegister));
this.EnsureCachePersistence(typeof(FABookHistory));
foreach (FARegister reg in PXSelectJoinGroupBy<FARegister,
LeftJoin<FATran, On<FARegister.refNbr, Equal<FATran.refNbr>>>,
Where<FATran.assetID, Equal<Required<FixedAsset.assetID>>>,
Aggregate<GroupBy<FARegister.refNbr>>>.Select(this, asset.AssetID))
{
this.Caches<FARegister>().Delete(reg);
}
foreach (FABookHistory hist in PXSelect<FABookHistory, Where<FABookHistory.assetID, Equal<Required<FixedAsset.assetID>>>>.Select(this, asset.AssetID))
{
this.Caches<FABookHistory>().Delete(hist);
}
}
So, it doesn't seem that simple process that I have thought at the beginning. It shows that FARegister and FABookHistory are linked as well.
So, I would really appreciate it if I know what is the proper steps to follow to make sure that not only I get the Fixed Assets back without breaking any relation to other tables but also properly link back to my custom fields and table.

Navigating from Location to Workorder

I need to :
1. Create a single page location application
2. Display all the asset present in the selected location in a table
3. Provide a button from which user can navigate to WOTRACK to view all the workorder(s) created on selected location and its asset(s).
I am facing difficulty in the 3rd one. I have tried Launch in Context and it is working fine except am not able to pass sql query like 'location={location} and assetnum in ({asset.assetnum})'. I need to filter workorders with particular location and all its assets.
I tried to save all the assets in the location to a Non-persistant attribute and passing the values of the attribute in the Launch in context url, Its working as expected but to do so I have written a script on 'Initialize value' which is causing performance issues.
script goes like this:
from psdi.server import MXServer;
from psdi.mbo import MboConstants;
if app == "LOCATION1" :
if mbo.getString("LOCATION") is not None:
Locsite = mbo.getString("SITEID")
desc = mbo.getString("DESCRIPTION")
MaxuserSet = MXServer.getMXServer().getMboSet("MAXUSER", mbo.getUserInfo())
MaxuserSet.setWhere(" userid='"+user+"' ")
MaxuserSet.reset()
UserSite = MaxuserSet.getMbo(0).getString("DEFSITE")
if Locsite == UserSite:
AssetSet = mbo.getMboSet("ASSET")
AssetSet.setFlag(MboConstants.DISCARDABLE, True);
if not AssetSet.isEmpty():
AssetList = ""
AssetMbo = AssetSet.moveFirst()
while AssetMbo is not None:
AssetList = AssetList + str(AssetMbo.getString("ASSETNUM")) + "%2C"
AssetMbo = AssetSet.moveNext()
mbo.setValue("non-persitant",str(AssetList),11L)
and in the LIC url i have given : 'http://xx.x.x.xx/maximo/ui/?event=loadapp&value=wotrack&tabid=List&additionalevent=useqbe&additionaleventvalue=location={LOCATION}|assetnum={non-persistant}'
Is there any other feasible solution to the requirement?
Thanks in Advance
Launch In Context is better used for sending the user to an outside-of-Maximo application and passing along some data from inside-Maximo to provide context in that external app.
What you are doing sounds like a good place to use a workflow process with an Interaction node. The developer tells the Interaction node which app to take the user to and which Relationship to use to find the data the user should work with there.
Why don't you add a table control inside the table details (expanded table row) and show a list of the work orders there. From the WONUM in that table, you could have an app link to take them to WOTRACK, if they want more details about a particular work order. No customization (automation scripting) needed. No workflow needed. Nice and simple.

Maximo automatisation script to change statut of workorder

I have created a non-persistent attribute in my WoActivity table named VDS_COMPLETE. it is a bool that get changed by a checkbox in one of my application.
I am trying to make a automatisation script in Python to change the status of every task a work order that have been check when I save the WorkOrder.
I don't know why it isn't working but I'm pretty sure I'm close to the answer...
Do you have an idea why it isn't working? I know that I have code in comments, I have done a few experimentations...
from psdi.mbo import MboConstants
from psdi.server import MXServer
mxServer = MXServer.getMXServer()
userInfo = mxServer.getUserInfo(user)
mboSet = mxServer.getMboSet("WORKORDER")
#where1 = "wonum = :wonum"
#mboSet .setWhere(where1)
#mboSet.reset()
workorderSet = mboSet.getMbo(0).getMboSet("WOACTIVITY", "STATUS NOT IN ('FERME' , 'ANNULE' , 'COMPLETE' , 'ATTDOC')")
#where2 = "STATUS NOT IN ('FERME' , 'ANNULE' , 'COMPLETE' , 'ATTDOC')"
#workorderSet.setWhere(where2)
if workorderSet.count() > 0:
for x in range(0,workorderSet.count()):
if workorderSet.getString("VDS_COMPLETE") == 1:
workorder = workorderSet.getMbo(x)
workorder.changeStatus("COMPLETE",MXServer.getMXServer().getDate(), u"Script d'automatisation", MboConstants.NOACCESSCHECK)
workorderSet.save()
workorderSet.close()
It looks like your two biggest mistakes here are 1. trying to get your boolean field (VDS_COMPLETE) off the set (meaning off of the collection of records, like the whole table) instead of off of the MBO (meaning an actual record, one entry in the table) and 2. getting your set of data fresh from the database (via that MXServer call) which means using the previously saved data instead of getting your data set from the screen where the pending changes have actually been made (and remember that non-persistent fields do not get saved to the database).
There are some other problems with this script too, like your use of "count()" in your for loop (or even more than once at all) which is an expensive operation, and the way you are currently (though this may be a result of your debugging) not filtering the work order set before grabbing the first work order (meaning you get a random work order from the table) and then doing a dynamic relationship off of that record (instead of using a normal relationship or skipping the relationship altogether and using just a "where" clause), even though that relationship likely already exists.
Here is a Stack Overflow describing in more detail about relationships and "where" clauses in Maximo: Describe relationship in maximo 7.5
This question also has some more information about getting data from the screen versus new from the database: Adding a new row to another table using java in Maximo

How do I disable caching on certain nodes in MVCSiteMapProvider?

I need to have a menu structure that changes depending on what page the user is currently viewing. Hence I need to disable caching for certain nodes as these may change for every request. How do I do this?
I have tried setting up the DynamicNode in the following way:
var dynamicNode = new DynamicNode()
{
Title = title,
Action = actionName,
Controller = controllerName,
RouteValues = routeValues,
Attributes = attributes,
ChangeFrequency = ChangeFrequency.Always,
LastModifiedDate = DateTime.Now,
UpdatePriority = UpdatePriority.Automatic,
};
But that seems tohave no effect.
I have also set cacheDuration="0" in the Web.config file, no effect.
I've also set the following in the GetCacheDesctription of the DynamicNodeProvider
return new CacheDescription("GuideDynamicNodeProvider")
{
AbsoluteExpiration = DateTime.Now,
};
Also with no effect.
Am I using these settings incorrectly? The documentation on this aspect is rather lacking.
Disabling caching for specific nodes is not supported. However, you can disable caching for the entire sitemap by setting the cache duration to 0.
If what you are trying to do is refresh nodes when the data changes, you can use the SiteMapCacheReleaseAttribute or call SiteMaps.ReleaseSiteMap() when the data is updated.
On the other hand, if data is updated in your database from a source that is not under your control, you can implement ICacheDependency yourself to create a SqlCacheDependency class and then inject it using DI. Have a look at the RuntimeFileCacheDependency class to see how that can be done.
Note that the reason the ChangeMonitor is put into a list is so it will support the RuntimeCompositeCacheDependency, which allows you to configure multiple cache dependencies for the same cache.

Change default entity to search for in CRM 2011 when adding connection account

In CRM 2011, under Account, there is the ability to add Connection. After clicking add Connection, you can browse/search for Name which defaults to "Contact". Is there a way to switch "Contact" to "Account" by default without having to switch the select box?
Apparently is just doing this:
document.getElementById("record2id").setAttribute("defaulttype", "1");
But i do a little search and this not work for the dialog of connections, check this alternative.
This doesn't work with connections.
With connections the object type code for the lookup is set in the Mscrm.Connection.preSelectObjectType function in Microsoft Dynamics CRM\CRMWeb_static\entities\connection.js.
There is a line like
$v_2.set_defaultType($v_3);
where the object type is set. $v_3 is set depending on the chosen role.
So you need to change it to
$v_2.set_defaultType(Mscrm.EntityTypeCode.Account.toString());
But you will lose the role based lookup configuration, so you might want to modify that. Plus it is unsupported and you will need to take into account the updating behavior when installing new rollups that change the connection.js (i.e. copy newer connection.js files by hand from an updated system, and customize them again).
Here are two approaches. Both works, but the first adds the type record Icon to the loockup field even if it empy. The second doesn't do that but a little bit more risky as it depends on the internal method names.
1st method:
if (IsNull(Xrm.Page.getAttribute('record2id').getValue())) {$("#record2id")[0].DataValue = [{ "type": scrm.EntityTypeCode.SystemUser.toString() }];}
2nd Method
document.original_preSelectObjectType = Mscrm.Connection.preSelectObjectType;
Mscrm.Connection.preSelectObjectType = function (roleLookup, peerRoleLookup) {
if (IsNull(roleLookup.DataValue) && IsNull(peerRoleLookup.DataValue) && !window.event.srcElement.DataValue) {
var $v_0 = window.event.srcElement;
$v_0.defaulttype = Mscrm.EntityTypeCode.SystemUser.toString();
$v_0.DefaultViewId = "";
$v_0.Lookup(true, false, null, false);
}
else {
document.original_preSelectObjectType(roleLookup, peerRoleLookup); }}

Resources