Willkommen auf schweda.net
 
 
 
 
 

Als Entwickler muss ich Dynamics AX immer wieder mal mit einem anderen Windows-Login starten, als dem eigenen. Deshalb habe ich in Windows 7 die Option Als anderer Benutzer ausführen im Kontext-Menü schmerzlich vermisst und statt dessen  immer Batch-Dateien dafür verwendet.

Über einen Blog-Eintrag bin ich aber auf einen wertvollen Tip gestossen: Wenn man die [SHIFT]-Taste hält, während man über die rechte Maustaste das Kontext-Menü einer Applikation aufruft, steht die Option Als anderer Benutzer ausführen wieder zur Verfügung.

Als anderer Benutzer ausführen


Mit der Version Dynamics AX 2012 halten die SQL Reporting Services (SSRS) nun endgültig Einzug im Leben eines jeden AX-Entwicklers. Zeit also, sich mit diesem Thema etwas intensiver zu beschäftigen. Die Ergebnisse meiner ersten Gehversuche mit den SSRS präsentiere ich Euch nun in diesem Beitrag.

Szenario: Es soll ein Bericht erstellt werden, bei welchem der Umsatz pro Verkaufsgruppe ausgewertet werden kann.

 

Schritt 1: Erstellen eines Queries in Dynamics AX

Ich gehe davon aus, daß der geneigte Leser schon einmal einen Query erstellt hat, deshalb werde ich darauf nur sehr oberflächlich eingehen.

Basis-Tabellen meines Queries sind die Tabellen
  • CustInvoiceTrans
  • CustInvoiceJour
  • CommissionSalesGroup

Die Tabelle CustInvoiceTrans wird per InnerJoin mit der CustInvoiceJour über die im Screenshot gezeigten Felder verknüpft, die Tabelle CommissionSalesGroup wird per OuterJoin über das Feld SalesGroup verknüpft.

  Query

Seit AX 2012 muss die Eigenschaft Dynamic der Fields-Gruppe aller beteiligten Tabellen entsprechend gesetzt werden, im einfachsten Fall wird diese auf Yes gesetzt, um im Visual Studio später alle Felder der Tabellen verwenden zu können.


Wer schon einmal eine Applikation inkl. Datenbank von einer Netzwerk-Domäne in eine andere übertragen hat, der kennt vielleicht die Situation. Man hat die Applikation und die Datenbank eingerichtet, kann sich in Dynamics AX aber nicht anmelden und erhält die Fehlermeldung:

You are not recognized user of Dynamics AX. Please contact your system administrator

Die Ursache dafür ist, daß in der Tabelle UserInfo - das ist jene Tabelle wo die AD-Benutzer verwaltet werden - natürlich noch die Benutzer der Ursprungsdomäne hinterlegt sind. Im einfachsten Fall ändert man in dieser Tabelle den Eintrag für den Administrator, indem man die Felder NetworkDomain, NetworkAlias und SID entsprechend ändert. NetworkDomain und NetworkAlias sind selbsterklärend, woher bekommt man aber die SID?

Eine einfache Internet-Recherche nach "GET SID" liefert einige Möglichkeiten, weniger bekannt ist aber, daß auch Dynamics AX 2009 selbst eine Methode zur Verfügung stellt, um die SID für einen Benutzer auszulesen.

Jetzt braucht man also nur noch ein anderes, bereits lauffähiges AX, wo man sich anmelden und den Job absetzen kann ;-)

static void getUserSid(Args _args)
{
    ;
    info(new xAxaptaUsermanager().getUserSid('h.schweda', 'schweda.net'));
}

Sollen in einem Formular nur bestimmte Felder zur Bearbeitung freigegeben sein, kann man die Eigenschaft allowEdit sämtlicher Felder der DataSource der Tabelle entsprechend umsetzen.

Einfacher geht’s mit folgendem Codebeispiel, welches in der init-Methode der DataSource eingebunden wurde und - im konkreten Fall in der Tabelle SalesLine - nur bei einem einzigen Feld die Bearbeitung erlaubt.

