Translate

Wednesday, January 15, 2020

X++ code to Export emails under print management destination settings

printMgmtSettings           printMgmtSettings;
    container                   printerSetting;
    SRSPrintDestinationSettings printDestinationSetting;
    PrintMgmtDocInstance        printMgmtDocInstance;
    while select printMgmtSettings
    {
        printDestinationSetting = new SRSPrintDestinationSettings();
        printDestinationSetting.unpack(printMgmtSettings.PrintJobSettings);
       
        if(printDestinationSetting.printMediumType() == srsPrintMediumType::Email)
        {
            select * from printMgmtDocInstance where printMgmtDocInstance.RecId==printMgmtSettings.ParentId;
            info(strFmt("%1 -%2",printMgmtDocInstance.NodeType, printDestinationSetting.emailTo()));
        }
    }

X++ code to setup print management destination settings on Customer

 PrintMgmtSettings       printMgmtSettings;
    PrintMgmtDocInstance    printMgmtDocInstance;
    CustTable               custtable = CustTable::find("C005751");

    container               printerSetting;
    SRSPrintDestinationSettings printDestinationSetting;

    printMgmtDocInstance    = printMgmtDocInstance::find(custtable.RecId,77,printMgmtNodeType::CustTable,printMgmtDocumentType::CustAccountStatement,1);

    if(printMgmtDocInstance.RecId == 0 && custtable.email() != strMin())
    {
        printerSetting = conNull();
        printDestinationSetting = new SRSPrintDestinationSettings();

        ttsBegin;

        printMgmtDocInstance.DocumentType = printMgmtDocumentType::CustAccountStatement;
        //printMgmtDocInstance.Name = strFmt("Email 'Account Statements' to %1", custtable.AccountNum);
        printMgmtDocInstance.NodeType = printMgmtNodeType::CustTable;
        printMgmtDocInstance.PrintType = PrintMgmtDocInstancetype::Original;
        printMgmtDocInstance.PriorityId = 1;
        printMgmtDocInstance.ReferencedRecId = custtable.recId;
        printMgmtDocInstance.ReferencedTableId = 77;// CustTable;
        printMgmtDocInstance.insert();

        // just use a new instance of SRSPrintDestinationSettings to serialize an empty container
        printDestinationSetting.unpack(printerSetting);
        printDestinationSetting.printMediumType(srsPrintMediumType::Email);
        printDestinationSetting.emailTo(custtable.email());
        //printDestinationSetting.emailCc(ccEmailString);
        printDestinationSetting.emailSubject("Proserv statements");
        printDestinationSetting.emailAttachmentFileFormat(SRSReportFileFormat::PDF);
        printerSetting = printDestinationSetting.pack();


        printMgmtSettings.clear();
        printMgmtSettings.Description = printmgmtdocInstance.Name;
        printMgmtSettings.NumberOfCopies = 1;
        printMgmtSettings.ParentId = printmgmtdocInstance.RecId;
        printMgmtSettings.PrintJobSettings = printerSetting;
        printMgmtSettings.PriorityId = printMgmtDocInstance.PriorityId;
        printMgmtSettings.insert();

        ttsCommit;
    }

X++ code to create/update party contact details- Customer/vendor/Employee/Prospect

below code to update customer/Vendor contact details like email, telephone, fax, etc.,

