AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 03.07.2009, 07:05   #1  
Blog bot is offline
Blog bot
Участник
 
25,640 / 848 (80) +++++++
Регистрация: 28.10.2006
dynamic-ax.co.uk: Import Emails from Outlook 2007 into Dynamics AX 2009
Источник: http://dynamic-ax.spaces.live.com/Bl...4DE3!411.entry
==============

 


One of the really good features of Dynamics AX, is the ability to send Emails directly from the application... hence we can email invoices as pdf attachments directly from Dynamics AX..
However, there isnt a automated method of importing Emails from outlook into Dynamics.. one can manually attach emails to a record using the Document Handling functionality.... this functionality is best used in CRM where one can drag a email from Dynamics and drop it in ax (CRM > Encyclopaedia or CRM > Documents).. however the drag and drop (even though its cool) its still manual..
This project basically gives you the ability to import email from outlook in to ax... the code can either be executed on a periodic basis or can be a part of the client start up..
The code here is designed to read emails from the users Inbox and then on a basis of certain parameters, it find out which record in ax the email should be attached to..
The parameters work in 2 ways..
1. Subject Line Prefixes
If you ever worked with a Email integrated ‘Bug tracking System’ then you would know what I am talking about..
Subject line code work the following way..
For example say we create a code for sales.. sales# .. this basically means that if a subject has the word ‘sales#’ then the characters following the code (sales#) signify the sales Id the email needs to be related to.
In this project I added two subject line prefix codes.. one for Sales and the other for Customer... so for example if the subject line has the word ‘cust#’ then the characters following the code specify the customer account number.
2. From Email Address
I added a parameter, so that apart from the above option, one can also search for the record (custTanble or ContactPerson) using the email address of the sender.
Note: you might want to revisit indexes on custTable and Contact person if you like to take this path.
Ohh... in addition to that, if a match is found.. i.e. we find a email that should be attached to a ax record, then after importing the mail into ax, the routine transfer the mail to a folder (specified in the parameters table), hence the mail wont be searched by the routine again.
I dont think I need to say any more.. the code is pretty much self explanatory and were ever I deemed necessary I added comments to explain what I was up to ..
Note: I would like to point out that this is totally untested code, and there are no warranties or guarantees that I or my company provide on the usage of this code... this code was written to show one of the many ways in which ax integrates with Office Systems and was not meant to be used in a production environment.

