Wednesday, June 19, 2019

AX2012 Multiple SSRS installs


Multiple SSRS installs
1)      Get your first instance of Dynamics AX with SSRS up and running.
2)      Install an additional SSRS instance.  In the SQL Server 2008 R2 installer be sure to select Installation then New installation or add new features to an existing installation.  On the Feature Selection page select SSRS.  In the Named instance field enter a name for the second instance of SSRS on this machine.  When you select the account that will run SSRS be sure to use the bc proxy account that you will use in the second environment.
3)      Go to Reporting Services Configuration Manager and be sure to connect to your new instance and then finish the setup of this new SSRS instance.  Be sure to give this site a different port than other sites already being used on this machine.
4)      We need to modify four of the SSRS configuration files.  It is recommended that you make backups of these files before you modify them.

The information below assumes the following:
1.            The SSRS Instance is running SQL Server 2008 R2
Note:  The path will differ slightly for non-R2 installations – “C:\Prog~1\SQL\MSR10\MSSQL02”
2.            The SSRS Instance has been installed on the C:\ drive
3.            The SSRS Instance is named “MSSQLSERVER02".

If your path or instance name are different you will need to change that in the instructions below.

a)       Modify the ReportManager's web.config file.  The location should be Location: C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER02\Reporting Services\ReportManager.  In the configuration section you will find an entry for httpRuntime.  Change that entry to look like what is highlighted below.
<configuration>
</>
<system.web>
  </>
    <httpRuntime maxRequestLength="100000" executionTimeout="90000" />
    <securityPolicy>
      <trustLevel name="RosettaMgr" policyFile="rsmgrpolicy.config" />
    </securityPolicy>
  </>
</system.web>


b)      Modify the ReportServer's rsreportserver.config file.  The location should  C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER02\Reporting Services\ReportServer.  In the Configuration section if you have the entry <RSWindowsNegotiate> you need to remove this (it is highlighted in red below).   You will want to add the sections that are in yellow.  Note that you can use the first instance's rsreportserver.config file as a reference because it will have these entries.  Don't just use that instances copy of this file though.  Make the edits in this instances file.

<configuration>
</>
<Authentication>
<AuthenticationTypes>
<RSWindowsNegotiate>
</>
<AuthenticationTypes>
   </Authentication>
<Service>
  </>
     <IsRdceEnabled>True</IsRdceEnabled>
   </Service>
    </>
<Extensions>
  </>
     <Data>
</>
<Extension Name="AXQUERY" Type="Microsoft.Dynamics.Framework.Reports.AxQueryConnection,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<Extension Name="AXDATAMETHOD" Type="Microsoft.Dynamics.Framework.Reports.AxDataMethodConnection,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<Extension Name="AXREPORTDATAPROVIDER" Type="Microsoft.Dynamics.Framework.Reports.AxReportProviderConnection,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<Extension Name="AXADOMD" Type="Microsoft.Dynamics.Framework.Reports.AxAdomdConnection,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<Extension Name="AXENUMDATAPROVIDER" Type="Microsoft.Dynamics.Framework.Reports.EnumProviderConnection,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
     </Data>
</>
<ReportDefinitionCustomization>
   <Extension Name="AXRDCE" Type="Microsoft.Dynamics.Framework.Reports.AxRdce.CustomizationExtension,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</ReportDefinitionCustomization>
   </Extensions>
</configuration>
 

c)       Modify the ReportServer's rssrvpolicy.config file.  It should be located at Location: C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER02\Reporting Services\ReportServer.  Add the material that is highlighted below.  I would use the original report servers configuration file as a guide when updating this one.

<configuration>
  <mscorlib>
    <security>
      <policy>
        <PolicyLevel version="1">
          <SecurityClasses>
          </>
            <SecurityClass Name="AxSessionPermission" Description="Microsoft.Dynamics.Framework.Reports.AxSessionPermission, Microsoft.Dynamics.Framework.Reports, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
          </SecurityClasses>
         <NamedPermissionSets>
         </>
          <PermissionSet class="NamedPermissionSet" version="1" Name="AxSessionPermissionSet">
              <IPermission class="AxSessionPermission" version="1" Unrestricted="true" />
              <IPermission class="SecurityPermission" version="1" Flags="Assertion" />
            </PermissionSet>
          </NamedPermissionSets>
         <CodeGroup class="FirstMatchCodeGroup" version="1" PermissionSetName="Nothing">
         </>
          <CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="FullTrust" Name="AX_Reports_Strong_Name" Description="This code group grants Dynamics AX Reports code full trust. ">
             <IMembershipCondition class="StrongNameMembershipCondition" version="1" PublicKeyBlob="0024000004800000940000000602000000240000525341310004000001000100B5FC90E7027F67871E773A8FDE8938C81DD402BA65B9201D60593E96C492651E889CC13F1415EBB53FAC1131AE0BD333C5EE6021672D9718EA31A8AEBD0DA0072F25D87DBA6FC90FFD598ED4DA35E44C398C454307E8E33B8426143DAEC9F596836F97C8F74750E5975C64E2189F45DEF46B2A2B1247ADC3652BF5C308055DA9" />
          </CodeGroup>
        </CodeGroup>