CustTable custTable;
   
    void setUpContacts(Addressing _desc, RefRecId _party,LogisticsElectronicAddressMethodType _type,LogisticsElectronicAddressLocator _value )
    {
        DirPartyLocation    dirPartyLocation,dirPartyLocationNew;
        LogisticsLocation   logisticsLocation;
        LogisticsElectronicAddress logEleAddress;

        select forupdate  forUpdate dirPartyLocation where dirPartyLocation.Party == _party
        join logisticsLocation where logisticsLocation.Description == _desc
                && logisticsLocation.recid==dirPartyLocation.Location
            join forUpdate logEleAddress where logEleAddress.Location == logisticsLocation.recid
                    && logEleAddress.Type == _type;
        if(logEleAddress.RecId != 0 && logEleAddress.Locator != _value)
        {
            ttsBegin;
            logEleAddress.validTimeStateUpdateMode(ValidTimeStateUpdate::Correction);
            logEleAddress.Locator = _value;
            logEleAddress.update();
            ttsCommit;
        }
        else if(logEleAddress.RecId == 0)
        {
            logisticsLocation = LogisticsLocation::create(_desc);
            dirPartyLocationNew.Party = _party;
            dirPartyLocationNew.Location = logisticsLocation.RecId;
            dirPartyLocationNew.ValidFrom = DateTimeUtil::utcNow();
            dirPartyLocationNew.ValidTo = DateTimeUtil::maxValue();
            dirPartyLocationNew.insert();

            logEleAddress.Location = logisticsLocation.RecId;
            logEleAddress.Type = _type;
            logEleAddress.Locator = _value;
            logEleAddress.insert();
        }
    }
    custTable = CustTable::find("C004800");
   setUpContacts("Email",custTable.Party,LogisticsElectronicAddressMethodType::Email,"prasan@domain.com");
    //Contact Name/description, Party, contact type, Email address

Tuesday, November 12, 2019

New Microsoft certifications for Dynamics 365 - role based

HI,

Microsoft has released its updated certifications on all Microsoft stack.

you can also filter the technology based on Dynamics 365 for certifications Dynamics 365 Finance and operations. Most of the AX previous certifications will expire this year end and until june next year, but the transcript will be available if you have completed any before they expire.

https://www.microsoft.com/en-us/learning/browse-all-certifications.aspx



Good luck.
Thanks
Prasan

List of free AX7/D365 courses available for download

Hi,

Below are the list of AX7 and D365 finance and operations courses available for free downloaded.

https://community.dynamics.com/365/b/learningcurriculum/posts/list-of-courses-available-for-download

and additional R3 courses available in below link
https://community.dynamics.com/365/b/learningcurriculum/posts/we-ve-added-more-ax-2012-r3-courses-for-download


also about how to download and run this course information will be available in the below link.

https://community.dynamics.com/365/b/learningcurriculum/posts/how-to-run-a-downloaded-course

PS: All information are provided from Microsoft websites.

Happy AX/D365 learning and good luck,

Thanks,
Prasan

AX Pending workflows using X++

Below is the X++ code to get the active pending workflows which are waiting for approvals and yet to approve.

workflowTrackingStatusTable is the header table which holds information about the order and the order which triggered the workflow and the current status of the workflow approvals.

workflowworkitemTable is the table which holds the information about who are all users are required to action the workflow for the particular workflow instance, once the approvee actioned then record will be deleted from this table and new approver is added to this table with new record.

workflowtrackingcommenttable will holds the comments of the user who has submitted, approved,rejected.

