How to refresh Factbox - axapta

I have a form, when i click on my button.It adds to my table A (what my factbox shows)is it possible to refresh the factbox with X++ code? I can't figure out how to refresh my infopart or query which factbox uses.

For an infopart you can call an update of the data source of the infopart's form run:
void clicked()
{
PartList partList;
int i;
FormRun infoPartFormRun;
FormDataSource infoPartDataSource;
super();
partList = new PartList(element);
for (i = 1; i <= partList.partCount(); i++)
{
infoPartFormRun = partList.getPartById(i);
if (infoPartFormRun.name() == identifierStr(MyInfoPart))
{
infoPartDataSource = infoPartFormRun.dataSource();
if (infoPartDataSource)
{
infoPartDataSource.research();
}
}
}
}
I added the check for the infoPartDataSource because I first tested this with a cue group fact box, which does not have a data source (or at least I could not figure out how to get the data source of one of the cues in the cue group and since you asked for an infopart fact box, I did not investigate further).
Update: The issue seems to be popular at the moment, Martin DrĂ¡b also wrote in his blog about it: Refreshing form parts

Related

Multiple OnSaving event in DevExpress XAF

Im working on a piece of code using DevExpress XAF, I noticed that if im using the event OnSaving that the code executes 2 times, how can i prevent that
protected override void OnSaving()
{
if (PrestamoP != null)
{
PrestamoP.Prestado -= Monto;
PrestamoP.Save();
}
else if (PrestamoG != null)
{
PrestamoG.Prestado -= Monto;
PrestamoG.Save();
}
base.OnSaving();
}
XPO does not guarantee that the OnSaving method is called once. See the corresponding note in the XPO Best Practices article.
I can see that you are changing the PrestamoP.Prestado property based on the value of the Monto property. This code is fine if you execute it only once and only when the Monto property is specified for the first time. This code is not fine if you:
Save this object without changing the Monto property;
Update the early specified Monto value.
So, it appears that a more complex logic is required for the PrestamoG.Prestado property. First, I would move it to the Monto property setter and take the previous value into account (do not forget to check the IsLoading property in this case). Second, I would consider calculating the Prestado value dynamically instead of storing its value. This will allow you to resolve issues with the duplicate business logic execution. See an example here: How to: Calculate a Property Value Based on Values from a Detail Collection.
I can offer different methods for CRUD functions on onSaving method.
IsNewObject, IsDeleted.
// insert
if (Session.IsNewObject(this))
{
a = new a(Session);
a.CreateReferencedProperties(this);
}
// delete
else if (IsDeleted)
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.Delete();
}
// update
else
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.CreateReferencedProperties(this);
}
You can use the code below to prevent xaf from entering on saving twice.
base.OnSaving();
SessionObjectLayer sessionObjectLayer = this.Session.ObjectLayer as SessionObjectLayer;
if (sessionObjectLayer == null || sessionObjectLayer.ParentSession == null)
{
//Enter only once
}

Dynamics AX - Adding tables to DatabaseLog programatically in AX 2009

