Is it possbile to move aotnode in axapta through code(I want to achive the same movement as done via alt-up, alt-down)
Dynamics AX 2009 has AOTmove method, but when I try
#AOT
ProjectNode root;
//SysContextMenuAOT ctx = new SysContextMenuAOT();
ProjectGroupNode firstChild;
ProjectGroupNode secondChild;
;
//root=ctx.first();
root = infolog.projectRootNode().AOTfindChild("Private").AOTfindChild("TestProject");
root = root.getRunNode();
firstChild = root.AOTfirstChild();
secondChild = firstChild.AOTnextSibling();
secondChild = firstChild.AOTnextSibling();
secondChild.AOTMove(secondChild.AOTparent());
and then call it on whole project it successfully moves secondChildNode, BUT it deletes every subnode inside of secondChild.
It does not seem to work (with project nodes).
AOTmove is used exclusively in form SysFavoritesAddFavorite and SysFavoritesOrganizeFavorites.
You may get some information looking there.
Related
I am building an application that will have the ability to create agenda items to discuss in a meeting. The agenda item might include one or more attachments to discuss so there is a one to many relation between the AgendaItems and the AgendaDocs models. So far, I have an insert form that looks like this:
The "Select File" button is a drive picker and the code I have inside the onDocumentSelect event is the following:
var docs = result.docs;
var createDataSource = app.datasources.AgendaDocs.modes.create;
for(var i=0; i<docs.length-1; i++){
var uniqueDraft = createDataSource.item;
createDataSource.items.push(uniqueDraft);
}
for(var i=0; i<createDataSource.items.length-1; i++){
var draft = createDataSource.item;
createDataSource.items[i].DocTitle = docs[i].name;
createDataSource.items[i].DocURL = docs[i].url;
createDataSource.items[i].DriveID = docs[i].id;
}
console.log(createDataSource.items);
The code is supposed to fill out the the List widget below the "Select File" button, but as how you see, the three items are the same. The datasource of the List widget is "AgendaDocs.modes.create" and the datasource of the insert form is "AgendaItems.modes.create".
Reading the official documentation from appmaker, makes me think it is possible since the properties of "CreateDataSource" includes "items". I need help from an expert here. Is this possible? Am I using the wrong approach?
First things first, it seems that you are trying to create records from different models and relationship between them in a one call... at this time App Maker is not that smart to digest such a complex meal. Most likely you'll need to break your flow into multiple steps:
Create (persist) Agenda Item
Create AgendaDocs records and relation with AgendaItem
Similar flow is implemented in Travel Approval template app, but it is not exactly the same as yours, since it doesn't create associations in batches.
Going back to the original question. Yep, it is possible to have multiple drafts, but not with the Create Datasource. You are looking for Manual Save Mode. Somewhere in perfect world your code would look similar to this:
// AgendaItems in Manual Save mode
var agendaDs = app.datasources.AgendaItems;
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it.
agendaDs.createItem();
var agendaDraft = agendaDs.item;
// Field values can be populated from UI via bindings...
agendaDraft.Type = 'X';
agendaDraft.Description = 'Y';
// onDocumentSelect Drive Picker's event handler
var docsDs = agendaDs.relations.AgendaDocs;
result.docs.forEach(function(doc) {
// this line will create item on client and automatically push it
// to ds.items and set ds.item to it...however it will throw an exception
// with this message:
// Cannot save a foreign key association for the 'AgendaItem'
// relation because the target record has not been persisted
// to the server. To fix this, call saveChanges()
// on the data source for that record's model: AgendaItem
docsDs.createItem();
var docDraft = docsDs.item;
docDraft.DocTitle = doc.name;
docDraft.DocURL = doc.url;
docDraft.DriveID = doc.id;
});
// submit button click
agendaDraft.saveChanges();
I have three models:
Timesheets
Employee
Manager
I am looking for all timesheets that need to be approved by a manager (many timesheets per employee, one manager per employee).
I have tried creating datasources and prefetching both Employee and Employee.Manager, but I so far no success as of yet.
Is there a trick to this? Do I need to load the query and then do another load? Or create an intermediary datasource that holds both the Timesheet and Employee data or something else?
You can do it by applying a query filter to the datasource onDataLoad event or another event. For example, you could bind the value of a dropdown with Managers to:
#datasource.query.filters.Employee.Manager._equals
- assuming that the datasource of the widget is set to Timesheets.
If you are linking to the page from another page, you could also call a script instead of using a preset action. On the link click, invoke the script below, passing it the desired manager object from the linking page.
function loadPageTimesheets(manager){
app.showPage(app.pages.Timesheets);
app.pages.Timesheets.datasource.query.filters.Employee.Manager._equals = manager;
app.pages.Timesheets.datasource.load();
}
I would recommend to redesign your app a little bit to use full power of App Maker. You can go with Directory Model (Manager -> Employees) plus one table with data (Timesheets). In this case your timesheets query can look similar to this:
// Server side script
function getTimesheets(query) {
var managerEmail = query.parameters.ManagerEmail;
var dirQuery = app.models.Directory.newQuery();
dirQuery.filters.PrimaryEmail._equals = managerEmail;
dirQuery.prefetch.DirectReports._add();
var people = dirQuery.run();
if (people.length === 0) {
return [];
}
var manager = people[0];
// Subordinates lookup can look fancier if you need recursively
// include everybody down the hierarchy chart. In this case
// it also will make sense to update prefetch above to include
// reports of reports of reports...
var subortinatesEmails = manager.DirectReports.map(function(employee) {
return employee.PrimaryEmail;
});
var tsQuery = app.models.Timesheet.newQuery();
tsQuery.filters.EmployeeEmail._in = subortinatesEmails;
return tsQuery.run();
}
I'm currently working on a system that will export all the nodes from my AX 2009 AOT to individual XPO files, for the purpose of tracking changes in a central version control repository. I'm having a fair amount of luck, but for some reason I cannot get the Forms or Data Sets node to export at all.
This is my current code set:
private void export(str parentNode)
{
TreeNode node, parent;
str folderName;
Set permissions = new Set(Types::Class);
;
folderName = exportBaseDir + parentNode;
permissions.add(new FileIoPermission(folderName, "r"));
permissions.add(new InteropPermission(InteropKind::ClrInterop));
CodeAccessPermission::assertMultiple(permissions);
//Create Filesystem Folder if needed
if (!WinApiServer::pathExists(folderName))
System.IO.Directory::CreateDirectory(folderName);
CodeAccessPermission::revertAssert();
parent = TreeNode::findNode(parentNode);
if (parent)
node = parent.AOTfirstChild();
else
warning(strfmt("Could not parse node: %1", parentNode));
while (node)
{
this.exportNode(node);
node = node.AOTnextSibling();
}
}
When I call export(#"\Forms"); or export(#"\Data Sets"); I get a "Could not parse node" message, meaning the TreeNode::findNode() didn't resolve correctly. Running it on any other node (such as Classes) does not have this issue. This also only happens when it is run in a Batch -- running it with the client (with the CodeAccessPermission parts removed) will export all nodes as expected.
Is there something that would prohibit Forms and Data Sets from being accessed from within a Batch? If so, what can I do to access those nodes?
As far as I can tell it's a server/client issue/bug. The easy solution would be to create this method on your class:
client static TreeNode clientTreeNode(str _path)
{
return TreeNode::findNode(_path);
}
Then in your code, below the parent = TreeNode::findNode(parentNode); line, put:
parent = parent ? parent : YourClassHere::clientTreeNode(parentNode);
And that should solve your issue. You'll have a bit of digging to do in order to find out why it doesn't work on the server tier if you just must know.
Just for my curiosity (and future knowledge), how does Entity Framework 5 decide when to create a new object vs. referencing an existing one? I might have just been doing something wrong, but it seems that every now and then if I do something along the lines of:
using (TestDB db = new TestDB())
{
var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
if(currParent == null) {
Parent newParent = new Parent();
newParent.Prop = passedProp;
currParent = newParent;
}
//maybe do something to currParent here
var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
currThing.Parent = currParent;
db.SaveChanges();
}
EF will create a new Parent in the database, basically a copy of the currParent, and then set the Parent_ID value of currThing to that copy. Then, if I do it again (as in, if there's already two of those parents), it won't make a new Parent and instead link to the first one. I don't really understand this behavior, but after playing around with it for a while something like:
using (TestDB db = new TestDB())
{
var currParent = db.Parents.Where(p => p.Prop == passedProp).FirstOrDefault();
if(currParent == null) {
Parent newParent = new Parent();
newParent.Prop = passedProp;
currParent = newParent;
}
//maybe do something to currParent here
var currThing = db.Things.Where(t => t.Prop == passedPropTwo).FirstOrDefault();
currThing.Parent = db.Parents.Where(p => p.ID == currParent.ID).First();
db.SaveChanges();
}
seemed to fix the problem. Is there any reason this might happen that I should be aware of, or was there just something weird about the way I was doing it at the time? Sorry I can't be more specific about what the exact code was, I encountered this a while ago and fixed it with the above code so I didn't see any reason to ask about it. More generally, how does EF decide whether to reference an existing item instead of creating a new one? Just based on whether the ID is set or not? Thanks!
If your specific instance of your DBContext provided that specific instance of that entity to you, then it will know what record(s) in the database it represents and any changes you make to it will be proper to that(those) record(s) in the database. If you instantiate a new entity yourself, then you need to tell the DBContext what exactly that record is if it's anything but a new record that should be inserted into your database.
In the special scenario where you have multiple DBContext instances and one instance provides you this entity but you want to use another instance to work with and save the entity, then you have to use ((IObjectContextAdapter)firstDbContext).ObjectContext.Detach() to orphan this entity and then use ((IObjectContextAdapter)secondDbContext).ObjectContext.Parents.Attach() to attach it (or ApplyChanges() if you're also editing it - this will call Attach for you).
In some other special scenarios (your object has been serialized and/or you have self-tracking entities), some additional steps may be required, depending on what exactly you are trying to do.
To summarize, if your specific instance of your DBContext is "aware" of your specific instance of an entity, then it will work with it as if it is directly tied to that specific row in the database.
Here's some code,
k.Bind<IGame>().To<Game>().Named("A")
.WithConstructorArgument("ColorChoiceCount", 12);
iGame = k.Get<IGame>("A");
((Game)iGame).SelectedColor = new GameColor(System.Drawing.Color.Red);
iGame = k.Get<IGame>("A");
On the first iGame = k.Get<IGame>("A"); I get a new instance of Game.
Next line: I change one of it's properties.
Next line (iGame = k.Get<IGame>("A"); again) I get a new instance again.
What I would like is to be able to retrieve instances I've already used.
But I'm totally new to this kind of tools so I guess I'm missing something.
Thank you if you can help me.
You need to specify the lifetime of your object - by default the container will create a new instance.
The available methods are as follows:
InScope
InTransientScope
InThreadScope
InSingletonScope
InRequestScope
http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
You probably want a singleton (single instance of the game):
k.Bind<IGame>().To<Game>().InSingletonScope().Named("A")
.WithConstructorArgument("ColorChoiceCount", 12);