Dynamics AX Blog - Microsoft Dynamics AX (Axapta) - Seite 36

Momentan angezeigt werden nur Beiträge der Kategorie »Microsoft Dynamics AX (Axapta)« Filter entfernen

In den letzten Jahren, in denen ich mich fast hauptsächlich mit der Entwicklung im Umfeld von Microsoft Dynamics AX (vormals Axapta) beschäftigt habe, ist das eine oder andere Code-Fragment entstanden, von dem ich mir vorstellen könnte, daß es auch für andere AX-Entwickler ganz nützlich sein könnte. Aber auch Tips und Tricks zu dem mächtigen ERP-System werde ich in dieser Kategorie präsentieren.

RSS-Feed dieser Kategorie

Tabellen mit doppelten RecIDs ermitteln

Wenn man in Dynamics AX (4.0 bzw. 2009) einmal in die Verlegenheit kommen sollte, prüfen zu müssen, ob es Tabellen mit doppelten RecIDs gibt, kann folgendes SQL-Script dabei behilflich sein:

use ax2009_standard_sp1

set nocount on

declare @tableName char(100)
declare db_cursor CURSOR FOR 
 select name from dbo.sysobjects
 where xtype = 'U'
 and name <> 'ROWSTAT'
 and name <> 'ROWSTATUS'
 and name <> 'SQLSYSTEMVARIABLES'
 and name <> 'SYSTEMSEQUENCES' -- SYSTEMSEQUENCES enthält immer doppelte RecIDs
 order by name asc

-- Temporäre Tabelle aufbauen
IF OBJECT_ID(N'tempdb..#tmp_duplrecid', N'U') IS NOT NULL
 begin
 drop table #tmp_duplrecid
 end

create table #tmp_duplrecid (tablename char(100), recordcounter bigint)   

OPEN db_cursor  
FETCH NEXT FROM db_cursor INTO @tableName  