I'm looking for a way to enable logging changes for certain tables.
I have tried and tested adding tables to database log programatically, but with various success so far - sometimes it works sometimes it doesn't (mostly it does not) - it seems simply inserting rows into DatabaseLog table doesn't quite do the trick.
What I have tried:
Adding row with proper tableId, fieldId, logType and . Domain had been assigned as 'Admin', main company, empty field and subcompanies with the same result.
I have created class that handles inserts, main two functions are:
public static void InsertBase(STR tableName, domainId _domain='Admin')
{
//base logging for insert, delete, uptade on fieldid=0
DatabaseLog DBDict;
TableId _tableId;
DatabaseLogType _logType;
fieldId _fieldId =0;
List logTypes;
int i;
ListEnumerator enumerator;
;
_tableId= tableName2id(tableName);
logTypes = new List(Types::Enum);
logTypes.addEnd(DatabaseLogType::Insert);
logTypes.addEnd(DatabaseLogType::Update);
logTypes.addEnd(DatabaseLogType::Delete);
logTypes.addEnd(DatabaseLogType::EventInsert);
logTypes.addEnd(DatabaseLogType::EventUpdate);
logTypes.addEnd(DatabaseLogType::EventDelete);
enumerator = logTypes.getEnumerator();
while(enumerator.moveNext())
{
_logType = enumerator.current();
select * from dbdict where
dbdict.logTable==_tableId && dbdict.logField==_fieldId
&& dbdict.logType==_logType;
if(!dbDict) //that means it doesnt exist
{
dbdict.logTable=_tableId;
dbdict.logField=_fieldId;
dbdict.logType=_logType;
dbdict.domainId=_domain;
dbdict.insert();
}
}
info("Success");
}
and the method that lists every single field and adds as logType::Update
public static void init(str TableName, DomainId domain='Admin')
{
DatabaseLogType logtype;
int i;
container kk, ll;
DatabaseLog dblog;
tableid _tableId;
fieldid _fieldid;
;
logtype = DatabaseLogType::Update;
//holds a container of not yet added table fields to databaselog
kk = BLX_AddTableToDatabaseLog::buildFieldList(logtype,TableName);
for(i=1; i <= conlen(kk);i++)
{
ll = conpeek(kk,i);
_tableid = tableName2id(tableName);
_fieldid = conpeek(ll,1);
info(strfmt("%1 %2", conpeek(ll,1),conpeek(ll,2)));
dblog.logType=logType;
dblog.logTable = _tableId;
dblog.domainId = domain;
dblog.logField =_fieldid;
dblog.insert();
}
}
result:
What am I missing ?
#EDIT with some additional info
Does not work for SalesTable and SalesLine, WMSBillOfLading.
I couldn't add log for SalesTable and SalesLine by using wizard in administration panel, but my colleague somehow did (she has done exactly the same things as me). We also tried to add log to various other tables and we often found out that she could while I could not and vice versa (and sometimes none managed to do it like in case of WMSBillOfLading table).
The inconsistency of this mechanism is what drove me to write this code, which I hoped would solve all the problems.
After doing your setup changes you probably have to call
SysFlushDatabaseLogSetup::main();
in order to flush any caches.
This method is also called in the standard AX code in the form method SysDatabaseLogTableSetup\Methods\close and in the class method SysDatabaseLogWizard\doRun.

How to identify advanced query or dynamic joins from query window?

