This simple code illustrates the table property by X++.
static void Job19(Args _args)
{
SysDictTable sysDictTable;
TreeNode treeNode;
TableName tableName = 'VendTable';
TableId tableId;
;
tableId = tableName2Id(tableName);
sysDictTable = new SysDictTable(tableId);
treeNode = sysDictTable.treeNode();
info(strFmt('%1',tableName));
info(strFmt('%1',tableId));
info(strFmt('%1',treeNode.AOTgetProperty("CreatedDateTime")));
info(strFmt('%1',treeNode.AOTgetProperty("SaveDataPerCompany")));
}
My technical memo on Microsoft Dynamics 365 for Finance and Operations (formerly AX)
Tuesday, April 26, 2016
Tuesday, April 19, 2016
X++ Get the list of user with role
Hi, if we would like to display the user list with its role, we can use a kind of the following code. It isn't quite a good code as we should join all tables in the while select statement, however I got the display with '@sys..' and not found the solution yet, so I change a bit back to basic by adding one more select in it.
static void getUserWithRole(Args _args)
{
UserInfo userInfo;
SecurityUserRole securityUserRole;
SecurityRole securityRole;
int i=0;
;
while
select userInfo
join securityUserRole
where securityUserRole.User == userInfo.Id
{
i++;
if (i >= 5)
break;
info(userInfo.Id);
securityRole = null;
select securityRole
where securityRole.RecId == securityUserRole.SecurityRole;
if (securityRole)
{
info(securityRole.AotName);
info(securityRole.Name);
info(securityRole.Description);
}
}
}
The result if we write it in csv format, will looks like below. Until the next post!
static void getUserWithRole(Args _args)
{
UserInfo userInfo;
SecurityUserRole securityUserRole;
SecurityRole securityRole;
int i=0;
;
while
select userInfo
join securityUserRole
where securityUserRole.User == userInfo.Id
{
i++;
if (i >= 5)
break;
info(userInfo.Id);
securityRole = null;
select securityRole
where securityRole.RecId == securityUserRole.SecurityRole;
if (securityRole)
{
info(securityRole.AotName);
info(securityRole.Name);
info(securityRole.Description);
}
}
}
The result if we write it in csv format, will looks like below. Until the next post!
Tuesday, April 12, 2016
X++ Create task to monitor batch running overnight and send email
Good morning! In real-life we have many batches which are scheduled to run overnight. This scenario will show how to create a task to check their status and send email to related supporters.
There are two sub tasks for this. They are:
1. Batch status checking
2. Sending email
3. Schedule task and configuration
Let's start with the class and other methods.
class batchMonitor extends RunBaseBatch
{
boolean isCompleted;
#define.filename(@'E:\aaa\batch.csv')
public container pack()
{
return conNull();
}
public boolean unpack(container packedClass)
{
return true;
}
public void run()
{
this.genCSV();
this.sendEmail();
}
} // end class
1. Batch status checking
This method will check batch status and write csv.
private void genCSV()
{
// ------------- Declaration -------------
BatchJob batchJob;
BatchJobHistory batchJobHistory;
BatchCaption jobName;
int i=0,
iMax=0;
container listOfJobName = ['B001', 'B002'];
str strCaption,
strRecurPeriod,
strBatchStatus;
// Declaration: CSV file
CommaTextIo file;
container line,
lineHeader = ['B001', 'B002'];
#File
;
// ------------- Process -------------
isCompleted = true;
// Check file availability
file = new CommaTextIo(#filename, #io_write);
if (!file || file.status() != IO_Status::Ok)
{
throw error("File cannot be opened.");
}
// Write header
line = lineHeader;
file.writeExp(line);
// Write line
iMax = conLen(listOfJobName);
while (i < iMax)
{
i++;
jobName = conPeek(listOfJobName,i);
batchJob = null;
batchJobHistory = null;
select *
from batchJob
where batchJob.Caption == jobName && batchJob.Status == BatchStatus::Waiting
join batchJobHistory
order by batchJobHistory.EndDateTime desc
where batchJobHistory.BatchJobId == batchJob.RecId;
strCaption = strFmt('"%1"',batchJob.Caption);
strRecurPeriod = batchJob.recurrenceText();
strBatchStatus = strFmt('%1',batchJobHistory.Status);
if (strBatchStatus != 'Ended')
isCompleted = false;
// Write to file
line = [strCaption,
strRecurPeriod,
strBatchStatus
];
file.writeExp(line);
}
file.finalize();
}
2. Sending email
This method will send an email.
private void sendEmail(Args _args)
{
str sender = 'test@test.com'; // mail id of the sender
str recipient = 'test@test.com'; // mulitple recipients can be specified
str cc = 'test@test.com'; // mulitple cc id's can be specified
str subject = 'Subject of the mail';
str body = 'Content of the mail';
List toList;
List ccList;
ListEnumerator enumList;
Set permissionSet;
System.Exception exception;
str mailServer;
int mailServerPort;
System.Net.Mail.SmtpClient mailClient;
System.Net.Mail.MailMessage mailMessage;
System.Net.Mail.MailAddress mailFrom;
System.Net.Mail.MailAddress mailTo;
System.Net.Mail.MailAddressCollection mailToCollection;
System.Net.Mail.MailAddressCollection mailCCCollection;
System.Net.Mail.AttachmentCollection mailAttachementCollection;
System.Net.Mail.Attachment mailAttachment;
;
//Prepare init value
subject = strFmt('Batch job status as of %1',today());
body = strFmt('Hi all\r\n\rPlease kindly see the batch job status as of %1 in the attached. The status is',today());
if (isCompleted)
{
subject += ' - Completed';
body += ' all completed.';
}
else
{
subject += ' - Failed';
body += ' failed.';
}
body += '\r\n\rRegards\r\n\rMisterAAA';
try
{
toList = strSplit(recipient, ';');
ccList = strSplit(cc, ';');
permissionSet = new Set(Types::Class);
permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
permissionSet.add(new FileIOPermission(#filename, 'test1'));
CodeAccessPermission::assertMultiple(permissionSet);
mailServer = SysEmaiLParameters::find(false).SMTPRelayServerName;
mailServerPort = SysEmaiLParameters::find(false).SMTPPortNumber;
mailClient = new System.Net.Mail.SmtpClient(mailServer, mailServerPort);
enumList = toList.getEnumerator();
enumList.moveNext();
mailFrom = new System.Net.Mail.MailAddress(sender);
mailTo = new System.Net.Mail.MailAddress(strLTrim(strRTrim(enumList.current())));
mailMessage = new System.Net.Mail.MailMessage(mailFrom, mailTo);
mailToCollection = mailMessage.get_To();
while (enumList.moveNext())
{
mailToCollection.Add(strLTrim(strRTrim(enumList.current())));
}
enumList = ccList.getEnumerator();
mailCCCollection = mailMessage.get_CC();
while (enumList.moveNext())
{
mailCCCollection.Add(strLTrim(strRTrim(enumList.current())));
}
mailMessage.set_Priority(System.Net.Mail.MailPriority::Normal);
mailMessage.set_Subject(subject);
mailMessage.set_Body(body);
mailAttachementCollection = mailMessage.get_Attachments();
mailAttachment = new System.Net.Mail.Attachment(#fileName);
mailAttachementCollection.Add(mailAttachment);
mailClient.Send(mailMessage);
mailMessage.Dispose();
CodeAccessPermission::revertAssert();
info("Email sent successfully");
}
catch (Exception::CLRError)
{
exception = ClrInterop::getLastException();
while (exception)
{
info(exception.get_Message());
exception = exception.get_InnerException();
}
CodeAccessPermission::revertAssert();
}
}
3. Schedule task and configuration
3.1 Create a folder on AOS server - This is important as Batch is being run on Server side so we need a folder to keep the attachment file. In this case, it's E:\aaa.
3.2 Check Outgoing email parameter
3.3 Check Batch server parameter
3.4 Schedule the class by the following job
static void batchSchedule(Args _args)
{
BatchHeader batchHeader;
BatchInfo batchInfo;
RunBaseBatch rbbTask;
str sParmCaption = "Batch Monitoring";
;
rbbTask = new batchMonitor();
batchInfo = rbbTask.batchInfo();
batchInfo.parmCaption(sParmCaption);
batchInfo.parmGroupId(""); // The "Empty batch group".
batchHeader = BatchHeader ::construct();
batchHeader.addTask(rbbTask);
batchHeader.save();
info(strFmt("'%1' batch has been scheduled.", sParmCaption));
}
Finally, our task is in the batch list which can manage through dynamics ax standard. Thanks for reading. Until next post!
References:
Walkthrough: Extending RunBaseBatch Class to Create and Run a Batch [AX 2012]
Sending emails with CC and attachment in AX 2012
There are two sub tasks for this. They are:
1. Batch status checking
2. Sending email
3. Schedule task and configuration
Let's start with the class and other methods.
class batchMonitor extends RunBaseBatch
{
boolean isCompleted;
#define.filename(@'E:\aaa\batch.csv')
public container pack()
{
return conNull();
}
public boolean unpack(container packedClass)
{
return true;
}
public void run()
{
this.genCSV();
this.sendEmail();
}
} // end class
1. Batch status checking
This method will check batch status and write csv.
private void genCSV()
{
// ------------- Declaration -------------
BatchJob batchJob;
BatchJobHistory batchJobHistory;
BatchCaption jobName;
int i=0,
iMax=0;
container listOfJobName = ['B001', 'B002'];
str strCaption,
strRecurPeriod,
strBatchStatus;
// Declaration: CSV file
CommaTextIo file;
container line,
lineHeader = ['B001', 'B002'];
#File
;
// ------------- Process -------------
isCompleted = true;
// Check file availability
file = new CommaTextIo(#filename, #io_write);
if (!file || file.status() != IO_Status::Ok)
{
throw error("File cannot be opened.");
}
// Write header
line = lineHeader;
file.writeExp(line);
// Write line
iMax = conLen(listOfJobName);
while (i < iMax)
{
i++;
jobName = conPeek(listOfJobName,i);
batchJob = null;
batchJobHistory = null;
select *
from batchJob
where batchJob.Caption == jobName && batchJob.Status == BatchStatus::Waiting
join batchJobHistory
order by batchJobHistory.EndDateTime desc
where batchJobHistory.BatchJobId == batchJob.RecId;
strCaption = strFmt('"%1"',batchJob.Caption);
strRecurPeriod = batchJob.recurrenceText();
strBatchStatus = strFmt('%1',batchJobHistory.Status);
if (strBatchStatus != 'Ended')
isCompleted = false;
// Write to file
line = [strCaption,
strRecurPeriod,
strBatchStatus
];
file.writeExp(line);
}
file.finalize();
}
2. Sending email
This method will send an email.
private void sendEmail(Args _args)
{
str sender = 'test@test.com'; // mail id of the sender
str recipient = 'test@test.com'; // mulitple recipients can be specified
str cc = 'test@test.com'; // mulitple cc id's can be specified
str subject = 'Subject of the mail';
str body = 'Content of the mail';
List toList;
List ccList;
ListEnumerator enumList;
Set permissionSet;
System.Exception exception;
str mailServer;
int mailServerPort;
System.Net.Mail.SmtpClient mailClient;
System.Net.Mail.MailMessage mailMessage;
System.Net.Mail.MailAddress mailFrom;
System.Net.Mail.MailAddress mailTo;
System.Net.Mail.MailAddressCollection mailToCollection;
System.Net.Mail.MailAddressCollection mailCCCollection;
System.Net.Mail.AttachmentCollection mailAttachementCollection;
System.Net.Mail.Attachment mailAttachment;
;
//Prepare init value
subject = strFmt('Batch job status as of %1',today());
body = strFmt('Hi all\r\n\rPlease kindly see the batch job status as of %1 in the attached. The status is',today());
if (isCompleted)
{
subject += ' - Completed';
body += ' all completed.';
}
else
{
subject += ' - Failed';
body += ' failed.';
}
body += '\r\n\rRegards\r\n\rMisterAAA';
try
{
toList = strSplit(recipient, ';');
ccList = strSplit(cc, ';');
permissionSet = new Set(Types::Class);
permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
permissionSet.add(new FileIOPermission(#filename, 'test1'));
CodeAccessPermission::assertMultiple(permissionSet);
mailServer = SysEmaiLParameters::find(false).SMTPRelayServerName;
mailServerPort = SysEmaiLParameters::find(false).SMTPPortNumber;
mailClient = new System.Net.Mail.SmtpClient(mailServer, mailServerPort);
enumList = toList.getEnumerator();
enumList.moveNext();
mailFrom = new System.Net.Mail.MailAddress(sender);
mailTo = new System.Net.Mail.MailAddress(strLTrim(strRTrim(enumList.current())));
mailMessage = new System.Net.Mail.MailMessage(mailFrom, mailTo);
mailToCollection = mailMessage.get_To();
while (enumList.moveNext())
{
mailToCollection.Add(strLTrim(strRTrim(enumList.current())));
}
enumList = ccList.getEnumerator();
mailCCCollection = mailMessage.get_CC();
while (enumList.moveNext())
{
mailCCCollection.Add(strLTrim(strRTrim(enumList.current())));
}
mailMessage.set_Priority(System.Net.Mail.MailPriority::Normal);
mailMessage.set_Subject(subject);
mailMessage.set_Body(body);
mailAttachementCollection = mailMessage.get_Attachments();
mailAttachment = new System.Net.Mail.Attachment(#fileName);
mailAttachementCollection.Add(mailAttachment);
mailClient.Send(mailMessage);
mailMessage.Dispose();
CodeAccessPermission::revertAssert();
info("Email sent successfully");
}
catch (Exception::CLRError)
{
exception = ClrInterop::getLastException();
while (exception)
{
info(exception.get_Message());
exception = exception.get_InnerException();
}
CodeAccessPermission::revertAssert();
}
}
3. Schedule task and configuration
3.1 Create a folder on AOS server - This is important as Batch is being run on Server side so we need a folder to keep the attachment file. In this case, it's E:\aaa.
3.2 Check Outgoing email parameter
3.3 Check Batch server parameter
3.4 Schedule the class by the following job
static void batchSchedule(Args _args)
{
BatchHeader batchHeader;
BatchInfo batchInfo;
RunBaseBatch rbbTask;
str sParmCaption = "Batch Monitoring";
;
rbbTask = new batchMonitor();
batchInfo = rbbTask.batchInfo();
batchInfo.parmCaption(sParmCaption);
batchInfo.parmGroupId(""); // The "Empty batch group".
batchHeader = BatchHeader ::construct();
batchHeader.addTask(rbbTask);
batchHeader.save();
info(strFmt("'%1' batch has been scheduled.", sParmCaption));
}
Finally, our task is in the batch list which can manage through dynamics ax standard. Thanks for reading. Until next post!
References:
Walkthrough: Extending RunBaseBatch Class to Create and Run a Batch [AX 2012]
Sending emails with CC and attachment in AX 2012
Monday, April 4, 2016
X++ Date and utcDateTime
I will consolidate the techniques relating to Date and utcDateTime here.
Conversion: Date to utcDateTime
Refer to Date to UTCDateTime Convertion Dynamics Ax 2012
Sometimes we might need to query the data on field 'createdDateTime' whose type is utcDateTime so this code will help to convert type Date to utcDateTime.
Value 0 to 86400 stand for the time period 00:00:00 to 23:59:59
-------------------------------------------------------------------------------------------------------------
utcDateTime: how to increase or decrease datetime value
Sometimes we might need to query the data on the specific range. This below would help to get idea to increase or decrease utcDateTime.
-------------------------------------------------------------------------------------------------------------
Conversion: numeric to Time (TimeOfDay)
we can convert numeric (0 - 86400) to TimeOfDay by following code.
Conversion: Date to utcDateTime
Refer to Date to UTCDateTime Convertion Dynamics Ax 2012
Sometimes we might need to query the data on field 'createdDateTime' whose type is utcDateTime so this code will help to convert type Date to utcDateTime.
Value 0 to 86400 stand for the time period 00:00:00 to 23:59:59
date exampleDate = 01\02\2015; // it's 01-Feb-2015
utcDateTime utcStartDate,
utcEndDate;
CustTable custTable;
;
utcStartDate = DateTimeUtil::newDateTime(startDate,0);
utcEndDate = DateTimeUtil::newDateTime(endDate,86400);
select count(RecId)
from custTable
where custTable.createdDateTime >= utcStartDate &&
custTable.createdDateTime <= utcEndDate;
-------------------------------------------------------------------------------------------------------------
utcDateTime: how to increase or decrease datetime value
Sometimes we might need to query the data on the specific range. This below would help to get idea to increase or decrease utcDateTime.
utcDateTime currentday,
yesterday;
;
currentday = DateTimeUtil::utcNow();
yesterday = DateTimeUtil::addDays(currentday, -1);
yesterday = DateTimeUtil::addSeconds(yesterday, 1);
info(strFmt('%1',currentday));
info(strFmt('%1',yesterday));
-------------------------------------------------------------------------------------------------------------
Conversion: numeric to Time (TimeOfDay)
we can convert numeric (0 - 86400) to TimeOfDay by following code.
TimeOfDay aaa;
;
aaa = 3600;
info(time2Str(aaa, TimeSeparator::Colon, TimeFormat::AMPM));
aaa = 4200;
info(time2Str(aaa, TimeSeparator::Colon, TimeFormat::AMPM));
aaa = 10800;
info(time2Str(aaa, TimeSeparator::Colon, TimeFormat::AMPM));
Subscribe to:
Posts (Atom)