WHILE @@FETCH_STATUS = 0  
 BEGIN  
  -- Tabellen mit doppelten RecIds ermitteln
  exec('insert into #tmp_duplrecid select ''' + @tableName + ''', COUNT(*)' +
    ' from ' + @tableName +
    ' group by RECID ' +
    ' having COUNT(*) > 1')
       
  FETCH NEXT FROM db_cursor INTO @tableName  
 END  

CLOSE db_cursor  
DEALLOCATE db_cursor

set nocount off

-- Tabellen mit doppelten RecIDs
select 'Tabelle enthält doppelte RecIDs: ' + tablename
from #tmp_duplrecid


 


 
 

Report auf Basis eines Query erstellen

Die Feldgruppe AutoReport einer Tabelle und deren Verwendung beim Drucken von Daten aus einem Formular heraus sollte wohl jedem Dynamics AX-Entwickler bekannt sein. Auf Basis dieser Standard-Funktionalität habe ich versucht, selbst einen Report per X++ auf Basis eines Query zu erstellen.

static void createReportFromQuery(Args _args)
{
    query                   query;
    report                  report;
    reportDesign            reportDesign;
    reportRun               reportRun;
    reportSection           reportSection;
    sysReportRun            sysReportRun;
    reportName              reportTemplateName = 'FrontPage';
    reportAutoDesignSpecs   reportAutoDesignSpecs;
    int                     ds;
    int                     f;
    printJobSettings        printJobSettings = new printJobSettings();
    ;

    // Build query
    query = new query();
    query.addDataSource(tableNum(vendTable));

    query.dataSourceTable(tableNum(vendTable)).addRange(fieldNum(vendTable, vendGroup)).value('10');

    // Add Fieldlist
    query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, accountNum));
    query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, name));
    query.dataSourceTable(tableNum(vendTable)).addSelectionField(fieldNum(vendTable, vendgroup));

    // Build printJobSettings
    printJobSettings.setTarget(PrintMedium::Screen);
    printJobSettings.format(PrintFormat::PDF);
    printJobSettings.fileName(@"C:\Temp\TempReportFromQuery.pdf");

    // Create report
    if( hasSecurityKeyAccess(securityKeyNum(SysDevelopment),
        AccessType::Edit))
    {
        report = new report();

        sysReportRun        = classfactory.reportRunClass(new Args(reportstr(SysReportAuto)));
        sysReportRun.init();

        sysReportRun.query(query);
        if (!sysReportRun.queryRun())
        {
            sysReportRun.queryRun(new SysQueryRun(query));
        }

        sysReportRun.queryRun().query().interactive(false);
        Report.interactive(!printJobSettings);            

        Report.query(sysReportRun.queryRun().query());

        reportDesign = report.addDesign();
        reportDesign.caption("TmpReportFromQuery");
        reportDesign.reportTemplate(reportTemplateName);
        reportDesign.orientation(printerOrientation::Auto);

        reportAutoDesignSpecs = reportDesign.autoDesignSpecs();

        // Add body
        for(ds = 1;ds <= sysReportRun.queryRun().query().dataSourceCount(); ds++)
        {
            reportSection = reportAutoDesignSpecs.addSection(reportBlockType::Body, sysReportRun.queryRun().query().dataSourceNo(ds).table());
            reportSection.arrangeMethod(arrangeMethod::Vertical);

            for(f=1;f<=sysReportRun.queryRun().query().dataSourceNo(ds).selectionCount();f++)
            {
                reportSection.addControl(sysReportRun.queryRun().query().dataSourceNo(ds).table(), sysReportRun.queryRun().query().dataSourceNo(ds).fields().field(f));
            }
        }

        // Run the report
        reportRun = new reportRun(report);
        reportRun.fetch();

        // Print the report
        if(printJobSettings)
        {
            reportRun.printJobSettings(printJobSettings.packPrintJobSettings());
        }
        reportRun.print();
    }
}

Das Ergebnis des obigen Codes ist ein sehr einfacher Report:


 
 

Arbeiten mit dem aufrufenden Objekt einer Form (Caller) II

Um in einem Objekt auf das aufrufende Objekte zuzugreifen, habe ich anhand von einigen Zeilen Code ja schon demonstriert (siehe hier und hier). Dabei hatte ich aber immer ausser Acht gelassen, daß das aufrufende Objekt ja auch eine Klasse sein kann.

Nun also hier ein entsprechendes Beispiel:

if (element.args() && 
    element.args().caller() && 
    classidget(element.args().caller()) == classnum(nameOfClass )) 
{
    nameOfClass = element.args().caller();
    someValue = nameOfClass.someMethod();
}

 
 

Praktisches Beispiel für die Verwendung des runBaseBatch-Frameworks

Vor einiger Zeit habe ich schon einmal einen Eintrag über das runBaseBatch-Framework geschrieben, damals ging's hauptsächlich um die Aufrufreihenfolge der einzelnen Methoden und wofür einige von diesen Methoden verwendet werden können bzw. verwendet werden sollten.

Nun habe ich mir basierend auf diesem Eintrag und vor allem aufgrund von Erfahrungswerten eine eigene Klasse namens tutorial_ClassWithQueryRun geschrieben, die einige oft benötigte Anforderungen vereinigt.

Diese Klasse vereint folgende Fähigkeiten:

  • Stapelfähigkeit
  • Arbeiten mit einem Query
  • Arbeiten mit den Nutzungsdaten
  • Schreiben einer Datei via textIo
  • Arbeiten mit Fortschrittsanzeigen (progress)

Screenshot


 
 

Beispiel für einen Query mit Gruppierung

Der nachstehende Code zeigt wie man einen Query mit Gruppierung, Sortierung und verschiedenen Filtern aufbaut und diesen mit Hilfe eines QueryRun abarbeitet.

static void tutorialQueryGroupBy(Args _args)
{
    Query                query;
    queryBuildDataSource queryBuildDataSource;
    queryBuildRange      queryBuildRangeInvoiceDate;
    queryrun             queryrun;
    Salesline            Salesline;
    ;

    query = new query();

    queryBuildDataSource = query.addDataSource(tableNum(Salesline));

    // Add group by-clause
    queryBuildDataSource.addGroupByField(fieldNum(Salesline, custAccount));
    queryBuildDataSource.addGroupByField(fieldNum(Salesline, currencyCode));

    // Add fields to select-clause
    queryBuildDataSource.addSelectionField(fieldNum(Salesline, custAccount),        SelectionField::Database);
    queryBuildDataSource.addSelectionField(fieldNum(Salesline, currencyCode),       SelectionField::Database);
    queryBuildDataSource.addSelectionField(fieldNum(Salesline, lineAmount),         SelectionField::Sum);
    queryBuildDataSource.addSelectionField(fieldNum(Salesline, createdDateTime),    SelectionField::Min);

    // Add sort by-clause
    queryBuildDataSource.addSortField(fieldNum(Salesline, custAccount), SortOrder::Ascending);

    // Add where-clause
    sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, DeliveryCountryRegionId)).value("AT, US");                                            
    sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, createdDateTime)).value(SysQueryRangeUtil::lessThanDate(-365));                       
    sysQuery::findOrCreateRange(queryBuildDataSource, fieldNum(Salesline, salesStatus)).value(strFmt("%1, %2", SalesStatus::Invoiced, SalesStatus::Delivered));

    // Represents Select
    // SELECT FIRSTFAST CustAccount, CurrencyCode, SUM(LineAmount), MIN(createdDateTime)
    // FROM SalesLine
    // GROUP BY SalesLine.CustAccount, SalesLine.CurrencyCode
    // ORDER BY SalesLine.CustAccount ASC
    // WHERE ((DeliveryCountryRegionId = N'AT' OR DeliveryCountryRegionId = N'US'))
    //   AND ((createdDateTime<'2009-07-06T22:00:00'))
    //   AND ((SalesStatus = 3 OR SalesStatus = 2))


    queryrun = new SysQueryRun(query);
    while(queryRun.next())
    {
        Salesline = queryRun.get(tableNum(SalesLine));

        info(strFmt("%1: %2 %3 (%4)", salesLine.CustAccount, 
                                      salesLine.LineAmount, 
                                      salesLine.CurrencyCode, 
                                      salesLine.createdDateTime));
    }
}

 
 

Clientseitige Mini-Stapelverarbeitung über die Methode setTimeOut

Vor kurzem stand ich vor der Herausforderung, in einem Formular alle x Sekunden etwas tun zu müssen (Beispielsweise sollen alle 2 Sekunden der Inhalt einer angezeigten Datenquelle/Tabelle aktualisiert werden).

Lösen kann man so etwas mit Hilfe der Methode setTimeOut, die z.B. auch von der clientseitigen Stapelverarbeitung unter Grundeinstellungen > Periodisch > Stapel > Bearbeitung verwendet wird.

Dazu muss z.B. im Formular eine Methode angelegt werden, die sich selbst über die setTimeOut-Methode nach einer definierten Zeitspanne (in tausendstel-Sekunden anzugeben) aufruft:

void runEvery2Seconds()
{
    // Set a Time Out with the idle flag set to false
    this.setTimeOut(funcname(), 2000, false);

    // ...do something...
    dataSourceName_ds.executeQuery();
}

 
 

SELECT-Statements mit Datumseinschränkung in AX 2009

Im Zuge eines Upgrades einer Applikation auf Dynamics AX 2009 ist mir ein Stückchen Code über den Weg gelaufen, dessen Code-Upgrade auf den ersten Blick recht einfach sein sollte, sich aber bei genauerer Betrachtung als doch gar nicht so einfach herausgestellt hat.

Und zwar gehts um ein einfaches SELECT-Statement, z.B. wie das folgende:

date myDate = str2date('07.05.2010', 123);

select count(recid)
from salesline
where salesline.createdDate == myDate;

Obiges Statement soll mir einfach eine Zahl der Auftragszeilen liefern, die an einem bestimmten Tag erstellt wurden.

In Dynamics AX 2009 wurde ja das Feld createdDate durch createdDateTime und einen vollständigen neuen Datentyp, der nun auch die Zeit mitspeichert, ersetzt.


 
 
Seiten « 1 ... 33 34 35 36 37 38 39 ... 52 » 

 

 
 
 
Beiträge des aktuellen Monats
April 2025
MoDiMiDoFrSaSo
 123456
78910111213
14151617181920
21222324252627
282930 
 
© 2006-2025 Heinz Schweda | Impressum | Kontakt | English version | Mobile Version
Diese Webseite verwendet Cookies, um Benutzern einen besseren Service anzubieten. Wenn Sie weiterhin auf der Seite bleiben, stimmen Sie der Verwendung von Cookies zu.  Mehr dazu