d)      Modify the ReportServer's web.config file.  It should be located at C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER02\Reporting Services\ReportServer.  Add the material that is highlighted below.  I would use the original report servers configuration file as a guide when updating this one.

<configuration>
</>
<system.web>
  </>
       <httpModules>
       </>
        <add name="AxReportsHttpModule" type="Microsoft.Dynamics.Framework.Reports.AxReportsHttpModule,Microsoft.Dynamics.Framework.ReportsExtensions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
       </httpModules>
       <globalization requestEncoding="utf-8" responseEncoding="utf-8" />
       <httpRuntime maxRequestLength="100000" executionTimeout="90000" />
       <securityPolicy>
        <trustLevel name="RosettaSrv" policyFile="rssrvpolicy.config" />
       </securityPolicy>
       </>
  </>
<hostingEnvironment shadowCopyBinAssemblies="true" />
</system.web>


5)      You will now need to restart SSRS.  After it is restarted be sure to try to access the Report Server website and Report Manager website and that they come up.
6)      Create a new Client Configuration in the Configuration Utility.
a.       Be sure to give it a name that you will remember what it is for such as DAXDev.
b.       Verify that the AOS connection information is set correctly for this environment.
c.       On the Connection tab of the Configuration utility click on the refresh button.


Important:  This step will be necessary anytime there is a change to the WCF Service Endpoints.
d.       Export the Configuration file and save it to the Report Server's bin directory of your new SSRS instance and use the name “Microsoft.Dynamics.AX.ReportConfiguration.axc”.
Important:  The file must have the name “Microsoft.Dynamics.AX.ReportConfiguration.axc” to be recognized by the SSRS framework.
7)    Create an SSRS binding.  Complete the following procedure to add another Reporting Services instance binding using the Report Servers configuration dialog in Microsoft Dynamics AX.
a.     Click System administration > Setup > Business intelligence > Reporting Services > Report servers.
b.    In the Configuration ID field, enter a name that will identify the Reporting Services and AOS instances that you are connecting.
c.     In the Description field, enter a brief description to help you identify the Reporting Services and AOS instances.
d.    Select the Default configuration check box if the Reporting Services instance specified in this configuration is the default report server.
e.     On the Reporting Server information tab, enter the following information about the Reporting Services instance:
                                          i.    Enter the name of the server computer running Reporting Services.
                                        ii.    Enter the name of the Reporting Services instance for the second instance.
                                        iii.    Enter the Report Manager URL. By default, the URL is http://ServerName/Reports02.
                                        iv.    Enter the URL of the Reporting Services web service. By default, the URL is http://ServerName/ReportServer02.
                                         v.    Enter a name for a report folder. This report folder will be created for you in Reporting Services.
f.      On the Application Object Server information tab, select an AOS instance.
g.    Click the Create report folder button to create the report folder.
h.    Click Validate settings to verify that the information you entered in this form is correct and to verify that the report folder has been created.
If the validation fails, do the following:
                                          i.    Verify the name of the report server in the Server name field.
                                         ii.    Verify that Reporting Services is running.
                                        iii.    Verify in IIS (on the report server) that the DefaultAppPool is running.
                                        iv.    If you are using SSL on your report server, verify that the SSL certificate is stored in the certificate store for the local computer. For more information, see the IIS documentation.
Note:  Alternatively, you can use the Set_AXReportServerConfiguration PowerShell command to create the new SSRS configuration entry.

