We have successfully implemented GST patches for India localization for Dynamics AX. You can go through the link below:
Please contact us at sales@axpeditesoftware.com in case you need help in implementing GST patches for DAX.
#OneIndiaOneTax
We have successfully implemented GST patches for India localization for Dynamics AX. You can go through the link below:
Please contact us at sales@axpeditesoftware.com in case you need help in implementing GST patches for DAX.
#OneIndiaOneTax
Friends,
Here is a good article by Cognitive Group on reasons as to why other ERP consultants should rush to try their expertise in Dynamics AX. It’s a good read.
#DynamicsAX, #AXJobs
This property is part of Tables Permission Framework (TPF). The Table Permissions Framework (TPF) enables administrators to add an additional level of security to tables that store sensitive data. TPF adds table-level security that verifies access rights no matter the origin of the request.
To enable TPF, an administrator specifies a value for the AOSAuthorizationProperty on a specific table in the AOT. The AOSAuthorizationProperty authorizes Create, Read, Update, and Delete operations. For some tables, it is important to authorize all operations because the data is sensitive. For other tables, you might find it suitable to specify a subset of operations, such as Create, Update, and Delete. In the case when you have specified a subset, the AOS authorizes the Create, Update, and Delete operations, but allows users to perform View operations if they have access to Microsoft Dynamics AX.
TPF can be enabled on any table in the Microsoft Dynamics AX database. For the sake of time and efficiency, however, administrators assign TPF to tables that are considered to be sensitive or to be of critical business value.
For example, consider the following scenario:
You can change or add TPF for a table, but its is recommended that you perform TPF changes in a test environment so that you can study the impact of TPF changes on user groups that access that table.
To enable TPF on database table:
If you added TPF to a table, you might need to specify or expand permissions for user groups that access that table. You can view which objects access a table by using the Used-by command in the AOT:
We can traverse our collections by using either an enumerator or an iterator.But there is no clarity why sometimes iterator fails while on other occasions it is flawless.Simply what we do is that just replace iterator with the enumerator.
First theoretically what happens behind the scenes is as follows:
When collection classes were first introduced in DAX, the iterator was the only option.But because of a few unwarranted drawbacks that appear as hard-to-find errors, enumerators were added, and iterators were kept for the backward compatibility. Just see the below listed code snippet
List list = new List(Types::Integer);
ListIterator iterator;
ListEnumerator enumerator;
;
//Populate List
…..
…..
//Traverse using an iterator.
iterator = new ListIterator(list);
while(Iterator.more())
{
print iterator.value());
iterator.next();
}
//Traverse using an enumerator
enumerator = list.getEnumerator();
while(enumerator.moveNext())
{
print enumerator.current();
}
The 1st difference is the way iterator and enumerator instances are created.For the iterator,you call new,and for the enumerator,you get an instance from the collection class by calling the getEnumerator method.
In most cases, both approaches will work equally well. However, when the collection class resides on the opposite tier from the tier on which it is traversed,the situation is quite different.
For example, if the collection resides on the client tier and is traversed on the server tier, the iterator approach fails because the iterator does not support cross-tier referencing.
The enumerator does not support cross-referencing either, but it doesn’t have to because it is instantiated on the same tier as the collection class. Traversing on the server tier using the client tier enumerator is quite network intensive, but the result is logically correct because some code is marked as “Called From”, meaning that it can run on either tier,depending on where it is called from. You could have broken logic if you use iterators, even if you test one execution path.In many cases, hard-to-track bugs such as this surface only when an operation is executed in batch mode.
The 2nd difference is the way traversing happens which is another potential threat as the onus lies on the developer to ensure that he moves the pointer by using .next() method else the code can land into endless loop.So again enumerator is clear winner here
But there is still one scenario while iterator holds edge over enumerator, if you want to delete/insert items from list.See the code snippet below:
List list = new List(Types::Integer);
ListIterator iterator;
;
list.addEnd(100);
list.addEnd(200);
list.addEnd(300);
iterator = new ListIterator(list);
while(iterator.more())
{
if(iterator.value() == 200)
iterator.delete();
iterator.next();
}
print list.toString(); //{100,300}
pause;
}
static void restoreDeletedSO(Args _args)
{
SalesTableDelete salesTableDelete;
SalesLineDelete salesLineDelete;
SalesTable salesTable;
SalesLine salesLine;
;SalesTableDelete = SalesTableDelete::find(‘00450_036’, true);ttsbegin;switch (salesTableDelete.Cancelled)
{
case Voided::Voided :
salesTable = conpeek(salesTableDelete.SalesTable, 1);
salesTable.insert();while select forupdate salesLineDelete where salesLineDelete.SalesId == salesTableDelete.SalesId
{
salesLine = conpeek(salesLineDelete.SalesLine, 1);
salesLine.insert();
}
salesTableDelete.delete();
break;case Voided::linesVoided :
while select forupdate salesLineDelete where salesLineDelete.SalesId == salesTableDelete.SalesId
{
salesLine = conpeek(salesLineDelete.SalesLine, 1);
salesLine.insert();
salesLineDelete.delete();
}
salesTableDelete.delete();
break;
}ttscommit;
}
static void restoreDeletedPO(Args _args)
{
PurchTableDelete purchTableDelete;
PurchLineDelete purchLineDelete;
PurchTable purchTable;
PurchLine purchLine;
;purchTableDelete = PurchTableDelete::find(‘00242_049’, true);ttsbegin;switch (purchTableDelete.Cancelled)
{
case Voided::Voided :
purchTable = conpeek(purchTableDelete.PurchTable, 1);
purchTable.insert();while select forupdate purchLineDelete where purchLineDelete.PurchId == purchTableDelete.PurchId
{
purchLine = conpeek(purchLineDelete.PurchLine, 1);
purchLine.insert();
}
purchTableDelete.delete();
break;case Voided::linesVoided :
while select forupdate purchLineDelete where purchLineDelete.PurchId == purchTableDelete.PurchId
{
purchLine = conpeek(purchLineDelete.PurchLine, 1);
purchLine.insert();
purchLineDelete.delete();
}
purchTableDelete.delete();
break;
}ttscommit;
}
static void getNextRecIdAX40(Args _args)
{
//Table that stores record ids details for tables
SystemSequences systemSequences;//Class that handles Record id generation
SystemSequence systemSequence = new SystemSequence();
;select firstonly systemSequences where systemSequences.tabId == tableNum(CustTable);systemSequence.suspendRecIds(systemSequences.tabId);
info(strFmt(‘Next record id: %1’, systemSequence.reserveValues(systemSequences.minVal, systemSequences.tabId)));
systemSequence.removeRecIdSuspension(systemSequences.tabId);
}
static void getNaxtRecIdAX30(Args _args)
{
SystemSequence systemSequence;
;systemSequence = new SystemSequence();systemSequence.flushCache();
systemSequence.setCacheSize(30)
info(strFmt(‘Buffer size: %1’, systemSequence.getCacheSize()));
info(strFmt(‘Next record id: %1’, systemSequence.nextVal();));
}
CREATE PROCEDURE initFromSMMQuotationTable @DATAAREAID NVARCHAR(3
AS DECLARE @NEXTVAL BIGINT, @ROWCOUNT BIGINT
SELECT ……,
RECID = IDENTITY(BIGINT,0,1) AS QUOTATIONID —Assign an IDENTITY column with a starting value of 0 incremented by 1
INTO #TEMP
FROM DEL_SMMQUOTATIONTABLE WHERE QUOTATIONSTATUS = 0 —SMMQuotationStatus::InProcess
SELECT @NEXTVAL=NEXTVAL —Retrieve the next value for RECID for this table (by TABID)
FROM SYSTEMSEQUENCES
WITH(UPDLOCK, HOLDLOCK) WHERE ID = -1 AND TABID = 1967
INSERT INTO SALESQUOTATIONTABLE
(column-list)
SELECT ……,
RECID = QUOTATIONID+@NEXTVAL —When we insert into the permanent table, we add the temporary table‟s IDENTITY column to the next value retrieved from SYSTEMSEQUENCES
FROM #TEMP
SELECT @ROWCOUNT = COUNT(*) FROM #TEMP
UPDATE SYSTEMSEQUENCES —We update SYSTEMSEQUENCES to reflect the number of rows that we have added to this table
SET NEXTVAL=NEXTVAL + @ROWCOUNT
WHERE ID = -1 AND TABID = 1967
GO
MAPI technique:
Following code demonstrates the usage of mapi class for sending email. It uses outlook to send mail.
static void emailThruMapi(Args _args)
{
MapiEx mapiEx;
MapiExMail mapiExMail;
boolean mapiInitialised;
COM outlook;
COM item;
COM outlookNameSpace;
COM folder;
COM attachments;
str storeId;
str entryId;#define.outlookapplication(‘Outlook.Application’)
#define.mapi(‘Mapi’)
#smmMSOutlook2002ObjectModelConstants
#define.htmlText(‘<html><body>Hi There</body></html>’)
;outlook = new COM (#outlookapplication);
outlookNameSpace = outlook.getNameSpace(#mapi);
outlookNameSpace.logon();folder = outlookNameSpace.getDefaultFolder(#olFolderInbox);
item = outlook.createItem(#olMailItem);
storeId = folder.storeId();mapiEx = new MapiEx();
if(mapiEx && mapiEx.mapiInitialised())
{
mapiInitialised = true;
if (!mapiEx.logon("","",0) || !mapiEx.openMessageStore(storeId))
{
mapiInitialised = false;
mapiEx.logout();
mapiEx.finalize();
}//To send mail in HTML format
item.bodyFormat(#olFormatHTML);
item.htmlBody(#htmlText);//To send mail in plain text format
//item.body(‘Hi There’);item.subject(‘Test mail’);
//—-Attachements——————-
attachments = item.attachments();
attachments.add(‘E:\\Test\\4000.pdf’, 1, 1, ‘4000.pdf’);item.saveSentMessageFolder(outlookNameSpace.getDefaultFolder(#olFolderSentMail));
item.save();
entryId = item.entryId();mapiExMail = mapiEx.getMailFromEntryId(entryId);
if (!mapiExMail)
{
mapiInitialised = false;
mapiEx.logout();
mapiEx.finalize();
}
}if(item)
{
if (mapiInitialised && mapiExMail)
{
//TO
mapiExMail.addRecipient(‘sumit.loya@sumitloya.com’, "", #olTo);
//CC
mapiExMail.addRecipient(‘sreenath.girigari@sreenath.com’,"",#olCC);
//BCC
mapiExMail.addRecipient(‘ashish.singh@ashishsingh.com’,"",#olBCC);try
{
mapiExMail.save();
mapiExMail.close();
mapiExMail.finalize();
item = outlookNameSpace.getItemFromID(strupr(entryId));//This will display the mail item
//item.display();//This will directly send the mail without poping the mail window
item.send();
}
catch
{
if (mapiInitialised)
{
mapiEx.logout();
mapiEx.finalize();
}// An error occured sending mail from outlook.
throw error("@SYS97460");
}
}
}
}
SysMailer:
In the following code you can see how to use SysMailer class for sending mails. To use SysMailer class you need to set Relay server or computer name, user name and password in Administration –> Setup –> Email parameters form. This class internally uses CDO.Message dll for communication purposes. Please note in AX 3.0 SysMailer uses Dundas.Mailer dll for communication.
static void emailThruSysMailer(Args _args)
{
SysMailer mailer = new SysMailer();
SysEmailParameters parameters = SysEmailParameters::find();
;if (parameters.SMTPRelayServerName)
{
mailer.SMTPRelayServer(parameters.SMTPRelayServerName,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}
else
{
mailer.SMTPRelayServer(parameters.SMTPServerIPAddress,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}mailer.fromAddress(‘sumit.loya@sumitloya.com’);
mailer.tos().appendAddress(‘sumit.loya@sumitloya.com’);
mailer.body(‘hi’);
mailer.sendMail();
}
SysInetMail:
SysInetMail internally uses Mapi framework only. But to use SysInetMail one has to setup email templates from Basic –> Setup –> Email templates. SysInetMail will automatically pick sender id, subject, sender name, email text etc. from the email template that you provide while sending from SysInetMail. If you provide a full email address and not the id from Email templates table then also mail will be sent but in that case you need to provide the details yourself.
static void emailThruSysInetMail(Args _args)
{
SysInetMail mail = new SysInetMail();
;//To send to an email address directly
mail.sendMailAttach(‘sumit.loya@sumitloya.com’, ‘sreenath.girigari@sreenath.com’, ‘Test mail’, ‘Hi There’, false, ‘E:\\Test\\4000.pdf’);
//To use an email template to send mail
SysInetMail::sendEMail(‘Alerts’, ‘en-us’, ‘sumit.loya@sumitloya.com’);
}
SysEmailBatch:
SysEmailBatch internally uses SysMailer class and is used to send emails in batch. That is this class is batchable. Here is a small example for the class
static void emailThruSysEmailBatch(Args _args)
{
SysEmailBatch batch = new SysEmailBatch();
;batch.parmSenderAddr(‘sumit.loya@solugenix.com’);
batch.parmEmailAddr(‘sumit.loya@solugenix.com’);
batch.parmMessageBody(‘Hi There’);
batch.parmSubject(‘Test mail’);
batch.run();
}
SmmOutlookEmail:
This class internally uses Mapi class and is extensively used in HRM module. One feature of this class is that we can specify email groups and it can send mails to all the members defined under this email group. Here is a sample code
static void emailThruSmmOutlookEmail(Args _args)
{
SmmOutlookEmail smmOutlookEmail = new SmmOutlookEmail();
;if (smmOutlookEmail.createMailItem())
{
smmOutlookEmail.addEMailRecipient(‘sumit.loya@sumitloya.com’);
smmOutlookEmail.addSubject(‘Test mail’);
smmOutlookEmail.isHTML(true);
smmOutlookEmail.addBodyText(‘<html><body>Hi There</body></html>’);
smmOutlookEmail.sendEMail(smmSaveCopyOfEmail::No);
}
}