X++:
class mrOutlookImporter 
{ 
mrOutlookImportParameters outLookPram; 
docuRef docuRef; 
Microsoft.Office.Interop.Outlook.MailItemClass mailItemClass; 
} 
void getMails() 
{ 
Microsoft.Office.Interop.Outlook._Application outAppl; 
Microsoft.Office.Interop.Outlook.MAPIFolder mapiFolder; 
Microsoft.Office.Interop.Outlook.NameSpaceClass Nspace; 
Microsoft.Office.Interop.Outlook.FoldersClass foldersClass; 
Microsoft.Office.Interop.Outlook.ItemsClass itemClass; 
Microsoft.Office.Interop.Outlook.MAPIFolder destinationFolder; 
//str dd; 
str folderName; 
str mailSubject; 
str fromEmail; 
str entryId; 
str storeId; 
int numOfEmails; 
int numOfFolders; 
boolean copyToDestination = false; 
; 
select firstonly outLookPram; 
new InteropPermission(InteropKind::ClrInterop).assert(); 
outAppl = new Microsoft.Office.Interop.Outlook.ApplicationClass(); 
nSpace = outAppl.GetNamespace('Mapi'); 
mapiFolder = nSpace.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders::olFolderInbox); 
foldersClass = mapiFolder.get_Folders(); 
numOfFolders = foldersClass.get_Count(); 
destinationFolder = foldersClass.GetFirst(); 
while(numOfFolders > 0) 
{ 
folderName = destinationFolder.get_Name(); 
if(folderName == outLookPram.outlookFolder) 
{ 
copyToDestination = true; 
break; 
} 
destinationFolder = foldersClass.GetNext(); 
numOfFolders--; 
} 
if(!copyToDestination) 
{ 
try 
{ 
foldersClass.Add(outLookPram.outlookFolder,Microsoft.Office.Interop.Outlook.OlDefaultFolders::olFolderInbox); 
copyToDestination = true; 
} 
catch(Exception::CLRError) 
{ 
error('cannot create folder'); 
} 
} 
itemClass = mapifolder.get_Items(); 
mailItemClass = itemClass.GetFirst(); 
numOfEmails = itemClass.get_Count(); 
while( numOfEmails > 0) 
{ 
try 
{ 
numOfEmails--; 
mailSubject = mailItemClass.get_Subject(); 
fromEmail = mailItemClass.get_SenderEmailAddress(); 
entryId = mailItemClass.get_EntryID(); 
storeId = mapifolder.get_StoreID(); 
if(copyToDestination && this.validateEmail(mailSubject)) 
{ 
if(this.importEmail(entryId,storeId)) 
mailItemClass.Move(destinationFolder); 
} 
else if(copyToDestination && outLookPram.checkUsingEmailAddress == NoYes::Yes) 
{ 
if(this.findAndSetDocuref('email',fromEmail,mailSubject)) 
{ 
if(this.importEmail(entryId,storeId)) 
mailItemClass.Move(destinationFolder); 
} 
} 
// one might consider moving a mail to a different folder even if a match isnt found. 
mailItemClass = itemClass.GetNext(); // 
} 
catch(Exception::CLRError) 
{ 
error('cannot read email'); 
continue; 
} 
} 
CodeAccessPermission::revertAssert(); 
} 
boolean validateEmail(str _subject) 
{ 
int firstPos; 
int secondPos; 
; 
//scan sales 
if(strScan(_subject,outLookPram.salesPrefix,1,strlen(_subject))) 
{ 
firstPos = strScan(_subject,outLookPram.salesPrefix,1,strlen(_subject)); 
secondPos = strScan(_subject,'',firstPos,strlen(_subject)); 
return this.findAndSetDocuref('sales',substr(_subject,firstPos,(secondPos-FirstPos)),_subject); 
} 
else if(strScan(_subject,outLookPram.custPrefix,1,strlen(_subject))) 
{ 
firstPos = strScan(_subject,outLookPram.custPrefix,1,strlen(_subject)); 
secondPos = strScan(_subject,'',firstPos,strlen(_subject)); 
return this.findAndSetDocuref('Cust',substr(_subject,firstPos,(secondPos-FirstPos)),_subject); 
} 
return false; 
} 
boolean findAndSetDocuref(str _type,str _num,str _subject) 
{ 
SalesTable salesTable; 
CustTable custTable; 
ContactPerson contactPerson; 
salesId salesId; 
custAccount custAccount; 
email email; 
; 
this.initDocuref(_subject); 
Switch(_type) 
{ 
Case 'sales' : 
{ 
salesId = _num; 
select firstonly recid,ContactPersonId from salesTable where salesTable.SalesId == salesId; 
if(salesTable.recid) 
{ 
docuref.RefRecId = salesTable.RecId; 
docuRef.RefTableId = tablenum(SalesTable); 
docuref.ContactPersonId = salesTable.ContactPersonId; 
return true; 
} 
break; 
} 
Case 'cust' : 
{ 
custAccount = _num; 
select firstonly recid,ContactPersonId from custtable where custTable.AccountNum == custAccount; 
if(custTable.RecId) 
{ 
docuref.RefRecId = custTable.RecId; 
docuRef.RefTableId = tablenum(custTable); 
docuref.ContactPersonId = custTable.ContactPersonId; 
return true; 
} 
break; 
} 
Case 'email' : 
{ 
email = _num; 
select firstonly ContactPersonId,recid from contactPerson 
where ( contactPerson.Email == email 
|| contactPerson.Email2 == email 
|| contactPerson.Email3 == email); 
if(contactPerson.ContactPersonId) 
{ 
docuref.RefRecId = contactPerson.RecId; 
docuRef.RefTableId = tablenum(contactPerson); 
docuref.ContactPersonId = contactPerson.ContactPersonId; 
return true; 
} 
select firstonly RecId,ContactPersonId from custTable where custTable.Email == email; 
if(custTable.AccountNum) 
{ 
docuref.RefRecId = custTable.RecId; 
docuRef.RefTableId = tablenum(custTable); 
docuref.ContactPersonId = custTable.ContactPersonId; 
return true; 
} 
} 
} 
return false; 
} 
boolean initDocuref(str _subject) 
{ 
smmDragDropObjectType smmDragDropObjectType = smmDragDropObjectType::InMail; 
; 
docuRef.ActualCompanyId = curext(); 
docuref.AuthorId = smmUtility::getCurrentContact(); 
docuref.Name = _subject; 
docuRef.TypeId = DocuType::findDroppedObjectType(smmDragDropObjectType); 
} 
boolean importEmail(str _entryId,str _storeId) 
{ 
DocuType docuType; 
DocuValue docuValue; 
NumberSeq numSeq; 
str tofilename; 
int numOfAttachments; 
FilePath archivePath; 
int lines = infolog.line(); 
Query q; 
; 
if (!_entryId || !_storeId) 
{ 
return checkFailed("@SYS87269"); 
} 
docuType = DocuType::find(docuRef.TypeId); 
// Check that the document file location isn't set to "Original location" in the document 
if (docuType.FilePlace == DocuFilePlace::NoCopy) 
{ 
// File location can not be set up to Orginal location for the Outlook mail types 
return checkFailed("@SYS87271"); 
} 
// Is archive set up to be in a Party (do not save in database) 
if (smmDocuments::mustArchiveFiles(docuType)) 
{ 
// Get archive path 
archivePath = docuType.ArchivePath; 
if (archivePath && ! WinAPI::pathExists(archivePath)) 
{ 
q = new Query(); 
q.addDataSource(tablenum(DocuType)).addRange(fieldnum(DocuType,TypeId)).value(queryValue(docuType.TypeId)); 
return checkFailed(strfmt("@SYS90175",archivePath),'', 
SysInfoAction_FormrunQuery::newFormnameControlnameQuery( 
formstr(docuType), 
identifierstr(Setup_ArchivePath),q)); 
} 
// If no path to set up on the document type the general archive path is used instead 
if (! archivePath) 
{ 
archivePath = Docu::archivePath(docuType.DataAreaId); 
} 
} 
else 
{ 
// The Windows path is used because the Outlook mail must be written somewhere on disk as a temp file before it can be saved in the database 
archivePath = WinAPI::getWindowsDirectory(); 
} 
// Get a new number from the document numbersequence 
numSeq = NumberSeq::newGetNum(DocuParameters::numRefDocuNumber(), true); 
// Use next number in numbersequence as filename 
docuValue.FileName = smmDocuments::getNonExistingFileName(numSeq, archivePath, 'msg'); 
// Save mail as the msg type file 
docuValue.FileType = 'msg'; 
// If path doesn't end with to backslash it should be appended 
archivePath = Docu::fileCheckPath(archivePath); 
docuValue.Path = archivePath; 
// Create filename based on the path in the document type table and the filename (number) 
tofilename = docuValue.Path + docuValue.FileName + (docuValue.FileType ? ('.' + docuValue.FileType) : ''); 
// Save the e-mail as a .msg file 
try 
{ 
// Call save function on the Outlook mail item 
mailItemClass.SaveAs(tofilename,Microsoft.Office.Interop.Outlook.OlDefaultFolders::olFolderInbox); 
} 
catch 
{ 
Error('Cannot Save Mail as file'); 
return false; 
} 
// Check that the file was saved correctly 
if (!archivePath || !WinAPI::fileExists(tofilename)) 
{ 
// Free the document numbersequence number that was reserved if the file wasn't found 
numSeq.abort(); 
// The file could not be saved. Please check the path in the document type setup. 
return checkFailed("@SYS86983"); 
} 
// Create the file reference 
docuValue.insert(); 
// Mark the document numbersequence number is used 
numSeq.used(); 
// Create the document 
docuRef.ValueRecId = docuValue.RecId; 
docuRef.Notes = ''; 
docuRef.SmmEMailEntryID = _entryID; 
docuRef.SmmEMailStoreID = _storeID; 
docuRef.insert(); 
ttscommit; 
// Should the file be stored in the Axapta database? 
if (docuType.FilePlace == DocuFilePlace::Database) 
{ 
// Copy the file to the database 
docuValue = DocuValue::writeDocuValue(docuRef, tofilename); 
// Delete the temp file that was placed in the Windows Party 
WinAPI::deleteFile(toFileName); 
} 
return true; 
}
- Mohammed Rasheed www.dynamic-ax.co.uk   Technorati Tags: AX-Outlook,dynamics ax 2009,Outlook 2007






