A library with Microsoft Dynamics AX, Microsoft Dynamics 365 FinOps and Supply Chain Management, Power Platform, Integrations, development and functional processes, tricks and trip, tutorials and much more....
Monday, December 18, 2017
Sunday, December 17, 2017
D365FO: How to debug a non-development and a non-production environment
This post outlines the steps how to debug an issue which is happening in non-development or non-production environments e.g. UAT environment.
Note: Restart IIS and make sure application pool is started
1. RDP to your environment where you want to debug the issue, with this example I am connecting to UAT (Sandbox).
2. Connect to SQL server
a. Get the server name from LCS database accounts
b. Use .database.windows.net to connect to database
3. Create a new SQL sign-in that the developer can use. This step lets the system administrator maintain the security of the sandbox environment. The developer will have access to one database for only a limited time. Use the following code to create the new SQL sign-in.
CREATE USER devtempuser WITH PASSWORD = 'pass@word1'
EXEC sp_addrolemember 'db_owner', 'devtempuser'
4. Stop following services in development box
a. IIS or world wide web publishing service development box
b. Microsoft Dynamics 365 unified Operations: Batch Management service
5. Go to J:\AosService\WebRoot\web.config.
6. Save a copy of the original web.config file, so that you can switch back later. I copied file in the same directory and renamed the web.config to webDev.config and copied one to web.config
7. Edit the following section in the web.config file.
Before your changes
<add key="DataAccess.Database" value="AxDB" />
<add key="DataAccess.DbServer" value="devDbServerName" />
<add key="DataAccess.SqlPwd" value="password" />
<add key="DataAccess.SqlUser" value="axdbadmin" />
After your changes
<add key="DataAccess.Database" value="sandboxdatabaseServerName" />
<add key="DataAccess.DbServer" value=" sandboxdbName.database.windows.net" />
<add key="DataAccess.SqlPwd" value="P@ssw0rd" />
<add key="DataAccess.SqlUser" value="devtempuser" />
8. Start IIS or world wide web publishing service
9. Do not start batch service otherwise your batch jobs of development box will start writing into UAT environment. Be careful with this step!!!
10. Now, connect to your development box from browser. Oops!!! It does not connect and throw an error. Don’t worry let’s jump into event log and see what’s in there
Okay; so it is an access issue and here is the resolution for it.
11. Execute the following command against Master database in UAT database server. IP address you get from the event viewer.
exec sp_set_firewall_rule N'PPDEV01', '13.70.85.22', '13.70.85.22'
Note: Restart IIS and make sure application pool is started
12. Connect to development URL again from browser and this time it works. You can now access UAT database (all legal entities etc.)
13. Restart or your start your VS in development box and attach to Process w3wp.exe to troubleshoot the issue(s)
Done with your debugging and got the resolution J
14. Now it is time to remove devtempuser from UAT database, this prevents having the permanent access to the sandbox database. Right-click on the devtempuser user under Sabndoxdatabase | Security | Users and delete it.
15. Stop IIS.
16. Revert changes from the web.config file or simply delete the copied one and rename the original one from WebDev to Web.
17. Start IIS.
18. Start batch service
19. Connect to development environment URL and make sure it is connected to development database.
Reference:
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/dev-tools/debugx-issue-against-copy-of-production
15. Stop IIS.
16. Revert changes from the web.config file or simply delete the copied one and rename the original one from WebDev to Web.
17. Start IIS.
18. Start batch service
19. Connect to development environment URL and make sure it is connected to development database.
Reference:
https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/dev-tools/debugx-issue-against-copy-of-production
Monday, November 27, 2017
Read excel through X++ in D365FO
Sample code to read an excel file, all it need file stream as a parameter.
public boolean importFromExcelfiles(System.IO.Stream _stream)
{
OfficeOpenXml.ExcelWorksheet worksheet;
OfficeOpenXml.ExcelRange cells;
OfficeOpenXml.ExcelPackage package = new OfficeOpenXml.ExcelPackage(_stream);
int totalRows,totalCells,rowCounter,cellCounter;
if(package)
{
worksheet = package.get_Workbook().get_Worksheets().get_Item(1);
cells = worksheet.Cells;
totalRows = worksheet.Dimension.End.Row ;
totalCells = worksheet.Dimension.End.Column;
for (rowCounter = 2; rowCounter<= totalRows; rowCounter++)
{
for (cellCounter=1; cellCounter<=totalCells; cellCounter++)
{
//Do something with the values
info(cells.get_Item(rowCounter, cellCounter).value);
}
}
}
return true;
}This method can be called like this;
FileUploadTemporaryStorageResult fileUploadResult;
fileUploadResult =
FileUploadControl.getFileUploadResult();
public void importFromFile()
{
if(fileUploadResult)
{
using(System.IO.Stream stream = fileUploadResult.openResult())
{
Filename
filename;
if(stream)
{
try
{
filename = fileUploadResult.getFileName();
if(this.importFromExcelfiles(stream))
{
info(strFmt("File %1 has been imported successfully", filename));
}
}
catch(Exception::Error)
{
warning("File import failure");
}
}
}
}
}
Thursday, July 6, 2017
Wednesday, July 5, 2017
D365FO - Method wrapping and chain of command
Another most awaiting Extensibility feature (Chain of Command) is going to come with one of the next platform updates.
This enables you to call/use protected members and methods without making them hookable and using pre/post event handlers.
More details are here https://blogs.msdn.microsoft.com/mfp/2017/07/04/extensible-x-chain-of-command/ and also here https://roadmap.dynamics.com/?i=296a1c89-ce4e-e711-80c0-00155d2433a1#.WVw1llbSw-M.linkedin
Stay tuned on above road map site to find more on this feature.
This enables you to call/use protected members and methods without making them hookable and using pre/post event handlers.
More details are here https://blogs.msdn.microsoft.com/mfp/2017/07/04/extensible-x-chain-of-command/ and also here https://roadmap.dynamics.com/?i=296a1c89-ce4e-e711-80c0-00155d2433a1#.WVw1llbSw-M.linkedin
Stay tuned on above road map site to find more on this feature.
Thursday, June 8, 2017
D365O - How to get current worker through X++
The current user or worker could be retrieved through following code since AX 2012
DirPersonUser::currentWorker()
However, In Dynamics 365 for operation this has refactored to;
HcmWorkerLookup::currentWorker()
DirPersonUser::currentWorker()
However, In Dynamics 365 for operation this has refactored to;
HcmWorkerLookup::currentWorker()
Saturday, April 15, 2017
AX 2012 : Remove an XML node through X++
Following code snippet may help you to remove an XML node from an XML message string.
This will remove DocuRef node along with its child nodes.
Original XML message
<?xml version="1.0" encoding="utf-8"?>
<SalesOrder xmlns = "http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<DocuRef class = "entity">
<Notes>Business Name</Notes>
</DocuRef>
</SalesOrder>
XML message after node being removed
<?xml version="1.0" encoding="utf-8"?>
<SalesOrder xmlns = "http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
</SalesOrder>
private str removeXMLTag(str xml)
{
XmlDocument doc = new XmlDocument();
XmlElement nodeScript;
XmlNode parentNode, childNode;
XmlNodeList xmlScriptList,
parentNodeList,
childNodeList;
int i,j;
doc.loadXml(xml);
// Get the root element and its child nodes
nodeScript = doc.getNamedElement("SalesOrder");
xmlScriptList = nodeScript.childNodes();
for(i=0; i < xmlScriptList.length(); i++)
{
parentNode = xmlScriptList.item(i);
childNodeList = parentNode.childNodes();
for (j=0; j < childNodeList.length(); j++)
{
childNode = childNodeList.item(j);
if (childNode.selectSingleNode("Notes"))
{
parentNode.removeChild(childNode);
}
}
}
return doc.outerXml();
}This will remove DocuRef node along with its child nodes.
Original XML message
<?xml version="1.0" encoding="utf-8"?>
<SalesOrder xmlns = "http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<DocuRef class = "entity">
<Notes>Business Name</Notes>
</DocuRef>
</SalesOrder>
XML message after node being removed
<?xml version="1.0" encoding="utf-8"?>
<SalesOrder xmlns = "http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
</SalesOrder>
Wednesday, April 12, 2017
AX 2012: Reading files from directory through X++
Following code can be used to read files from the specified path. With this example, I am reading a CSV files from a directory.
Class declaration
main method
Construct
readFileFromDirectory method
getFiles method
openFile method
readLine method
processRow method
processCLRErrorException method
Class declaration
class readFilesFromDirectory extends RunBase
{
FilePath filePath;
Filename filename;
CommaTextIo fileIO;
Set fileSet;
CommaIo commaIo;
str currentFile;
container lineCon;
#File
#AviFiles
}
main method
Public static
void main(Args _args)
{
readFilesFromDirectory readFiles =
readFilesFromDirectory::construct();
readFiles.readFilesFromDirtecory();
}
Construct
Public static
readFilesFromDirectory construct()
{
return
new readFilesFromDirectory();
}
readFileFromDirectory method
private void
readFilesFromDirtecory()
{
SetEnumerator sE;
int row = 1;
filePath = @"C:\Temp\FilesToRead";
this.getFiles();
sE = fileSet.getEnumerator();
while
(sE.moveNext()) // loop through all files
{
filename = sE.current();
this.openFile();
while(this.readLine())
// loop through all lines from current file
{
this.processRow(row, lineCon);
row++;
}
}
}
getFiles method
private void
getFiles()
{
System.String[] files;
System.Collections.IEnumerator enumerator;
str
file;
try
{
fileSet = new
Set(Types::Container);
files =
System.IO.Directory::GetFiles(filePath, '*.csv');
enumerator = files.GetEnumerator();
while
(enumerator.MoveNext())
{
file = enumerator.get_Current();
fileSet.add([file]);
}
}
catch
(Exception::Internal)
{
this.processCLRErrorException();
}
catch
(Exception::CLRError)
{
this.processCLRErrorException();
}
}
openFile method
private void
openFile()
{
FileIOPermission fileIOPermission;
fileIOPermission = new FileIOPermission(filename, #io_read);
fileIOPermission.assert();
commaIo = new
CommaIo(filename, #io_read);
if
(commaIo && commaIo.status() == IO_Status::Ok)
{
commaIo.inRecordDelimiter(#delimiterCRLF);
}
else
{
throw
error(strFmt("Error when opening file %1", filename));
}
}
readLine method
private boolean
readLine()
{
boolean
readLine;
if
(commaIo && commaIo.status() == IO_Status::Ok)
{
lineCon = commaIo.read();
readLine = lineCon != conNull();
}
else
{
lineCon = conNull();
readLine = false;
}
return
readLine;
}
processRow method
private void
processRow(int _rowIdx, container
_row)
{
//conpeek(_row,
1);
//conpeek(_row,
2);
//conpeek(_row,
3);
//conpeek(_row,
4);
//conpeek(_row,
5);
//conpeek(_row,
6);
//conpeek(_row,
7);
//conpeek(_row,
8);
//conpeek(_row,
9);
//conpeek(_row,
10);
}
processCLRErrorException method
private void
processCLRErrorException(
boolean
_throw = true
)
{
str
strError;
CLRObject exc;
CLRObject innerExc;
CLRObject clrExcMessage;
exc = CLRInterop::getLastException();
if
(exc)
{
clrExcMessage = exc.get_Message();
innerExc = exc.get_InnerException();
while
(innerExc != null)
{
clrExcMessage =
innerExc.get_Message();
innerExc =
innerExc.get_InnerException();
}
strError =
CLRInterop::getAnyTypeForObject(clrExcMessage);
error(strError);
if (_throw)
{
throw
error("Update has been cancelled");
}
}
}
Subscribe to:
Posts (Atom)
How to enable new Microsoft teams - Public Preview!
New Microsoft Teams is just AWESOME, quick but useful post below shows how you have this preview feature to make your life EASY! Open Micr...
-
One of the features of the dual-write is initial sync where you copy data from the source app (Finance Operation OR DataVerse) to the target...
-
This post focuses on the integration technologies available to have the Microsoft Dynamics 365 Finance Operations data available in Datavers...
-
Scenario: There are times when you want to delete an entity from target entity list and when you do so, you face an error message which does...