How to find all tables using a defined EDT? - axapta

How does one create a job which finds all tables containing a particular extended data type?
I found this JOB, but it gives me an error: https://fredshen.wordpress.com/2006/02/05/find-out-tables-containing-specific-edt/

Use the Cross Reference Tool.
It will show code uses as well.

try this:
static void findEdtinTable(Args _args)
{
treeNode childNode;
treeNode fields;
treenodeIterator it, itFld;
str properties;
str table;
str field;
str extendedDataType;
str searchType = "PurchInternalInvoiceId"; // EDT
int x;
treeNode t = TreeNode::findNode('\\Data Dictionary\\Tables');
it = t.AOTiterator();
childNode= it.next();
while (childNode)
{
Table = childNode.treeNodeName();
itFld = t.AOTfindChild(childNode.treeNodeName()).AOTfindChild("Fields").AOTiterator();
fields = itFld.next();
while (fields)
{
field = fields.treeNodeName();
properties = fields.AOTgetProperties();
extendedDataType = findProperty(properties, "ExtendedDataType");
if (extendedDataType == searchType)
{
info(strfmt("%1 / %2 – ExtendedDataType: %3", table, field, extendedDataType));
}
fields = itFld.next();
}
childNode= it.next();
}
}

Related

Loop through grid columns in runtime in AX 2012

I want to loop through columns(fields) in grid control in AX 2012 form. I have this logic to loop through fields :
static void Job1(Args _args)
{
SysDictTable dictTable = new SysDictTable(tableNum(PurchLine));
SysDictField dictField;
TreeNode treeNode;
FieldId fieldId = dictTable.fieldNext(0);
while (fieldId)
{
dictField = dictTable.fieldObject(fieldId);
if (dictField.isSql() && !dictField.isSystem())
{
treeNode = dictField.treeNode();
info(strFmt("%1 | %2 | %3",
dictField.name(), // Field name
treeNode.AOTgetProperty("Label"), // Label id
SysLabel::labelId2String(treeNode.AOTgetProperty("Label")))); // Label string
}
fieldId = dictTable.fieldNext(fieldId);
}
}
This loop through table, I need similar logic to loop through grid fields but in RUNTIME.
For example if grid data source has 5 columns and 3 are removed in UI, i want to loop just through these 2 that are left in the grid.
So my question is how to get grid fields in runtime( not from datasource table) ?
The below assumes your grid is named "GridOverview". You call it like:
::outputGridControls(element) where element is your form.
public static void outputGridControls(FormRun _formRun)
{
int i;
FormGridControl formGridControl;
FormControl formControl;
formGridControl = _formRun.control(_formRun.controlId(identifierStr(GridOverview)));
if (formGridControl)
{
for (i=1; i<=formGridControl.controlCount(); i++)
{
formControl = formGridControl.controlNum(i);
if (formControl && formControl.visible())
{
info(strFmt("%1 (%2)", formControl.name(), formControl.labelText()));
}
}
}
}
What you're doing above is reflecting on the AOT table, but you need to be looking at the FormRun and not the actual table or even the DataSource.

How can i get columns added on column?

heres my code below...
TableColumn tc = new TableColumn();
TableColumn[] tc2 = new TableColumn[10];
for(int i=0; i<5, i++){
tc.getColumns().add(tc2[i]);
}
and i try to override commit method for editing cells.
public void commit(Object val) {
// Get the table
TableView<MainTable> t = this.getTableView();
// Get the selected row/column
MainTable selectedRow = t.getItems().get(this.getTableRow().getIndex());
TableColumn<MainTable, ?> selectedColumn = t.getColumns().get(t.getColumns().indexOf(this.getTableColumn()));
// Get current property name
String propertyName = ((PropertyValueFactory) selectedColumn.getCellValueFactory()).getProperty();
// Create a method name conforming to java standards ( setProperty )
propertyName = ("" + propertyName.charAt(0)).toUpperCase() + propertyName.substring(1);
// Try to run the update
try {
// Type specific checks - could be done inside each setProperty() method
if(val instanceof Double) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, double.class);
method.invoke(selectedRow, (double) val);
}
if(val instanceof String) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, String.class);
method.invoke(selectedRow, (String) val);
}
if(val instanceof Integer) {
Method method = selectedRow.getClass().getMethod("set" + propertyName, int.class);
method.invoke(selectedRow, (int) val);
}
} catch (Exception e) {
e.printStackTrace();
}
// CommitEdit for good luck
commitEdit((String) val);
}
and i got ArrayIndexOutofBoundsException on console view.
so my question is
how can i select getcolumns added other column???
TableColumn<MainTable, ?> selectedColumn = t.getColumns().get(t.getColumns().indexOf(this.getTableColumn()));
i think this code has to be changed...
anyone got ideas??
Nested columns are not part of the TableView.columns list.
If you need the corresponding TableView column, just go up through the hierarchy until you reach a column without a parentColumn:
TableColumn<MainTable, ?> selectedColumn = this.getTableColumn();
TableColumn<MainTable, ?> c = selectedColumn;
while ((c = selectedColumn.getParentColumn()) != null) {
selectedColumn = c;
}
If you just need the column itself, simply use this.getTableColumn(), instead of finding the index of the column in the columns list and then accessing that index in the same list. (I guess the latter is what you need.)
Furthermore, if PropertyValueFactory returns properties of the item class, you could use this property to set the value instead of using reflection:
ObservableValue obs = selectedColumn.getCellObservableValue(this.getIndex());
if (obs instanceof WritableValue) {
((WritableValue) obs).setValue(val);
} else {
// reflecitive approach
}
Furthermore you shouldn't add null as a nested column, but you're doing it here:
TableColumn[] tc2 = new TableColumn[10];
for(int i=0; i<5, i++){
tc.getColumns().add(tc2[i]);
}