Источник: http://dynamic-ax.spaces.live.com/Bl...4DE3!411.entry
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору.
Старый 03.07.2009, 07:17   #2  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от Blog bot Посмотреть сообщение
X++:
...
return this.findAndSetDocuref('sales',substr(_subject,firstPos,(secondPos-FirstPos)),_subject); 
...
return this.findAndSetDocuref('Cust',substr(_subject,firstPos,(secondPos-this.initDocuref(_subject); 
...
Case 'sales' : 
...
Case 'cust' : 
...
Case 'email' : 
...
Я, конечно, не специалист в программировании Outlook.
Но интересно, как к такому коду отнесутся неанглоязычные версии Аутлука?
И различает ли Аутлук большие и маленькие буквы?

Что-то с подозрением отношусь я к подобному коду, в котором содержатся магические константы.
__________________
полезное на axForum, github, vk, coub.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
gatesasbait: Dynamics AX 2009 SSRS and SSAS Integration Tips Blog bot DAX Blogs 3 09.07.2009 13:07
AX UK: Microsoft Dynamics AX 2009 Management Pack for Systems Center Operations Manager (SCOM) 2007 Blog bot DAX Blogs 0 23.06.2009 03:05
DynamicsAxSCM: Changes in Sales and Transfer Order Picking from Microsoft Dynamics AX 4.0 to Dynamics AX 2009 Blog bot DAX Blogs 0 18.05.2009 02:05
Dynamics AX: Managing Your Supply Chain Using Microsoft Dynamics AX 2009 - Book Review Blog bot DAX Blogs 0 31.03.2009 23:06
axStart: Microsoft Dynamics AX 2009 Hot Topics Web Seminar Series Blog bot DAX Blogs 0 06.08.2008 12:05

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 13:59.