while select /*firstOnly10*/ workflowTrackingStatusTable //order by workflowworkitemTable.createdDateTime asc
            where workflowTrackingStatusTable.TrackingStatus == WorkflowTrackingStatus::Pending
            //&& workflowTrackingStatusTable.InstanceNumber=='INS2367227'
            //TODO: for testing- commet it in LIVE
            && (workflowTrackingStatusTable.ContextTableId == 1425//Supplier Invoice
            || workflowTrackingStatusTable.ContextTableId == 1967//Sales quotations
            || workflowTrackingStatusTable.ContextTableId == 345//Purchase order
            || workflowTrackingStatusTable.ContextTableId == 1551//Purchase requisitions
            || workflowTrackingStatusTable.ContextTableId == 617//Invoice proposals
            || workflowTrackingStatusTable.ContextTableId == 484//Travel expense claims
            || workflowTrackingStatusTable.ContextTableId == 4627) //Timesheet
            && workflowTrackingStatusTable.ContextCompanyId != "01"
            && (((workflowTrackingStatusTable.ContextTableId != 1551 && workflowTrackingStatusTable.ContextTableId != 484) && workflowTrackingStatusTable.ContextCompanyId != strMin())
                || (workflowTrackingStatusTable.ContextTableId == 1551 || workflowTrackingStatusTable.ContextTableId == 484))
            && workflowTrackingStatusTable.ContextRecId != 0
            && workflowTrackingStatusTable.ContextTableId != 0
            join workflowworkitemTable
                where workflowTrackingStatusTable.CorrelationId==workflowworkitemTable.CorrelationId
                && workflowworkitemTable.Status==WorkflowWorkItemStatus::Pending
            //&& workflowworkitemTable.UserId == 'prasan.k'
            && workflowworkitemTable.modifiedDateTime >= dateStart
        //TODO:Uncomment the date criteia when release to LIVE
            outer Join  workflowtrackingtable
                where workflowtrackingtable.Workflowtrackingstatustable==workflowTrackingStatusTable.recId
                && workflowtrackingtable.TrackingType == WorkflowTrackingType::Submission
                && workflowtrackingtable.User == workflowworkitemTable.UserId
            outer join  workflowtrackingcommenttable
                where workflowtrackingcommenttable.trackingid ==workflowtrackingtable.trackingId
        {

}

Workflow Approvals message tracking X++

Helps to find the approvals tracking like who and all are approved so far for the particular workflow and what are the workflow approval comments as such in X++ code.




private str getworkflowTrackmessage(WorkflowInstanceNumber _instanceNumber)
{
    WORKFLOWTRACKINGSTATUSTABLE w;
    WORKFLOWTRACKINGTABLE wt ;
    WORKFLOWTRACKINGTABLE wt2;
    workflowsteptable wst;
    workflowElementtable wet;
    workflowTrackingWorkItem wtw;
    UserInfo user;
    UserInfo user2;
    str txt,ret;
    workflowtrackingcommenttable wtct;
    boolean first=true;

    while select * from w ORDER BY wt2.CREATEDdATEtIME ASC
       where w.instANCEnUMBER==_instanceNumber//'INS2380404'//INS2383500'//
    join  wt2 where w.recId == wt2.workflowTrackingStatusTable && wt2.USER!='axwfexec'
    join wtct where wt2.RecId==wtct.WorkflowTrackingTable
    outer join wtw where wt2.recId==wtw.workflowTrackingTable
    outer join wst where wst.stepId == wt2.stepId
    // join wet where wet.ElementId == wt2.ElementId
    outer join user where user.id == wt2.User
    outer join user2 where user2.id==wtw.ToUser
    {
        txt ='';
        if (wt2.TrackingType == WorkflowTrackingType::Approval)
        {
            txt = strFmt("%1 : %2 Approved on %3 ", wst.Name, User.name,wt2.createdDateTime);
            if (wtct.Comment != strMin())
            {
                txt = strFmt("%1 ,Comments: %2",txt, wtct.Comment);
            }
            //info(txt);
        }

        if (wt2.TrackingType == WorkflowTrackingType::Delegation)
        {
            txt =strFmt("%1 : %2 Delegated to %3 on %4",wst.Name,User.name,user2.name,wt2.createdDateTime);
            if (wtct.Comment != strMin())
            {
                txt = strFmt("%1 ,Comments: %2",txt, wtct.Comment);
            }
            //info(txt);
        }

         if (wt2.TrackingType == WorkflowTrackingType::Rejection)
        {
            txt = strFmt("%1 : %2 Rejected on %3",wst.Name, User.name,wt2.createdDateTime);
            if (wtct.Comment != strMin())
            {
                txt = strFmt("%1 ,Comments: %2",txt, wtct.Comment);
            }
            //info(txt);
        }

        if (wt2.TrackingType == WorkflowTrackingType::Creation)
        {
            txt =strFmt("%1 : Assigned to user: %2 on %3",wst.Name, User.name,wt2.createdDateTime);
            if (wtct.Comment != strMin())
            {
                txt = strFmt("%1 ,Comments: %2",txt, wtct.Comment);
            }
            //info(txt);
        }

        if (wt2.TrackingType == WorkflowTrackingType::Submission)
        {
            txt = strFmt("Submitted by : %1 on %2", User.name,wt2.createdDateTime);
            if (wtct.Comment != strMin())
            {
                txt = strFmt("%1 ,Comments: %2", txt, wtct.Comment);
            }
            //info(txt);
        }
        if(first)
        {
            ret = txt;
            first = false;
        }
        else
        {
            ret = ret + '\n' + txt;
        }
    }
    return ret;
}