8)    Deploy the SSRS reports.  The site http://technet.microsoft.com/en-us/library/dd309703.aspx talks about this.
9)    Revert to the original configuration.  At this point, you’ll want to clear out the temporary configuration in the AX Configuration dialog. This will prevent the original SSRS Instance from being affected by the AOS configuration changes made for the new SSRS Instance.
a.     Launch the Dynamics AX Server Configuration utility located under Start > Administrative Tools > Microsoft Dynamics AX 2012 Configuration.  Select the original configuration to enable the original SSRS Configuration.


Tuesday, May 21, 2019

Post Sales Order Confirmation X++

Generate post sales order confirmation from X++

SalesTable salesTable = SalesTable::find(“000747”);
SalesFormLetter salesFormLetter;

salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation);
salesFormLetter.update(salesTable, systemDateGet(), SalesUpdate::All);

Change Report name of original invoice

modify method runprintmgmt in  SalesInvoiceController

if(formLetterReport.moveNextPrintSetting())
            {
                printSettingDetail = formLetterReport.getCurrentPrintSetting();

                if(printSettingDetail.parmReportFormatName() == PrintMgmtDocType::construct(PrintMgmtDocumentType::SalesOrderInvoice).getDefaultReportFormat())
                {
                    this.parmReportName(ssrsReportStr(SalesInvoiceCopy, Report));
                 
                }
                printSettingDetail.parmReportFormatName(this.parmReportName());
                this.resetReportDataContract();
                formLetterReport.parmReportRun().loadSettingDetail(printSettingDetail);
            }

Tuesday, May 14, 2019

Get all Security roles with related Privileges duties permissions and related paths of menuitem


This post will guid you to get all security roles with it related privileges duties permissions with related paths of menu item

Run this job to fill SysSecFlatDataTable, after job complete process you can open form from AOT SysSecObjectsInRole to confirm data population.

static void updateSecurityData(Args _args)
{
   sECURITYROLE secRoleTbl;

   SecuritySubRole secSubRoleTbl;

   SECURITYROLE secSubRoles;

   SecurityRoleTaskGrant secRoleTaskGrantTbl;

   SecurityTask secTaskTbl;

   SecurityTaskExplodedGraph secTaskExplodedGraphTbl;

   SecurityTask privilegesTbl;

   SecurityTaskEntryPoint secTaskEntryPointTbl;

   SecurableObject entryPointTbl;

   SysSecFlatDataTable dataTable;

   delete_from dataTable;



   while select * from secRoleTbl

       outer join * from secSubRoleTbl where (secSubRoleTbl.SecurityRole == secRoleTbl.RecId)

       outer join * from secSubRoles where (secSubRoleTbl.SecuritySubRole == secSubRoles.RecId)

       join   * from secRoleTaskGrantTbl where(secRoleTbl.RECID == secRoleTaskGrantTbl.SECURITYROLE || secSubRoles.RECID == secRoleTaskGrantTbl.SECURITYROLE)

       join * from secTaskTbl where (secRoleTaskGrantTbl.SECURITYTASK == secTaskTbl.RECID)

       join * from secTaskExplodedGraphTbl where (secTaskExplodedGraphTbl.SECURITYTASK == secTaskTbl.RECID)

       join * from privilegesTbl where (secTaskExplodedGraphTbl.SECURITYSUBTASK == privilegesTbl.RECID)

       join * from secTaskEntryPointTbl where (secTaskExplodedGraphTbl.SECURITYSUBTASK == secTaskEntryPointTbl.SECURITYTASK)

       join * from entryPointTbl where secTaskEntryPointTbl.ENTRYPOINT == entryPointTbl.RECID

       {
             ttsbegin;
           SysSecObjectsMiner::AddRelatedRolesRecToDataTable(dataTable, secRoleTbl.RecId, secRoleTbl.AOTNAME, secRoleTbl.NAME,

            secSubRoles.RecId, secSubRoles.AotName, secSubRoles.Name, secTaskTbl.RecId, secTaskTbl.AOTNAME, secTaskTbl.NAME, privilegesTbl.RecId, privilegesTbl.AOTNAME, privilegesTbl.NAME,

           entryPointTbl.RecId, entryPointTbl.NAME, entryPointTbl.TYPE, secTaskEntryPointTbl.PERMISSIONGROUP);
            ttscommit;

   }

}

And then run this job to truncate and run batch job to update Xref Tables

static void UpdateCrossRefBatch(Args _args)

{

    ;

    xRefUpdate::truncateXrefTables();

    xRefUpdateIL::updateAllXref(true, false, true);

    info("Done, cross reference update batch job created.");


}

And then create table with 2 field path string 900 and RefRecId recid to be filled by below jobs