How to retrieve attributes from a classDeclaration with reflection

I want to create a custom attribute that will be applied to classDeclarations. I can enumerate attributes from other methods on the class, but not the classDeclaration itself because it is some sort of special method.
I know it is possible though because SysObsoleteAttribute (called from the kernel) is placed in classDeclarations all over.
In Classes\CustCustomerService\create I just copied the attributes to Classes\CustCustomerService\classDeclaration at the top for this test.
[AifDocumentCreateAttribute, SysEntryPointAttribute(true)]
class CustCustomerService extends AifDocumentService
{
}
I created a static method on a new class:
static public void AttribsOfSysEntryPointAttributeOnMethod
(
str _sNameOfClass,
str _sNameOfMethod,
str _nameOfAttribute
)
{
int nClassId;
SysDictMethod sdm;
Object attributeAsObject;
SysDictClass sysDictClass;
Array attribArray = new Array(Types::Class);
int i;
nClassId = Global::className2Id(_sNameOfClass);
sysDictClass = new SysDictClass(nClassId);
sdm = new SysDictMethod(UtilElementType::ClassInstanceMethod, nClassId, _sNameOfMethod);
attribArray = sdm.getAllAttributes();
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
{
// Unable to get attributes, try another way
error(strFmt("Unable to retrieve attribute array for method %1", sdm.name()));
// It is, so let's try and enumerate ALL attributes and output them directly from class dec
sdm = sysDictClass.objectMethodObject(1);
if (attribArray)
{
for (i=1; i<=attribArray.lastIndex(); i++)
{
attributeAsObject = attribArray.value(i);
info(strFmt("[%3] Attrib Class Id: %1 [%2]", classIdGet(attributeAsObject), classId2Name(classIdGet(attributeAsObject)), _sNameOfMethod));
}
}
else
error(strFmt("Still unable to retrieve attribute array for method %1", sysDictClass.objectMethod(1)));
}
}
Then created a job to call it, and you can see how it works for one method, but not the other.
static void Job5(Args _args)
{
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "create", "SysEntryPointAttribute");
AttributeReflection::AttribsOfSysEntryPointAttributeOnMethod("CustCustomerService", "classDeclaration", "SysEntryPointAttribute");
}
Any ideas how to enumerate Attributes from the classDeclaration??
The classDeclaration is not a method and cannot be called. Hence your sysDictClass variable is null.
Googling reveals that the getAllAttributes method exits on DictClass:
attribArray = sdm ? sdm.getAllAttributes() : sysDictClass.getAllAttributes();

LINQ dynamic property in select