Wednesday, October 30, 2019

MultiSelectHelper class - Select multilpe records in form grid to update the data altogether once.

1) Create a class that extends the runbase class.

2) write a main method to handle the class methods and its arugments

In main method declare the below helper class
 MultiSelectionHelper                helper;//multiselect

and also validate the class is invoked based on correct paramaters and caller

for example: if(_args && _args.dataset() == tableNum(VendInvoiceInfoTable))//if class is invoked from table VendInvoiceInfoTable.

also initialize the helper class as below
 helper  = MultiSelectionHelper::createFromCaller(_args.caller());

read the first record as vendInvoiceInfoTable = helper.getFirst();

and next records as vendInvoiceInfoTable = helper.getNext();

loop the multiple records recading
while (vendInvoiceInfoTable)
{
info(vendInvoiceInfoTable.num);

vendInvoiceInfoTable = helper.getNext();

}

refresh the caller form - formDataSourceRefresh(VendInvoiceInfoTable); this will help to show the data updated inmmidiately after the function runs.

Invoke this class using Action menu item and please make sure to setup the menu property :MultiSelect to YES whereever you add this menuitem on the form



complete example:

MultiSelectionHelper                helper;//multiselect
    if(_args && _args.dataset() == tableNum(VendInvoiceInfoTable))
    {
     
        helper  = MultiSelectionHelper::createFromCaller(_args.caller());
        vendInvoiceInfoTable = helper.getFirst();
   
        if (pendingInvoiceUpdate.prompt())
        {
            while (vendInvoiceInfoTable)
            {
                if(vendInvoiceInfoTable ))
                {
               info(vendInvoiceInfoTable.num);
                }
                vendInvoiceInfoTable = helper.getNext();
            }
            formDataSourceRefresh(VendInvoiceInfoTable);//refresh the caller form
        }
    }

Wednesday, March 20, 2019

Consuming AIF web services deployed from Dynamics Ax from External client- Mobile app

Hi folks,

Hope you are well!!


I have found some interesting things on how to communicate to AX from external client using AIF Websrvice

Once after the service classes is created in AX and deployed under Inbound ports- the svc file exposes the client.

we need to refer this web service reference using C#.

IN VS we need to use 'Add referencne' option and specify the svc file path where it path can be found on AX inbound port.


For example the path looks like this.
http://SERVERNane/MicrosoftDynamicsAXAif60/Messenger/xppservice.svc
where Messanger in the path is the name of the service.

once the service reference is created we need to use and refer that in C# code

using MessangerSolution.MessangerApp;
=>Name of the service refrence created in C# is MessangerApp

declare the service
MessangerApp.BasicHttpBinding_PSV_Messenger app = new BasicHttpBinding_PSV_Messenger();

Providing credentials:
app.UseDefaultCredentials = FALSE;
app.Credentials = new System.Net.NetworkCredential("USERVNAME","SECURE PASSWORD","DOMAIN");

then you can invoke the service class methods like
app.PassMessage();

This also fixes the below issue on network credentials:
System.Net.WebException: The request failed with HTTP status 401: Unauthorized

Wednesday, December 27, 2017

Database Re-Index script

Hi, Below is the script to re-build the database indesxes- to imporove performance of the system. hope this helps




SET nocount ON;

DECLARE @objectid INT;

DECLARE @indexid INT;

DECLARE @partitioncount BIGINT;

DECLARE @schemaname NVARCHAR(130);

DECLARE @objectname NVARCHAR(130);