static void updatePathData(Args _args)
{
    Custom_SecurityPaths Custom_SecurityPaths;
    SysSecFlatDataTable SysSecFlatDataTable;
    #TreeNodeSysNodeType

   #Properties

   #AOT

   TreeNode menuItemNode = TreeNode::findNode(@"\Menu Items\Display\CustTableListPage");

   TreeNode menuNode;

   xRefPaths xRefPaths;

   xRefReferences xRefReferences;

   TreeNode parentNode;

   Str path;
    while select SysSecFlatDataTable where SysSecFlatDataTable.EntryPointType==SecurableType::MenuItemDisplay && SysSecFlatDataTable.EntryPoint>''{
      menuItemNode = TreeNode::findNode(@"\Menu Items\Display\"+SysSecFlatDataTable.EntryPoint);

   xRefPaths = xRefPaths::find(menuItemNode.treeNodePath());

       while select xRefReferences

           where xRefReferences.referencePathRecId == xRefPaths.RecId

           && xRefReferences.Reference == XRefReference::Read

       {

           path = SysLabel::labelId2String(menuItemNode.AOTgetProperty(#PropertyLabel));

           menuNode = TreeNode::findNode(xRefPaths::findRecId(xRefReferences.xRefPathRecId).Path);

           if(menuNode && SysTreeNode::path2ApplObjectType(menuNode.treeNodePath()) == UtilElementType::Menu)

           {

               parentNode = menuNode.AOTparent();

               while(parentNode && parentNode.treeNodePath() != #MenusPath)

               {

                   path = SysLabel::labelId2String(parentNode.AOTgetProperty(#PropertyLabel))  + " > " + path;

                   parentNode = parentNode.AOTparent();

               }
            Custom_SecurityPaths.clear();
            Custom_SecurityPaths.Path=path;// info(path);
            Custom_SecurityPaths.RefRecid=SysSecFlatDataTable.RecId;
            Custom_SecurityPaths.doInsert();

           }
while select SysSecFlatDataTable where SysSecFlatDataTable.EntryPointType==SecurableType::MenuItemDisplay && SysSecFlatDataTable.EntryPoint>''{
      menuItemNode = TreeNode::findNode(@"\Menu Items\Action\"+SysSecFlatDataTable.EntryPoint);

   xRefPaths = xRefPaths::find(menuItemNode.treeNodePath());

       while select xRefReferences

           where xRefReferences.referencePathRecId == xRefPaths.RecId

           && xRefReferences.Reference == XRefReference::Read

       {

           path = SysLabel::labelId2String(menuItemNode.AOTgetProperty(#PropertyLabel));

           menuNode = TreeNode::findNode(xRefPaths::findRecId(xRefReferences.xRefPathRecId).Path);

           if(menuNode && SysTreeNode::path2ApplObjectType(menuNode.treeNodePath()) == UtilElementType::Menu)

           {

               parentNode = menuNode.AOTparent();

               while(parentNode && parentNode.treeNodePath() != #MenusPath)

               {

                   path = SysLabel::labelId2String(parentNode.AOTgetProperty(#PropertyLabel))  + " > " + path;

                   parentNode = parentNode.AOTparent();

               }
            Custom_SecurityPaths.clear();
            Custom_SecurityPaths.Path=path;// info(path);
            Custom_SecurityPaths.RefRecid=SysSecFlatDataTable.RecId;
            Custom_SecurityPaths.doInsert();

           }
while select SysSecFlatDataTable where SysSecFlatDataTable.EntryPointType==SecurableType::MenuItemDisplay && SysSecFlatDataTable.EntryPoint>''{
      menuItemNode = TreeNode::findNode(@"\Menu Items\Output\"+SysSecFlatDataTable.EntryPoint);

   xRefPaths = xRefPaths::find(menuItemNode.treeNodePath());

       while select xRefReferences

           where xRefReferences.referencePathRecId == xRefPaths.RecId

           && xRefReferences.Reference == XRefReference::Read

       {

           path = SysLabel::labelId2String(menuItemNode.AOTgetProperty(#PropertyLabel));

           menuNode = TreeNode::findNode(xRefPaths::findRecId(xRefReferences.xRefPathRecId).Path);

           if(menuNode && SysTreeNode::path2ApplObjectType(menuNode.treeNodePath()) == UtilElementType::Menu)

           {

               parentNode = menuNode.AOTparent();

               while(parentNode && parentNode.treeNodePath() != #MenusPath)

               {

                   path = SysLabel::labelId2String(parentNode.AOTgetProperty(#PropertyLabel))  + " > " + path;

                   parentNode = parentNode.AOTparent();

               }
            Custom_SecurityPaths.clear();
            Custom_SecurityPaths.Path=path;// info(path);
            Custom_SecurityPaths.RefRecid=SysSecFlatDataTable.RecId;
            Custom_SecurityPaths.doInsert();

           }


       }

}

}

Then outer join the paths table with SysSecFlatDataTable 1:n and you will get each role and entry point path with access

Sunday, May 12, 2019

AX2012 : CIL generation error : The given key was not present in the dictionary.

Recently I faced this error when generating incremental IL 
"CIL generation: The given key was not present in the dictionary."

The quick way to fix this is to check the CIL log file, generally located at "C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin\XppIL\Dynamics.Ax.Application.dll.log"

Here you will find the AOT object for which the CIL generator found the error. Compile that object, fix the error and then regenerate the IL. 

Before generating IL it is good practise to make sure that there are no compilation errors in the code. 

How to create a batch Job in AX 2012


The Batch Job can be created in Different manners. I am explaining one way for it.
Steps:
Create a class for the logic what you want you want to do.
Add this class with the Batch Job.
run the Batch Job.
Note: If you want to run the batch job in the client mode to one more step needed.
4) run the batch job in client mode.

Step:1
I am creating a simple class .
go to classes node in the AOT. create a New Class name it. Open Class Declaration
and extend it to the RunBaseBatch class.
class Batch4Demo extends RunBaseBatch
{
}
after this override the run method of the class. Right click on the class in the AOT go to Override and select run method. in this run methode write your own logic like as shown bellow.
public void run()
{
//Code logic
}
after this override pack and unpack methods.
public container pack()
{
return conNull();
}
public boolean unpack(container packedClass)
{
return true;
}
now create a main method foe the class and call the run method in that
(this is very important with out main you cant run the class)
right click and add new method write following code.
private void main(Args _Args)
{
    Classname b = new classname(); //name of the class
    b.run();
}

Step:2
Create a Job in the Job Node of AOT write the following code in it.
static void EEMC_SecurityRoleBatch(Args _args)
{

    BatchHeader batHeader;
    BatchInfo batInfo;
    RunBaseBatch srTask;
    str sParmCaption = "Security Role Update Batch Job";
    ;
    srTask = new classname();
    batInfo= srTask .batchInfo();
    batInfo.parmCaption(sParmCaption);
    batInfo.parmGroupId(""); // The "Empty batch group".
    batHeader = BatchHeader ::construct();
    batHeader.addTask(srTask);
    batHeader.save();
    info(strFmt("'%1' batch has been scheduled.", sParmCaption));
}
Step:3
Now Compile the Job And Class. run the Batch Job by pressing F5.
Before running Generate the IL code. Go to Build and click on Generate Incremental CIL..
It will create a Batch job in the Batch Jobs Form you can view it by going into the
following path
System Administration --> Inquiries --> BatchJobs --> Batch Jobs.
You should see the status as waiting---> Executing --> Ended in the status column
of the Batch Jobs form.
Now the above batch job will run only on the server.
If you want to make it run on the Client. follow bellow steps.
Step:4
Override the method runsImpersonated. Right click on the Batch4Demo class and override the above method write the following code in it.
public boolean runsImpersonated()
{
// false means that the batch must run on a client.
return false;
}
Now run the class as mentioned above.
After running it we have to add it to the client section to do this.
Go to Organization administration --> Basic Or Periodic --> Batch Processing.
open the form and you can see batch group and private check box option.
in the group option select empty group( Because in the above code we have selected empty in the batch group). uncheck the private. now click ok. the batch job will run.






Monday, May 6, 2019

Users Security Roles Inquiry

Get all related security roles for users
below is a job and you can create query and form for visual view


static void FT_UserRoleInquiry(Args _args)
{
    UserInfo        _UserInfo;
    SecurityRole    _SecurityRole;
    SecurityUserRole    _SecurityUserRole;
   
   while select * from _UserInfo join _SecurityUserRole where _SecurityUserRole.User == _UserInfo.id join _SecurityRole where _SecurityRole.RecId == _SecurityUserRole.SecurityRole
    {
        info(strFmt("user %1 Security %2",_UserInfo.name,_SecurityRole.AotName    ));
    }
}