Passing parameters to parameter dialog for Query-based reports - axapta

I have a query-based report in which the query has some interactive ranges enabled on them. This is great except the value is blank, or has the last values used pre-populated. One of these is Vendor account number. If I wanted to have this report to pre-populate the Vend account based on whichever Vendor account record is selected (the caller), how would I be able to achieve this?

The answer was easy, although hard to find. I wasn't aware that you could access query objects from within a controller. The solution is to create a Controller class with only a main() method defined as normal, and the prePromptModifyContract method overridden. The following code will solve the problem:
SomeTable someTable;
Query query;
super();
if (this.parmArgs() && this.parmArgs().dataset() == tableNum(SomeTable))
{
someTable = this.parmArgs().record();
query = this.getFirstQuery();
SysQuery::findOrCreateRange(query.dataSourceTable(tableNum(SomeOtherTable)), fieldNum(SomeOtherTable, SomeOtherField)).value(SysQuery::value(someTable.SomeField));
}

I haven't tried this, but you could override the query's init method and call to element.args().record() as in a Form.
Something like this:
public void init()
{
VendTable vendTable;
super();
if (element.args().dataset() == tableNum(VendTable))
{
vendTable = element.args().record();
//populate your ranges with vendTable
}
}
I hope it works!

Related

initialize value of edit method in init method of form

I want to initialize a value of an edit method inside the init method of form, i wrote this:
[Form]
public class foo extends FormRun
{
str paymTermId;
public void init()
{
CustTable custTable = CustTable::find("DE-001");
paymTermId = custTable.paymTermId;
super();
}
edit str edtpaymTermId(boolean set, str _paymTermId)
{
if (set)
{
paymTermId= _paymTermId;
}
return paymTermId ;
}
}
But when i open the form the control remains empty.
any suggestions?
I tried to reproduce the issue, but was not successful. For me, when opening the form, the control shows a value.
A possible reason why it is not working for you could be that you open the form in the wrong company. In your code, you retrieve the value to display in the control from the payment term of customer DE-001. This customer exists in company USMF in the Contoso demo data and has payment term Net10. If the form is opened in this company, the value is shown in the control. If you are in another company (e.g. DAT), no value is shown.
I see 2 things that are wrong:
You are setting the value BEFORE super(). It should be after.
You SHOULDN'T initialize the value via field, you should do it calling edit method. Edit methods have a boolean SET parameter which can simulate a call for setting a value.

EF Core Update with List

To make updates to a record of SQL Server using Entity Framework Core, I query the record I need to update, make changes to the object and then call .SaveChanges(). This works nice and clean.
For example:
var emp = _context.Employee.FirstOrDefault(item => item.IdEmployee == Data.IdEmployee);
emp.IdPosition = Data.IdPosition;
await _context.SaveChangesAsync();
But is there a standard method if I want to update multiple records?
My first approach was using a list passing it to the controller, but then I would need to go through that list and save changes every time, never really finished this option as I regarded it as not optimal.
For now what I do is instead of passing a list to the controller, I pass each object to the controller using a for. (kind of the same...)
for(int i = 0; i < ObjectList.Count; i ++)
{
/* Some code */
var httpResponseObject = await MyRepositories.Post<Object>(url+"/Controller", Object);
}
And then do the same thing on the controller as before, when updating only one record, for each of the records...
I don't feel this is the best possible approach, but I haven't found another way, yet.
What would be the optimal way of doing this?
Your question has nothing to do with Blazor... However, I'm not sure I understand what is the issue. When you call the SaveChangesAsync method, all changes in your context are committed to the database. You don't have to pass one object at a time...You can pass a list of objects
Hope this helps...
Updating records in bulk using Entity Framework or other Object Relational Mapping (ORM) libraries is a common challenge because they will run an UPDATE command for every record. You could try using Entity Framework Plus, which is an extension to do bulk updates.
If updating multiple records with a single call is critical for you, I would recommend just writing a stored procedure and call if from your service. Entity Framework can also run direct queries and stored procedures.
It looks like the user makes some changes and then a save action needs to persist multiple records at the same time. You could trigger multiple AJAX calls—or, if you need, just one.
What I would do is create an endpoint—with an API controller and an action—that's specific to your needs. For example, to update the position of records in a table:
Controller:
/DataOrder
Action:
[HttpPut]
public async void Update([FromBody] DataChanges changes)
{
foreach(var change in changes)
{
var dbRecord = _context.Employees.Find(change.RecordId);
dbRecord.IdPosition = change.Position;
}
_context.SaveChanges();
}
public class DataChanges
{
public List<DataChange> Items {get;set;}
public DataChangesWrapper()
{
Items = new List<DataChange>();
}
}
public class DataChange
{
public int RecordId {get;set;}
public int Position {get;set;}
}
The foreach statement will execute an UPDATE for every record. If you want a single database call, however, you can write a SQL query or have a stored procedure in the database and pass the data as a DataTable parameter instead.

Table Update Event Handler