DECLARE @indexname NVARCHAR(130);

DECLARE @partitionnum BIGINT;

DECLARE @partitions BIGINT;

DECLARE @frag FLOAT;

DECLARE @command NVARCHAR(4000);



-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function

-- and convert object and index IDs to names.

SELECT OBJECT_ID AS objectid,

INDEX_ID AS indexid,

PARTITION_NUMBER AS partitionnum,

AVG_FRAGMENTATION_IN_PERCENT AS frag

INTO #work_to_do

FROM sys.DM_DB_INDEX_PHYSICAL_STATS (DB_ID(), NULL, NULL, NULL, 'LIMITED')

WHERE AVG_FRAGMENTATION_IN_PERCENT > 10.0

AND INDEX_ID > 0;



-- Declare the cursor for the list of partitions to be processed.

DECLARE partitions CURSOR FOR

SELECT *

FROM #work_to_do;



-- Open the cursor.

OPEN partitions;



-- Loop through the partitions.

WHILE ( 1 = 1 )

BEGIN;

FETCH next FROM partitions INTO @objectid, @indexid, @partitionnum, @frag;

IF @@FETCH_STATUS < 0

BREAK;

SELECT @objectname = QUOTENAME(o.NAME),

@schemaname = QUOTENAME(s.NAME)

FROM sys.OBJECTS AS o

JOIN sys.SCHEMAS AS s

ON s.SCHEMA_ID = o.SCHEMA_ID

WHERE o.OBJECT_ID = @objectid;

SELECT @indexname = QUOTENAME(NAME)

FROM sys.INDEXES

WHERE OBJECT_ID = @objectid

AND INDEX_ID = @indexid;

SELECT @partitioncount = COUNT (*)

FROM sys.PARTITIONS

WHERE OBJECT_ID = @objectid

AND INDEX_ID = @indexid;

-- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding.

IF @frag < 30.0

SET @command = N'ALTER INDEX ' + @indexname + N' ON '

+ @schemaname + N'.' + @objectname + N' REORGANIZE';

IF @frag >= 30.0

SET @command = N'ALTER INDEX ' + @indexname + N' ON '

+ @schemaname + N'.' + @objectname

+ N' REBUILD WITH(ONLINE = ON, MAXDOP = 8)';

IF @partitioncount > 1

SET @command = @command + N' PARTITION='

+ CAST(@partitionnum AS NVARCHAR(10));

begin try

EXEC (@command);

end try

begin catch

print 'whoops'

end catch



PRINT N'Executed: ' + @command;

END;



-- Close and deallocate the cursor.

CLOSE partitions;

DEALLOCATE partitions;



-- Drop the temporary table.

DROP TABLE #work_to_do;

GO



sp_updatestats

Wednesday, May 3, 2017

List of table Ids in AX- SQL dictionary

Hi,


If you ever want to know where all table Id of AX tables are stored? here is the answer its SQLDICTIONARY


here is a query to know the table ID for VendTable

select tabId from sql where sql.sqlName=='VendTable'
&& sql.array==0;

Hope this Helps!
Prasan

Tuesday, March 14, 2017

Power BI documents

Hi,

Found some interesting topics that would help to explore much on data analysis and reporting.

Microsoft Power BI Documents

Introducing Microsoft Power BI, by Marco Russo and Alberto Ferrari:
http://www.sqlbi.com/books/introducing-microsoft-power-bi/

Power BI tutorial videos from Adam Saxton:
 
hope you get on to this.
 
Thanks,
Prasan

Dynamics AX 7 Wikipedia

Hi folks,

Below is the link where you can find documentation of AX 7.0 and dynamics 365 for operations. Happy Daxing!!

https://ax.help.dynamics.com/en/

Thanks,
Prasan

Tuesday, September 29, 2015

AX2012- Opening an url form AX X++ code

Infolog.lookupurl("www.google.com");


this statement will quickly open google website in default browser.


Hope this helps,
Prasan