In the query window that pops up, if a user right clicks and chooses "1:n" and selects a table, how can one detect and use that table? I have a good sample job and screenshots that should demonstrate what I'm trying to accomplish.
I wrote this sample job that dumps out the AOT query objects but not the dynamically joined table/range/value.
static void InventSumQuery(Args _args)
{
Query query = new Query(queryStr(InventDimPhys));
QueryRun qr = new QueryRun(query);
QueryBuildRange queryRange;
DictField dictField;
int i, n;
if(qr.prompt())
{
for (n=1; n<=query.dataSourceCount(); n++)
{
for (i=1; i<=query.dataSourceNo(n).rangeCount(); i++)
{
queryRange = query.dataSourceNo(n).range(i);
dictField = new dictField(query.dataSourceNo(n).table(), fieldName2id(query.dataSourceNo(n).table(), queryRange.AOTname()));
info(strFmt("%1.%2", tableId2name(dictField.tableid()), dictField.name()));
}
}
}
info("Done");
}
Of course I figure my own answer out. Query objects are static, and the query form actually just modifies the query when you make the change.
So you need to modify the code above to:
if(qr.prompt())
{
query = qr.query();
This gets the modified query. The advanced querying actually is just a function of the form itself that ultimately modifies the query.

Qt: two actions in menu (with the same text)

I create a menu dynamically. I add several checkable actions into one menu. Sometimes actions may have the same text that user sees. It's up to user (actually user adds commands into menu).
The problem is in this case clicking works wrong. If I click on the first action (from 2 with the same texts) everything is good but if I click on the second one, both actions are selected. I don't understand why. The code where actions have been created is here:
for (int i = 0; i< currentList.size(); i++)
{
QString lanKey = currentList.at(i)->Language->toString();
QAction* lanAction = new QAction(this);
QString name ="action_" + currentList.at(i)->Id->toString();
lanAction->setObjectName(name);
lanAction->setText(lanKey);
lanAction->setCheckable(true);
lanAction->setData(i);
connect(lanAction, SIGNAL(triggered(bool)), this, SLOT(ShowSomething(bool)));
ui->menuMy->addAction(lanAction);
}
Here, lanKey is language that may be the same for different actions. Anyway click on the specific action should lead only to checking of this action. What's wrong?
The slot is here:
void VMainWindow::ShowSomething(bool IsTriggered)
{
QAction* senderAction = (QAction*)sender();
int listIndex = senderAction->data().toInt();
if (IsTriggered)
{
CreateEditor(subtitles, listIndex);
}
else
{
//hide this editor
QString name = "editor" + editorsList->Id->toString();
QDockWidget* editorDock = this->findChild<QDockWidget*>(name);
if (editorDock != 0)
{
this->removeDockWidget(editorDock);
this->setLayout(layout());
}
}
}
Thanks
The source of problem is found: it turned out that the slot finds the checked action wrong - by text, not by id.
I can't find a logical issue in the code you posted so far. Here are a couple of options which I would try in order to resolve this problem:
Limit the users possibilities when adding items to a menu so that he can't add two items with the same name.
Add qDebug() output to ShowSomething to see if there is a problem with signals&slots. For example, if the slot gets called once for the first item but twice for the second item there is a problem there.
Debug into CreateEditor step-by-step.
As the problem seems to appear only for actions with a similar name, you should make sure that you never make a lookup of an action (or something related) by its text() but rather by its data() or objectName() (assuming that currentList.at(i)->Id will always be unique)

Is there any way to speed up crystal reports generation?

We are running a reporting web application that allows the user to select a few fields and a crystal report is generated based off of the fields selected. The SQL that is generated for the most complex report will return the data in < 5 seconds, however it takes the report and average of 3 minutes to run, sometimes longer causing a time out. We are running VS2010. The reports are basically set up out of the box with no real manipulations or computations being done, just displaying the data in a nice format. Is there anything we can try to speed it up, pre-loading a dummy report to load the dlls, some hack to make crystal run faster, anything?
EDIT: Code Added to show the databinding
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
string strFile = Server.MapPath(#"AwardStatus.rpt");
CrystalReportSource1.Report.FileName = strFile;
DataTable main = Main();
CrystalReportSource1.ReportDocument.SetDataSource(main);
CrystalReportViewer1.HasCrystalLogo = false;
CrystalReportSource1.ReportDocument.ExportToHttpResponse(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, Response, false, "pmperformance");
}
}
private DataTable Main()
{
Guid guidOffice = Office;
CMS.Model.ReportsTableAdapters.ViewACTableAdapter rptAdapter = new CMS.Model.ReportsTableAdapters.ViewACTableAdapter();
Reports.ViewAwardedContractsDataTable main = new Reports.ViewAwardedContractsDataTable();
if (Office == new Guid())
{
IEnumerable<DataRow> data = rptAdapter.GetData().Where(d => UserPermissions.HasAccessToOrg(d.guidFromId, AuthenticatedUser.PersonID)).Select(d => d);
foreach (var row in data)
{
main.ImportRow(row);
}
}
else if (guidOffice != new Guid())
{
main = rptAdapter.GetDataByOffice(guidOffice);
}
else
{
main = new Reports.ViewACDataTable();
}
return main;
}
private Guid Office
{
get
{
string strOffice = Request.QueryString["Office"];
Guid guidOffice = BaseControl.ParseGuid(strOffice);
if (!UserPermissions.HasAccessToOrg(guidOffice, AuthenticatedUser.PersonID))
{
return Guid.Empty;
}
else
{
return guidOffice;
}
}
}
protected void CrystalReportSource1_DataBinding(object sender, EventArgs e)
{
//TODO
}
This may be a bit flippant, but possibly consider not using crystal reports... We had a fair bit of trouble with them recently (out of memory errors being one), and we've moved off to other options and are quite happy...
Here's what I would do:
Put clocks from the time you get the field choices from the user, all the way to when you display the report. See where your processing time is going up.
When you look at the clocks, there can be various situations:
If Crystal Reports is taking time to fill the report, check how you're filling it. If you're linking the report fields directly to your data table, CR is probably taking time looking up the data. I suggest creating a new table (t_rpt) with dynamic columns (Field1, Field2,..FieldN) and pointing your report template to that table. I don't know if you're already doing this.
If it's taking time for you to lookup the data itself, I suggest creating a view of your table. Even though a memory hog, this will make your lookup quick and you can delete the view once you're done.
If it's none of the above, let us know what your clocks show.
In terms of loading any large amount of data, you'll always want to use a stored procedure.
Outside of that, you WILL see a delay in the report running the first time the Crystal DLLs load. Yes, you can preload them as you mentioned and that will help some.

Resources