Im Gegensatz zu einem früher veröffentlichten Beitrag mit dem gleichen Thema werden bei diesem Codebeispiel auch Array-Felder korrekt berücksichtigt.

public void init()
{
    FormDataObject  fdo;
    int             f;
    SysDictTable    sysDictTable;
    MapEnumerator   fdoMapEnumerator;
    ;
    sysDictTable = new SysDictTable(tablenum(SalesLine));
    for(f=1;f<=sysDictTable.fieldCnt();f++)
    {
        fdoMapEnumerator = new MapEnumerator(formDataSourceArrayFieldExtObjects(salesLine_ds, sysDictTable.fieldCnt2Id(f)));
        while (fdoMapEnumerator.moveNext())
        {
            fdo = fdoMapEnumerator.currentValue();
            if(fdo)
            {
                fdo.allowEdit(false);
            }
        }
    }

    fdo = salesLine_ds.object(fieldnum(SalesLine, QtyOrdered));
    fdo.allowEdit(true);
}

Getestet in Dynamics AX 2009


Wer einmal in die Verlegenheit kommt, ermitteln zu müssen ob für eine bestimmte Tabelle Datensatzvorlagen vorhanden sind, für den kann folgendes Code-Beispiel hilfreich sein.

Im Beispiel wird ermittelt, wieviele Benutzer- bzw. Unternehmensvorlagen es für die Tabelle InventTable gibt und ob der aktuelle Benutzer sich den Vorlagendialog anzeigen lässt, wenn er/sie einen neuen Artikel anlegt.

static void SysRecordTemplatesActive(Args _args)
{
    tableId tableId = tableNum(inventTable);
    common common = new sysdictTable(tableId).makeRecord();
    SysRecordTemplateStorageUser storageUser       = SysRecordTemplateStorage::newCommon(common, SysRecordTemplateType::User);
    SysRecordTemplateStorageCompany storageCompany = SysRecordTemplateStorage::newCommon(common, SysRecordTemplateType::Company);
    sysRecordTemplateSelect sysRecordTemplateSelect;
    container userTemplates;
    container companyTemplates;
    ;
    // Liste der Vorlagen
    userTemplates = storageUser.get();
    info(strFmt("Anzahl Benutzervorlagen für Tabelle %1: %2", new sysdictTable(tableId).label(),
                                                              conLen(userTemplates)));

    companyTemplates = storageCompany.get();
    info(strFmt("Anzahl Unternehmensvorlagen für Tabelle %1: %2", new sysdictTable(tableId).label(),
                                                                  conLen(companyTemplates)));
   
    // Soll der aktive Benutzer nach Vorlagen gefragt werden?
    sysRecordTemplateSelect = SysRecordTemplateSelect::newTableId(tableId);
    sysRecordTemplateSelect.load();
   
    info(strFmt("Datensatzvorlagen für die Tabelle %1 in Verwendung: %2", new sysdictTable(tableId).label(),
                                                                          enum2str(sysRecordTemplateSelect.parmPrompt())));
}

Wenn bei einem Benutzer der Register Stapel beim Aufruf eines Berichtes nicht angezeigt wird, kann dies u.U. daran liegen, daß der Benutzer keine oder unzureichende Berechtigung für den Sicherheitsschlüssel Stapelverarbeitungsbericht (SecurityKey BatchReport) hat.


 


Nachstehender Job durchsucht alle Projekte nach einem bestimmten Element des AOT.

Dafür wird ein Dialog verwendet, wo man lediglich den Namen und den Typ des zu suchenden Elementes angeben muss. Außerdem kann man noch einstellen, ob auch die privaten Projektknoten durchsucht werden sollen.