AX2012- Update/Change FinancialDimension value in X++ code

static void PSV_UpdateOpDivDimension_Items(Args _args)
{
InventTable inventTable;
DimensionAttributeValueSetStorage dimStorage;
DimensionAttribute dimAttrOpDiv;
DimensionAttributeValue dimAttributeValue;
changeCompany('DAT')
{
while select forUpdate inventTable
{
ttsBegin;
dimStorage = DimensionAttributeValueSetStorage::find(inventTable.DefaultDimension);
dimAttrOpDiv = DimensionAttribute::findByName("Operating_Division");

dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttrOpDiv, 'NewValue', true, true);

dimStorage.addItem(dimAttributeValue);
inventTable.DefaultDimension = dimStorage.save();
inventTable.doUpdate();
ttsCommit;
}
}
}


This code updates the items financial dimensions to new values.


hope this helps,
Thanks
Prasan

Monday, September 22, 2014

Updating the value of existing dimesnions - Default dimension

Here is a code to update the value of particular dimensions for items

InventTable inventTable;

DimensionAttributeValueSetStorage dimStorage;

DimensionAttribute dimAttrOpDiv;

DimensionAttributeValue dimAttributeValue;

while select forUpdate inventTable

{

ttsBegin;

dimStorage = DimensionAttributeValueSetStorage::find(inventTable.DefaultDimension);

dimAttrOpDiv = DimensionAttribute::findByName("DIMENSION NAME);

dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttrOpDiv, 'VALUE', true, true);


dimStorage.addItem(dimAttributeValue);

inventTable.DefaultDimension = dimStorage.save();

inventTable.doUpdate();


ttsCommit;






}

}


Thursday, July 24, 2014

Code to check the current user have system administartor rights in AX X++ code

Hi,


Sometime before I was written a big statement to find out the System admin rights of current user. Later I realized about global function that will help us to find the same.


isSystemAdministrator(): will tell you the current user is having the admin rights or not.


if isSystemAdministrator() return true then the user is having system admin rights, else not a system admin.


Hope this helps you.
Prasn

Tuesday, July 30, 2013

Finding the current company curreny code in AX 2012

Hello,

Here is the code to find the company currency code in AX 2012

Method 1:
currencyCode = Ledger::accountingCurrency(CompanyInfo::current());

Method 2:
currencyCode = Ledger::accountingCurrency(CompanyInfo::Find().recid);

Method 3:
currencyCode = CompanyInfo::standardCurrency();
Thanks,
Prasan

Wednesday, June 19, 2013

AX 2012 – SSRS Reporting : Selecting the report design at the runtime based on the static parameter (dialog field) values

 
Hi,

Here is a solution to select the design at the runtime based on dialog field values in AX 2012 SSRS reports. Hope this will help you.

 
Create a controller class that extends SrsReportRunController()
 
And override preRunModifyContract() in the controller() class and follow the below steps.
 
protected void preRunModifyContract()
{
    boolean     paramValue1;
    boolean     paramValue2;
 
    SrsReportDataContract dataContract = this.parmReportContract();
    SrsReportRdlDataContract contract = dataContract.parmRdlContract();
 
    paramValue1 = contract.getValue(Param1_name);
    paramValue2 = contract.getValue(Param2_name);
   
    if(paramValue1)
    {
        dataContract.parmReportName(reportname.design_1);
    }
    else if(paramValue2)
    {
        dataContract.parmReportName(reportname.design_2);
    }
   // else default design is used to display the report which is used in main(); or you can specify your design name here
}
 
static void main(Args _args)
{
    xxx controller = new xxx (); // Let us assume the current class as xxx
 
    controller.parmReportName(reportName.defaultDesign);  // Make sure you specify the default design while running the report and this is required to run the report and to get the dialog.
    controller.parmArgs(_args);
    controller.startOperation();
}
 
And now, Create the output menu item and specify the object type to class and specify the object to this controller class which we create now. And the report is ready to select the design at runtime.
Hope this will helps you,
Prasan.. J