📜 ⬆️ ⬇️

How to use the function of processing incoming mail in SharePoint 2010 - a practical example

Often the company's business process includes the need to process documentation containing bar codes, and then transfer them to a certain accounting system. This is true, for example, for the sphere of railway transportation and airlines: in the coupon barcode sent by e-mail agents of the company, ticket numbers are protected. Operators are forced to process and deposit thousands of such coupons on a daily basis into the internal accounting system.

Work routine, human factors provoke errors. How to automate the process and save the operator from the need for manual processing of letters and their attachments? We found a simple solution using MS SharePoint. As usual, we tried to make the most of the existing system functionality, the existing library and a little of our programmer magic.
Exchange Setup

If you read the documentation and set everything up correctly, then everything is done for you in a few clicks in the settings of the document library:


')


On the server with SharePoint, the mail server is configured to receive emails. Incoming letters in the form of eml-files appear in the file system in the inbox folder. SharePoint every two minutes checks the inbox folder, and if there are files there, it processes and deletes them.

It is worth noting that all files (with the wrong recipient address, with incorrect additional tags or created using Outlook Express) will not be processed - the file must be created by the SMTP server of IIS.

As a result, all letters arriving at this address fall into the library of documents:



Each letter contains one or several attachments in jpg, png, tiff, pdf formats, which may contain one or several documents marked with a bar code:



For barcode recognition, we used the zxing open source library, with which we can scan and process different types of barcodes. Here we had to slightly modify the capabilities of the library, or rather, the terminating symbol algorithm, since the customer uses their own specific encoding, which is not supported in zxing (although similar to codabar).

Next, we implemented an Event Handler, using zxing every 15 minutes, defining documents in email attachments that automatically reach the portal via Exchange. Then he takes them through the library, pulls out the attachments, the sender's address and the sender's code (the personal code of the agent of the company).

Caution! There is a lot of routine code on how to pull out the mail data through CDO and work with its Stream.
Code
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using ADODB; using CDO; using ETR.REBT.BarcodeReader; using Stream = System.IO.Stream; namespace ETR.BusinessLogic.EBTBlanks { internal class EBTMailParser { private static readonly string[] SupportedExtensions = { "application/pdf" , "image/jpeg" , "image/bmp" , "image/png" , "image/tiff" }; private static readonly Regex AgencyCodePattern = new Regex(@"{\w*}"); public static EBTMailParseResult ParseEmail(Stream emlStream) { var result = new EBTMailParseResult { Success = true }; Message msg = new MessageClass(); ADODB.Stream stream = new StreamClass(); try { CopyStream(emlStream, stream); msg.DataSource.OpenObject(stream, "_Stream"); result.FromAddress = msg.From; result.SendDate = msg.SentOn; result.ReceivedDate = msg.ReceivedTime; string agentCode; if (TryParseAgencyCode(msg.Subject, out agentCode)) { result.AgencyCode = agentCode; } else { result.Status = "    .      ,        . , {12345}"; return result; } if (msg.Attachments.Count == 0) { result.Status = "      "; return result; } result.Attachments = ParseAttachments(msg.Attachments).ToList(); return result; } catch (Exception ex) { //If we get unknown error - we don't mark letter as parsed an try next time result.Success = false; result.Status = " ."; result.ExceptionMessage = ex.Message; return result; } finally { stream.Close(); } } private static bool TryParseAgencyCode(string subject, out string result) { var allMatchResults = AgencyCodePattern.Matches(subject); if (allMatchResults.Count != 1 || false == allMatchResults[0].Success) { result = null; return false; } result = allMatchResults[0].Value.Substring(1, allMatchResults[0].Value.Length - 2); return true; } private static void CopyStream(Stream netStream, ADODB.Stream adoStream) { //adoStream.Open(Type.Missing, ADODB.ConnectModeEnum.adModeUnknown, ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified, String.Empty, String.Empty); adoStream.Type = StreamTypeEnum.adTypeBinary; adoStream.Open(); netStream.Position = 0; var buffer = new byte[1024]; while (netStream.Read(buffer, 0, buffer.Length) != 0) { adoStream.Write(buffer); } adoStream.Flush(); } private static void CopyStream(ADODB.Stream adoStream, Stream netStream) { while (!adoStream.EOS) { var bytes = (byte[])adoStream.Read(1024); netStream.Write(bytes, 0, bytes.Length); } netStream.Flush(); } private static IEnumerable<EBTAttachmentParseResult> ParseAttachments(IBodyParts attachments) { var barcodeReader = new BarcodeReader(true); for (var i = 1; i <= attachments.Count; i++) { var attachment = attachments[i]; var fileResult = new EBTAttachmentParseResult { FileName = attachment.FileName }; if (false == SupportedExtensions.Any(ct => ct == attachment.ContentMediaType)) { fileResult.Status = String.Format(" {0}  ", attachment.ContentMediaType); yield return fileResult; } var stream = attachment.GetDecodedContentStream(); try { var memoryStream = new MemoryStream(); CopyStream(stream, memoryStream); memoryStream.Position = 0; var parseResult = barcodeReader.Decode(memoryStream, attachment.FileName); fileResult.Status = parseResult.AllPages > parseResult.RecognizedPages ? String.Format(" {0}  {1} ", parseResult.RecognizedPages, parseResult.AllPages) : fileResult.Status; fileResult.Stream = memoryStream; if (parseResult.ResultList != null && parseResult.ResultList.Count > 0) { fileResult.BarcodeNumbers = parseResult.ResultList.Select(b => ParseBarcode(b.Text)).ToList(); } } catch (Exception ex) { fileResult.Status = " ."; fileResult.ExceptionMessage = ex.Message; } finally { stream.Close(); } yield return fileResult; } } private static EBTBarcodeParseResult ParseBarcode(string barCode) { if (barCode.Length != 15) return new EBTBarcodeParseResult { BarcodeNumber = barCode, Status = "    " }; return new EBTBarcodeParseResult { BarcodeNumber = barCode, CouponNumber = barCode.Substring(1, 13).Insert(3, " ") }; } } } 


If the handler finds any error (incorrect file extension, agent code is not recognized, etc.), an event with its description is registered in the EventBus.

If there are no errors, the attachments are sorted through the library, barcodes are pulled out of them, from which information is read. For each specific barcode, a form is created in SharePoint, which is already processed by the operators when they receive the original documents.



Finally, an event is sent to EventBus with a message stating that the letter has been parsed, information on how many files were found in it, how many coupons were attached, and how many tickets were in coupons. All interested parties receive a push notification.



This business process is a small part of the large office work of a large transport company. It can also be easily translated to other areas where it is necessary to process incoming documents in a lot and routinely, and where SharePoint and Exchange are used.

Our postulate in such conditions is not to stop the improvement of the existing system, to automate all the routines, using the available tools, ingenuity and our programming skills.

Source: https://habr.com/ru/post/211669/


All Articles