// Hi everyone
i do this call in Action :
[HttpGet]
public virtual ActionResult JsonGetProvinces(int countryId)
{
//WebSiteContext WbContext = new WebSiteContext();
//UnitOfWork UnitofWork = new UnitOfWork(WbContext);
var provinces =
(
from province in unitofWork.ProvinceRepository.All
where province.CountryId == countryId
select new
{
Id = province.Id,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
}
).ToList();
return Json(provinces, JsonRequestBehavior.AllowGet);
}
something is wrong with my query :
var provinces =
(
from province in unitofWork.ProvinceRepository.All
where province.CountryId == countryId
select new
{
Id = province.Id,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
}
).ToList();
Particulary,
Name = province.GetType().GetProperty("Name_" + CultureManager.GetCurrentCultureShortName()).GetValue(province)
In BDD, there is Name_fr, Name_en columns
and i'm trying to take one dynamically... Is it possible ?
Of course, i can take both and choose dynamically the column in View but i would to know how do...
Thank you for your help
The short answer is you need to change your code a bit and using expression tree inside. Look at this question
EF can not translate function calls to SQL. Using expression trees can be comlicated see this question
Here is a sample with expression trees. The GetQuery2 is the same as GetQuery but with expression tree and a propertyname parameter.
public static IQueryable<Foo> GetQuery(BlogContext context)
{
var query = from x in context.BlogEntries
select new Foo
{
NameX = x.Name
};
return query;
}
public static IQueryable<Foo> GetQuery2(BlogContext context, string propertyName)
{
ConstructorInfo ci = typeof(Foo).GetConstructor(new Type[0]);
MethodInfo miFooGetName = typeof(Foo).GetMethod("set_NameX");
MethodInfo miBlogEntry = typeof(BlogEntry).GetMethod("get_" + propertyName);
ParameterExpression param = Expression.Parameter(typeof(BlogEntry), "x");
IQueryable<Foo> result = Queryable.Select<BlogEntry, Foo>(
context.BlogEntries,
Expression.Lambda<Func<BlogEntry, Foo>>(
Expression.MemberInit(
Expression.New(ci, new Expression[0]),
new MemberBinding[]{
Expression.Bind(miFooGetName,
Expression.Property(param,
miBlogEntry))}
),
param
)
);
return result;
}
It is easier the fetch all all language strings and write an additional Property Name that does the magic.

X++ passing current selected records in a form for your report

I am trying to make this question sound as clear as possible.
Basically, I have created a report, and it now exists as a menuitem button so that the report can run off the form.
What I would like to do, is be able to multi-select records, then when I click on my button to run my report, the current selected records are passed into the dialog form (filter screen) that appears.
I have tried to do this using the same methods as with the SaleLinesEdit form, but had no success.
If anyone could point me in the right direction I would greatly appreciate it.
Take a look at Axaptapedia passing values between forms. This should help you. You will probably have to modify your report to use a form for the dialog rather than using the base dialog methods of the report Here is a good place to start with that!
Just wanted to add this
You can use the MuliSelectionHelper class to do this very simply:
MultiSelectionHelper selection = MultiSelectionHelper::createFromCaller(_args.caller());
MyTable myTable = selection.getFirst();
while (myTable)
{
//do something
myTable = selection.getNext();
}
Here is the resolution I used for this issue;
Two methods on the report so that when fields are multi-selected on forms, the values are passed to the filter dialog;
private void setQueryRange(Common _common)
{
FormDataSource fds;
LogisticsControlTable logisticsTable;
QueryBuildDataSource qbdsLogisticsTable;
QueryBuildRange qbrLogisticsId;
str rangeLogId;
set logIdSet = new Set(Types::String);
str addRange(str _range, str _value, QueryBuildDataSource _qbds, int _fieldNum, Set _set = null)
{
str ret = _range;
QueryBuildRange qbr;
;
if(_set && _set.in(_Value))
{
return ret;
}
if(strLen(ret) + strLen(_value) + 1 > 255)
{
qbr = _qbds.addRange(_fieldNum);
qbr.value(ret);
ret = '';
}
if(ret)
{
ret += ',';
}
if(_set)
{
_set.add(_value);
}
ret += _value;
return ret;
}
;
switch(_common.TableId)
{
case tableNum(LogisticsControlTable):
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrLogisticsId = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, LogisticsId));
fds = _common.dataSource();
for(logisticsTable = fds.getFirst(true) ? fds.getFirst(true) : _common;
logisticsTable;
logisticsTable = fds.getNext())
{
rangeLogId = addrange(rangeLogId, logisticsTable.LogisticsId, qbdsLogisticsTable, fieldNum(LogisticsControlTable, LogisticsId),logIdSet);
}
qbrLogisticsId.value(rangeLogId);
break;
}
}
// This set the query and gets the values passing them to the range i.e. "SO0001, SO0002, SO000£...
The second methods is as follows;
private void setQueryEnableDS()
{
Query queryLocal = element.query();
;
}
Also on the init method this is required;
public void init()
{
;
super();
if(element.args() && element.args().dataset())
{
this.setQueryRange(element.args().record());
}
}
Hope this helps in the future for anyone else who has the issue I had.

Resources