I am investigating the capabilities of the new delegate & event subscription pattern in AX 2012.
At the moment I am looking to detect when a particular field has been modified, for example when SalesTable.SalesStatus is changed to SalesStatus::Invoiced.
I have created the following post-event handler and attatched to the SalesTable.Update method;
public static void SalesTable_UpdatePosteventHandler(XppPrePostArgs _args)
{
Info("Sales Update Event Handler");
}
Now I know I can get the SalesTable from the _args, but how can I detect a field has changed? I could really use a before & after version, which makes me think I am subscribing to the wrong event here.
If the update method does not update the field, you can use a pre event handler on the update method. If you want to monitor the PriceGroup field on the CustTable table then create a class called CustTableEventHandler containing this method:
public static void preUpdateHandler(XppPrePostArgs _args)
{
CustTable custTable = _args.getThis();
if (custTable.PriceGroup != custTable.orig().PriceGroup)
info(strFmt("Change price group from '%1' to '%2'", custTable.orig().PriceGroup, custTable.PriceGroup));
}
A post event handler will not work, as orig() will return the changed record.
Also if the the record is updated using doUpdate your handler is not called.
You could also override the aosValidateUpdate on CustTable, which is called even if doUpdate is used. This method is always run on the AOS server.
public boolean aosValidateUpdate()
{
boolean ret = super();
if (this.PriceGroup != this.orig().PriceGroup)
info(strFmt("Change price group from '%1' to '%2'", this.orig().PriceGroup, this.PriceGroup));
return ret;
}
Yet another option would be a global change to the Application.eventUpdate method.
From the header of the method:
Serves as a callback that is called by the kernel when a record in a
table is updated, provided that the kernel has been set up to monitor
records in that table.
A developer can set up the kernel to call back on updates for a given
table by inserting a record into the DatabaseLog kernel table with all
fields set to relevant values, which includes the field logType set to
EventUpdate. It is possible to set up that the kernel should call back
whenever a record is updated or when a specific field is updated.This
is very similar to how logUpdate is called and set up. The call
of this method will be in the transaction in which the record is
updated.
This method is used by the alert rule notification system. I would recommend against this, unless it is a global change (like alert rules).
Alert rules can be extended as described here.

How to determine which field has been updated?

What's the correct way to determine which field has been changed in update() method on a table?
I know it's possible in modifiedField() via fieldId but that's too early.
The method you are looking for is Orig()
Look in the update() method of BOMTable or BankAccountTrans
The table has a method called orig that exposes the values of the last saved state of the current record.
a good example and description can be found here
http://msdax.blogspot.co.uk/2007/07/programming-of-basic-methods-of-tables.html
void update () {
CustTable this_Orig = this.orig ();
;
if (this_Orig.custGroup! = this.custGroup)
{
//Cust group is changing on this update
}
...

Dynamic list constraint on Alfresco

I'm trying to follow the examples provided in this post, to create a dynamic list constraint in Alfresco 3.3.
So, I've created my own class extending ListOfValuesConstraint:
public class MyConstraint extends ListOfValuesConstraint {
private static ServiceRegistry registry;
#Override
public void initialize() {
loadData();
}
#Override
public List getAllowedValues() {
//loadData();
return super.getAllowedValues();
}
#Override
public void setAllowedValues(List allowedValues) {
}
protected void loadData() {
List<String> values = new LinkedList<String>();
String query = "+TYPE:\"cm:category\" +#cm\\:description:\"" + tipo + "\"";
StoreRef storeRef = new StoreRef("workspace://SpacesStore");
ResultSet resultSet = registry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, query);
// ... values.add(data obtained using searchService and nodeService) ...
if (values.isEmpty()) {
values.add("-");
}
super.setAllowedValues(values);
}
}
ServiceRegistry reference is injected by Spring, and it's working fine. If I only call loadData() from initialize(), it executes the Lucene query, gets the data, and the dropdown displays it correctly. Only that it's not dynamic: data doesn't get refreshed unless I restart the Alfresco server.
getAllowedValues() is called each time the UI has to display a property having this constraint. The idea on the referred post is to call loadData() from getAllowedValues() too, so the values will be actually dynamic. But when I do this, I don't get any data. The Lucene query is the same, but returns 0 results, so my dropdown only displays -.
BTW, the query I'm doing is: +TYPE:"cm:category" +#cm\:description:"something here", and it's the same on each case. It works from initialize, but doesn't from getAllowedValues.
Any ideas on why is this happening, or how can I solve it?
Thanks
Edit: we upgraded to Alfresco 3.3.0g Community yesterday, but we're still having the same issues.
This dynamic-list-of-values-constraint is a bad idea and I tell you why:
The Alfresco repository should be in a valid state all the time. Your (dynamic) list of constraints will change (that's why you want it to be dynamic). Adding items would not be a problem, but editing and removing items are. If you would remove an item from your option-list, the nodes in the repository with this property value will be invalid.
You will not be able to fix this easily. The standard UI will fail on invalid-state-nodes. Simply editing this value and setting it to something valid will not work. You have been warned.
Because the default UI widget for a ListConstraint is a dropdown, not every dropdown should be a ListConstraint. ListConstraints are designed for something like a Status property: { Draft, Waiting Approval, Approved }. Not for a list of customer-names.
I have seen this same topic come up again and again over the last few years. What you actually want is let the user choose a value from a dynamic list of options (combo box). This is a UI problem, not a dictionary-model-issue. You should setup something like this with the web-config-context.xml (Alfresco web UI) or in Alfresco Share. The last one is more flexible and I would recommend taking that path.

Resources