static void findObjectWithinProjects(Args _args)
{
    UtilElementType utilElementType        = UtilElementType::Table;
    Name            objectName             = 'custTable';
    boolean         includePrivateProjects = false;
    Dialog          dialog;
    DialogField     df_objectName;
    DialogField     df_utilElementType;
    DialogField     df_privateProjects;
    container       conProjects;
    int             c;
    TreeNode                treeNodeRoot;
    SysOperationProgress    sysOperationProgress;
    UtilElementType         utilElementTypeSelection;   

    #Aot
    #TreeNodeSysNodeType
    #AviFiles
    #define.objectNameField("Name of object")
    #define.utilElementTypeField("Type of object")

    void findChildNodes(TreeNode _treeNodeParent, ProjectNode _projectNode, str _objectName)
    {
        TreeNode            treeNode;
        TreeNodeIterator    treeNodeIterator;

        ;
        treeNodeIterator = _treeNodeParent.AOTiterator();
        treeNode = treeNodeIterator.next();
        while (treeNode)
        {
            if (treeNode.AOTgetNodeType() == #NT_PROJECT_GROUP)
            {
                findChildNodes(treeNode, _projectNode, _objectName);
            }
            else if (treeNode.AOTname() like _objectName)
            {
                utilElementTypeSelection = str2enum(utilElementTypeSelection, enum2str(utilElementType));
                if (!utilElementType || treeNode.applObjectType() == utilElementTypeSelection)
                {
                    if (!confind(conProjects, _projectNode.AOTname()))
                    {
                        conProjects = conins(conProjects, conlen(conProjects)+1, _projectNode.AOTname());
                    }
                    return;
                }
            }
            treeNode.treeNodeRelease();
            treeNode = treeNodeIterator.next();
        }
    }
    void loopProjectsNode(TreeNode _treeNode)
    {
        ProjectNode projectNode;
        TreeNode treeNodeProject;

        if (_treeNode)
        {
            treeNodeProject = _treeNode.AOTfirstChild();
            while (treeNodeProject)
            {
                projectNode = treeNodeProject;
                sysOperationProgress.setText(projectNode.name());
                findChildNodes(projectNode.loadForInspection(), treeNodeProject, objectName);
                treeNodeProject = treeNodeProject.AOTnextSibling();
            }
        }
    }
    ;
    dialog = new Dialog();
    dialog.caption("Find projects containing specific object");
    df_objectName      = dialog.addField(Types::String, #objectNameField);
    df_utilElementType = dialog.addField(typeid(UtilElementType), #utilElementTypeField);
    df_privateProjects = dialog.addField(typeid(NoYesId), "Include Private projects");
    df_objectName.value(objectName);
    df_utilElementType.value(utilElementType);
    df_privateProjects.value(includePrivateProjects);
    if( !dialog.run())
    {
        return;
    }
    objectName             = df_objectName.value();
    utilElementType        = df_utilElementType.value();
    includePrivateProjects = df_privateProjects.value();
    if (objectName == '*' || objectName == '')
    {
        throw error(strfmt("@SYS26332", #objectNameField));
    }
    setprefix(strfmt("Projects containing %1 '%2'", utilElementType, objectName));

    startLengthyOperation();

    sysOperationProgress = new SysOperationProgress();
    sysOperationProgress.setCaption("Searching");
    sysOperationProgress.setAnimation(#AviSearch);

    // Private projects
    if(includePrivateProjects)
    {
        loopProjectsNode(SysTreeNode::getPrivateProject());
    }
    // Shared projects
    loopProjectsNode(SysTreeNode::getSharedProject());
    sysOperationProgress.kill();
    endLengthyOperation();

    // List projects
    for(c=1;c<=conlen(conProjects);c++)
    {
        info(conpeek(conProjects, c));
    }
}

Wer eine etwas komfortablere Möglichkeit vorzieht, der kann sich bei Firma Loncar Technologies Inc. ein entsprechendes XPO herunterladen und ins AX einspielen. AUf Basis dieses XPOs ist auch obiger Job entstanden.

https://www.loncartechnologies.com/download.php


Seiten 1 2 3 4 ... 20 » 

 


 
 
Willkommen in meinem Blog!
Du willst mehr über mich bzw. über mein Blog erfahren?

Dann klicke bitte hier!
Feeds Was ist das?
RSS RSS-Feed
Atom Atom-Feed
Schlagworte / Tags Was ist das?
Beiträge des aktuellen Monats
Februar 2012
MoDiMiDoFrSaSo
 12345
6789101112
13141516171819
20212223242526
272829 

 

 
© 2006 Heinz Schweda | Impressum | Kontakt