Codeunit 1294 OCR Service Mgt.
- App
- Base Application
- Namespace
- Microsoft.EServices.EDocument
- Versions
- 17-28
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/eServices/EDocument/OCRServiceMgt.Codeunit.al1339 lines, Copyright (c) Microsoft Corporation. MIT
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.EServices.EDocument;
using Microsoft.CRM.Outlook;
using Microsoft.Purchases.Vendor;
using Microsoft.Utilities;
using System;
using System.Integration;
using System.IO;
using System.Telemetry;
using System.Utilities;
using System.Xml;
codeunit 1294 "OCR Service Mgt."
{
trigger OnRun()
begin
end;
var
MissingCredentialsQst: Label '%1\ Do you want to open %2 to specify the missing values?', Comment = '%1=error message. %2=OCR Service Setup';
MissingCredentialsErr: Label 'You must fill the User Name, Password, and Authorization Key fields.', Comment = '%1 = OCR Service Setup';
OCRServiceSetup: Record "OCR Service Setup";
AuthCookie: DotNet Cookie;
ConnectionSuccessMsg: Label 'Connection succeeded.';
ConnectionFailedErr: Label 'The connection failed. Check that the User Name, Password, and Authorization Key fields are filled correctly.';
NoFileContentErr: Label 'The file is empty.';
InitiateUploadMsg: Label 'Initiate document upload.';
GetDocumentConfirmMsg: Label 'Acknowledge document receipt.';
DocumentDownloadedTxt: Label 'The document was downloaded. Document ID: %1, Track ID: %2', Comment = '%1 = Document Identifier (usually a guid), %2 = Track ID';
UploadFileMsg: Label 'Send to OCR service.';
AuthenticateMsg: Label 'Log in to OCR service.';
GetNewDocumentsMsg: Label 'Get received OCR documents.';
GetDocumentMsg: Label 'Receive OCR document.';
UploadFileFailedMsg: Label 'The document failed to upload. Service Error: %1', Comment = '%1 = Response from OCR service, this will probably be an XML string';
UploadFileFailedTelemetryMsg: Label 'The document failed to upload. Service Error: %1', Locked = true;
UploadFileFailedWithNoResponseMsg: Label 'The document failed to upload. The OCR service returned no response.', Locked = true;
UploadTotalSuccessMsg: Label 'Notify OCR service that %1 documents are ready for upload.', Comment = '%1 = Number of documents to be uploaded';
NewDocumentsTotalMsg: Label 'Downloaded %1 of %2 documents.', Comment = '%1 = Number of documents downloaded (e.g. 5), %2 = Number of documents processed';
DocumentNotDownloadedTxt: Label 'Could not download the document from the OCR service. Document ID: %1, Track ID: %2', Comment = '%1 = Document ID, %2 = Track ID';
DownloadNotRegisteredTxt: Label 'Could not register that the document was downloaded from the OCR service. Document ID: %1, Track ID: %2', Comment = '%1 = Document ID, %2 = Track ID';
ImportSuccessMsg: Label 'The document was successfully received.';
DocumentNotReadyMsg: Label 'The document cannot be received yet. Try again in a few moments.';
NotUploadedErr: Label 'You must upload the image first.';
NotValidDocIDErr: Label 'Received document ID %1 contains invalid characters.', Comment = '%1 is the value.';
LoggingConstTxt: Label 'OCR Service';
UploadSuccessMsg: Label 'The document was successfully sent to the OCR service.';
NoOCRDataCorrectionMsg: Label 'You have made no OCR data corrections.';
VerifyMsg: Label 'The document is awaiting your manual verification on the OCR service site.\\Choose the Awaiting Verification link in the OCR Status field.';
FailedMsg: Label 'The document failed to be processed.';
MethodGetTok: Label 'GET', Locked = true;
MethodPutTok: Label 'PUT', Locked = true;
MethodPostTok: Label 'POST', Locked = true;
MethodDeleteTok: Label 'DELETE', Locked = true;
OCRServiceUserSuccessfullyUploadedDocumentTxt: Label 'A document was successfully uploaded to OCR service.', Locked = true;
OCRServiceUserFailedToUploadDocumentTxt: Label 'A document upload to OCR Service failed.', Locked = true;
OCRServiceUserSuccessfullyUploadedLearningDocumentTxt: Label 'A learning document was successfully uploaded to OCR service.', Locked = true;
OCRServiceUserFailedToUploadLearningDocumentTxt: Label 'A learning document upload to OCR Service failed.', Locked = true;
OCRServiceUserSuccessfullyDownloadedDocumentTxt: Label 'A document was successfully downloaded from OCR service.', Locked = true;
OCRServiceUserFailedToDownloadDocumentTxt: Label 'A document download from OCR service failed.', Locked = true;
OCRServiceUserCreatedGenJnlLineOutOfOCRedDocumentTxt: Label 'A general journal line was successfully created out of an OCRed incoming document.', Locked = true;
OCRServiceUserCreatedInvoiceOutOfOCRedDocumentTxt: Label 'An invoice was successfully created out of an OCRed incoming document.', Locked = true;
ConnectionFailedTxt: Label 'Connection to OCR service failed.', Locked = true;
ConnectionSucceedTxt: Label 'Successfully connected to OCR service.', Locked = true;
GettingCurrentCustomerFailedTxt: Label 'Getting current customer from OCR service failed.', Locked = true;
GettingCurrentUserFailedTxt: Label 'Getting current user from OCR service failed.', Locked = true;
GettingUserConfigurationFailedTxt: Label 'Getting user configuration from OCR service failed.', Locked = true;
InitializingUploadFailedTxt: Label 'Initializing upload to OCR service failed.', Locked = true;
NoFileContentTxt: Label 'The file is empty.', Locked = true;
GettingDocumentsForUserFailedTxt: Label 'Getting documents for the user from OCR service failed.', Locked = true;
GettingDocumentsForCustomerFailedTxt: Label 'Getting documents for the customer from OCR service failed.', Locked = true;
GettingBatchDocumentsFailedTxt: Label 'Getting batch documents from OCR service failed.', Locked = true;
GettingBatchesFailedTxt: Label 'Getting batches from OCR service failed.', Locked = true;
RegisteringDownloadFailedTxt: Label 'Registering download to OCR service failed.', Locked = true;
RegisteringDownloadSucceedTxt: Label 'Registering download to OCR service succeed.', Locked = true;
InsertingIncomingDocumentTxt: Label 'Inserting incoming document.', Locked = true;
UpdatingIncomingDocumentTxt: Label 'Updating incoming document.', Locked = true;
InvalidDocumentIdTxt: Label 'Invalid document ID was received from OCR service.', Locked = true;
DocumentsDownloadedTxt: Label '%1 of %2 documents were successfully downloaded from OCR service.', Locked = true;
DocumentNotUploadedTxt: Label 'The document is not uploaded.', Locked = true;
CannotFindAttachmentTxt: Label 'Cannot find attachment.', Locked = true;
UploadFileSucceedTxt: Label 'A document was successfully uploaded to OCR service.', Locked = true;
FailedRequestResultTxt: Label 'Request to OCR service failed. Status code: %1. Message: %2. Details: %3.', Locked = true;
FailedRequestBodyTxt: Label 'Request to OCR service failed. Method: %1. URL: %2. Body: %3', Locked = true;
TelemetryCategoryTok: Label 'AL OCR Service', Locked = true;
procedure SetURLsToDefaultRSO(var OCRServiceSetup: Record "OCR Service Setup")
begin
OCRServiceSetup."Sign-up URL" := 'https://store.readsoftonline.com/nav';
OCRServiceSetup."Service URL" := 'https://services.readsoftonline.com';
OCRServiceSetup."Sign-in URL" := 'https://nav.readsoftonline.com';
end;
procedure CheckCredentials()
var
OCRServiceSetup: Record "OCR Service Setup";
begin
if not HasCredentials(OCRServiceSetup) then
if Confirm(StrSubstNo(GetCredentialsQstText()), true) then begin
Commit();
PAGE.RunModal(PAGE::"OCR Service Setup", OCRServiceSetup);
end;
if not HasCredentials(OCRServiceSetup) then
Error(GetCredentialsErrText());
end;
local procedure HasCredentials(OCRServiceSetup: Record "OCR Service Setup"): Boolean
begin
exit(
OCRServiceSetup.Get() and
OCRServiceSetup.HasPassword(OCRServiceSetup."Password Key") and
OCRServiceSetup.HasPassword(OCRServiceSetup."Authorization Key") and
(OCRServiceSetup."User Name" <> ''));
end;
procedure GetCredentialsErrText(): Text
begin
exit(MissingCredentialsErr);
end;
procedure GetCredentialsQstText(): Text
var
OCRServiceSetup: Record "OCR Service Setup";
begin
exit(StrSubstNo(MissingCredentialsQst, GetCredentialsErrText(), OCRServiceSetup.TableCaption()));
end;
[Scope('OnPrem')]
procedure Authenticate(): Boolean
var
AuthenticationSucceeded: Boolean;
begin
if not TryAuthenticate(AuthenticationSucceeded) then begin
Session.LogMessage('00008K8', ConnectionFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
if not AuthenticationSucceeded then begin
Session.LogMessage('00008K9', ConnectionFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailed(OCRServiceSetup.RecordId, AuthenticateMsg, ConnectionFailedErr); // throws error
end;
Session.LogMessage('00008KA', ConnectionSucceedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, AuthenticateMsg, '');
exit(true);
end;
[TryFunction]
[NonDebuggable]
local procedure TryAuthenticate(var AuthenticationSucceeded: Boolean)
var
TempBlob: Codeunit "Temp Blob";
HttpWebRequestMgt: Codeunit "Http Web Request Mgt.";
HttpStatusCode: DotNet HttpStatusCode;
ResponseHeaders: DotNet NameValueCollection;
InStr: InStream;
ResponseString: Text;
ResponseReceived: Boolean;
begin
GetOcrServiceSetup(false);
HttpWebRequestMgt.Initialize(StrSubstNo('%1/authentication/rest/authenticate', OCRServiceSetup."Service URL"));
HttpWebRequestMgt.DisableUI();
RsoAddHeaders(HttpWebRequestMgt);
HttpWebRequestMgt.SetMethod(MethodPostTok);
HttpWebRequestMgt.AddBodyAsText(
StrSubstNo(
'<AuthenticationCredentials><UserName>%1</UserName><Password>%2</Password>' +
'<AuthenticationType>SetCookie</AuthenticationType></AuthenticationCredentials>',
OCRServiceSetup."User Name", OCRServiceSetup.GetPasswordAsSecretText(OCRServiceSetup."Password Key").Unwrap()));
TempBlob.CreateInStream(InStr);
ResponseReceived := HttpWebRequestMgt.GetResponse(InStr, HttpStatusCode, ResponseHeaders);
if ResponseReceived then begin
InStr.ReadText(ResponseString);
AuthenticationSucceeded := StrPos(ResponseString, '<Status>Success</Status>') >= 1;
end else
Error(GetLastErrorText);
if AuthenticationSucceeded then
HttpWebRequestMgt.GetCookie(AuthCookie);
end;
[Scope('OnPrem')]
procedure UpdateOrganizationInfo(var OCRServiceSetup: Record "OCR Service Setup")
var
XMLDOMManagement: Codeunit "XML DOM Management";
XMLRootNode: DotNet XmlNode;
XMLNode: DotNet XmlNode;
ResponseStr: InStream;
begin
if not RsoGetRequest('accounts/rest/currentcustomer', ResponseStr) then begin
Session.LogMessage('00008KB', GettingCurrentCustomerFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
Error(GetLastErrorText);
end;
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
if XMLDOMManagement.FindNode(XMLRootNode, 'Id', XMLNode) then
OCRServiceSetup."Customer ID" := CopyStr(XMLNode.InnerText, 1, MaxStrLen(OCRServiceSetup."Customer ID"));
if XMLDOMManagement.FindNode(XMLRootNode, 'Name', XMLNode) then
OCRServiceSetup."Customer Name" := CopyStr(XMLNode.InnerText, 1, MaxStrLen(OCRServiceSetup."Customer Name"));
if XMLDOMManagement.FindNode(XMLRootNode, 'ActivationStatus', XMLNode) then
OCRServiceSetup."Customer Status" := CopyStr(XMLNode.InnerText, 1, MaxStrLen(OCRServiceSetup."Customer Status"));
if not RsoGetRequest('users/rest/currentuser', ResponseStr) then
Session.LogMessage('00008KC', GettingCurrentUserFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
if XMLDOMManagement.FindNode(XMLRootNode, 'OrganizationId', XMLNode) then
OCRServiceSetup."Organization ID" := CopyStr(XMLNode.InnerText, 1, MaxStrLen(OCRServiceSetup."Organization ID"));
OCRServiceSetup.Modify();
end;
[Scope('OnPrem')]
procedure UpdateOcrDocumentTemplates()
var
OCRServiceDocumentTemplate: Record "OCR Service Document Template";
XMLDOMManagement: Codeunit "XML DOM Management";
XMLRootNode: DotNet XmlNode;
XMLNode: DotNet XmlNode;
XMLNode2: DotNet XmlNode;
ResponseStr: InStream;
begin
GetOcrServiceSetup(false);
OCRServiceSetup.TestField("Organization ID");
if not RsoGetRequest(StrSubstNo('accounts/rest/customers/%1/userconfiguration', OCRServiceSetup."Organization ID"), ResponseStr) then
Session.LogMessage('00008KD', GettingUserConfigurationFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
OCRServiceDocumentTemplate.LockTable();
OCRServiceDocumentTemplate.DeleteAll();
foreach XMLNode in XMLRootNode.SelectNodes('AvailableDocumentTypes/UserConfigurationDocumentType') do begin
OCRServiceDocumentTemplate.Init();
XMLNode2 := XMLNode.SelectSingleNode('SystemName');
OCRServiceDocumentTemplate.Code := CopyStr(XMLNode2.InnerText, 1, MaxStrLen(OCRServiceDocumentTemplate.Code));
XMLNode2 := XMLNode.SelectSingleNode('Name');
OCRServiceDocumentTemplate.Name := CopyStr(XMLNode2.InnerText, 1, MaxStrLen(OCRServiceDocumentTemplate.Name));
OCRServiceDocumentTemplate.Insert();
end;
end;
[Scope('OnPrem')]
procedure RsoGetRequest(PathQuery: Text; var ResponseStr: InStream): Boolean
begin
exit(RsoRequest(PathQuery, MethodGetTok, '', ResponseStr));
end;
[TryFunction]
[Scope('OnPrem')]
procedure RsoGetRequestBinary(PathQuery: Text; var ResponseStr: InStream; var ContentType: Text)
var
HttpWebRequestMgt: Codeunit "Http Web Request Mgt.";
HttpStatusCode: DotNet HttpStatusCode;
ResponseHeaders: DotNet NameValueCollection;
begin
GetOcrServiceSetup(true);
HttpWebRequestMgt.Initialize(StrSubstNo('%1/%2', OCRServiceSetup."Service URL", PathQuery));
HttpWebRequestMgt.DisableUI();
RsoAddCookie(HttpWebRequestMgt);
RsoAddHeaders(HttpWebRequestMgt);
HttpWebRequestMgt.SetMethod(MethodGetTok);
HttpWebRequestMgt.CreateInstream(ResponseStr);
HttpWebRequestMgt.GetResponse(ResponseStr, HttpStatusCode, ResponseHeaders);
ContentType := ResponseHeaders.Item('Content-Type');
end;
[Scope('OnPrem')]
procedure RsoPutRequest(PathQuery: Text; Data: Text; var ResponseStr: InStream): Boolean
begin
exit(RsoRequest(PathQuery, MethodPutTok, Data, ResponseStr));
end;
[Scope('OnPrem')]
procedure RsoPostRequest(PathQuery: Text; Data: Text; var ResponseStr: InStream): Boolean
begin
exit(RsoRequest(PathQuery, MethodPostTok, Data, ResponseStr));
end;
[Scope('OnPrem')]
procedure RsoDeleteRequest(PathQuery: Text; Data: Text; var ResponseStr: InStream): Boolean
begin
exit(RsoRequest(PathQuery, MethodDeleteTok, Data, ResponseStr));
end;
[Scope('OnPrem')]
procedure RsoRequest(PathQuery: Text; RequestAction: Code[6]; BodyText: Text; var ResponseStr: InStream): Boolean
var
HttpWebRequestMgt: Codeunit "Http Web Request Mgt.";
HttpStatusCode: DotNet HttpStatusCode;
ResponseHeaders: DotNet NameValueCollection;
begin
GetOcrServiceSetup(true);
HttpWebRequestMgt.Initialize(StrSubstNo('%1/%2', OCRServiceSetup."Service URL", PathQuery));
HttpWebRequestMgt.DisableUI();
RsoAddCookie(HttpWebRequestMgt);
RsoAddHeaders(HttpWebRequestMgt);
HttpWebRequestMgt.SetMethod(RequestAction);
if BodyText <> '' then
HttpWebRequestMgt.AddBodyAsText(BodyText);
HttpWebRequestMgt.CreateInstream(ResponseStr);
exit(HttpWebRequestMgt.GetResponse(ResponseStr, HttpStatusCode, ResponseHeaders));
end;
[Scope('OnPrem')]
procedure RsoRequest(PathQuery: Text; RequestAction: Code[6]; RequestBody: Text; var ResponseBody: Text; var ErrorMessage: Text; var ErrorDetails: Text; var StatusCode: Integer): Boolean
var
HttpWebRequestMgt: Codeunit "Http Web Request Mgt.";
ResponseHeaders: DotNet NameValueCollection;
HttpStatusCode: DotNet HttpStatusCode;
RequestUrl: Text;
Result: Boolean;
begin
GetOcrServiceSetup(true);
RequestUrl := StrSubstNo('%1/%2', OCRServiceSetup."Service URL", PathQuery);
HttpWebRequestMgt.Initialize(RequestUrl);
HttpWebRequestMgt.DisableUI();
RsoAddCookie(HttpWebRequestMgt);
RsoAddHeaders(HttpWebRequestMgt);
HttpWebRequestMgt.SetMethod(RequestAction);
if RequestBody <> '' then
HttpWebRequestMgt.AddBodyAsText(RequestBody);
Result := HttpWebRequestMgt.SendRequestAndReadTextResponse(ResponseBody, ErrorMessage, ErrorDetails, HttpStatusCode, ResponseHeaders);
if not Result then begin
StatusCode := HttpStatusCode;
Session.LogMessage('0000BBJ', StrSubstNo(FailedRequestResultTxt, StatusCode, ErrorMessage, ErrorDetails), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
Session.LogMessage('0000BBK', StrSubstNo(FailedRequestBodyTxt, RequestAction, RequestUrl, RequestBody), Verbosity::Warning, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
end;
exit(Result);
end;
local procedure RsoAddHeaders(var HttpWebRequestMgt: Codeunit "Http Web Request Mgt.")
begin
HttpWebRequestMgt.AddHeader('x-rs-version', '2011-10-14');
HttpWebRequestMgt.AddHeader('x-rs-key', OCRServiceSetup.GetPasswordAsSecretText(OCRServiceSetup."Authorization Key"));
HttpWebRequestMgt.AddHeader('x-rs-culture', 'en-US');
HttpWebRequestMgt.AddHeader('x-rs-uiculture', 'en-US');
end;
local procedure RsoAddCookie(var HttpWebRequestMgt: Codeunit "Http Web Request Mgt.")
begin
if IsNull(AuthCookie) then
if not Authenticate() then
Error(GetLastErrorText);
if AuthCookie.Expired then
if not Authenticate() then
Error(GetLastErrorText);
HttpWebRequestMgt.SetCookie(AuthCookie);
end;
local procedure URLEncode(InText: Text): Text
var
SystemWebHttpUtility: DotNet HttpUtility;
begin
SystemWebHttpUtility := SystemWebHttpUtility.HttpUtility();
exit(SystemWebHttpUtility.UrlEncode(InText));
end;
procedure DateConvertYYYYMMDD2XML(YYYYMMDD: Text): Text
begin
if StrLen(YYYYMMDD) <> 8 then
exit(YYYYMMDD);
exit(StrSubstNo('%1-%2-%3', CopyStr(YYYYMMDD, 1, 4), CopyStr(YYYYMMDD, 5, 2), CopyStr(YYYYMMDD, 7, 2)));
end;
procedure DateConvertXML2YYYYMMDD(XMLDate: Text): Text
begin
exit(DelChr(XMLDate, '=', '-'))
end;
internal procedure GetFeatureTelemetryName(): Text
var
OCRServiceTelemetryNameTxt: Label 'Document Exchange', Locked = true;
begin
exit(OCRServiceTelemetryNameTxt);
end;
local procedure GetOcrServiceSetup(VerifyEnable: Boolean)
begin
GetOcrServiceSetupExtended(OCRServiceSetup, VerifyEnable);
end;
procedure GetOcrServiceSetupExtended(var OCRServiceSetup: Record "OCR Service Setup"; VerifyEnable: Boolean)
begin
OCRServiceSetup.Get();
if OCRServiceSetup."Service URL" <> '' then
exit;
if VerifyEnable then
OCRServiceSetup.CheckEnabled();
OCRServiceSetup.TestField("User Name");
OCRServiceSetup.TestField("Service URL");
end;
[Scope('OnPrem')]
procedure StartUpload(NumberOfUploads: Integer): Boolean
var
ResponseStr: InStream;
ResponseText: Text;
begin
if NumberOfUploads < 1 then begin
Session.LogMessage('00008L9', InitializingUploadFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
// Initialize upload
if not RsoGetRequest(StrSubstNo('files/rest/requestupload?targetCount=%1', NumberOfUploads), ResponseStr) then begin
Session.LogMessage('00008KE', InitializingUploadFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailed(OCRServiceSetup.RecordId, InitiateUploadMsg, '');
exit(false); // in case error text is empty
end;
ResponseStr.ReadText(ResponseText);
if ResponseText = '' then begin
Session.LogMessage('00008KF', InitializingUploadFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailed(OCRServiceSetup.RecordId, InitiateUploadMsg, '');
exit(false); // in case error text is empty
end;
Session.LogMessage('00008KG', StrSubstNo(UploadTotalSuccessMsg, NumberOfUploads), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, InitiateUploadMsg, StrSubstNo(UploadTotalSuccessMsg, NumberOfUploads));
exit(true);
end;
[Scope('OnPrem')]
procedure UploadImage(var TempBlob: Codeunit "Temp Blob"; FileName: Text; ExternalReference: Text[50]; Template: Code[20]; LoggingRecordId: RecordID): Boolean
var
HttpRequestURL: Text;
APIPart: Text;
begin
GetOcrServiceSetup(true);
APIPart := StrSubstNo(
'files/rest/image2?filename=%1&customerid=&batchexternalid=%2&buyerid=&documenttype=%3&sortingmethod=OneDocumentPerFile',
URLEncode(FileName), ExternalReference, Template);
HttpRequestURL := StrSubstNo('%1/%2', OCRServiceSetup."Service URL", APIPart);
if UploadFile(TempBlob, HttpRequestURL, '*/*', 'application/octet-stream', LoggingRecordId) then begin
Session.LogMessage('000089H', OCRServiceUserSuccessfullyUploadedDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(true)
end;
Session.LogMessage('000089I', OCRServiceUserFailedToUploadDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
[Scope('OnPrem')]
procedure UploadLearningDocument(var TempBlob: Codeunit "Temp Blob"; DocumentId: Text; LoggingRecordId: RecordID): Boolean
var
HttpRequestURL: Text;
APIPart: Text;
begin
GetOcrServiceSetup(true);
APIPart := StrSubstNo('documents/rest/%1/learningdocument', DocumentId);
HttpRequestURL := StrSubstNo('%1/%2', OCRServiceSetup."Service URL", APIPart);
if UploadFile(TempBlob, HttpRequestURL, '', '', LoggingRecordId) then begin
Session.LogMessage('000089J', OCRServiceUserSuccessfullyUploadedLearningDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(true)
end;
Session.LogMessage('000089K', OCRServiceUserFailedToUploadLearningDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
local procedure UploadFile(var TempBlob: Codeunit "Temp Blob"; HttpRequestURL: Text; HttpRequestReturnType: Text; HttpRequestContentType: Text; LoggingRecordId: RecordID): Boolean
var
HttpWebRequestMgt: Codeunit "Http Web Request Mgt.";
OfficeMgt: Codeunit "Office Management";
HttpStatusCode: DotNet HttpStatusCode;
ResponseHeaders: DotNet NameValueCollection;
ResponseStr: InStream;
ResponseText: Text;
begin
if not TempBlob.HasValue() then begin
Session.LogMessage('00008KH', NoFileContentTxt, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, UploadFileMsg, NoFileContentErr);
LogActivityFailed(LoggingRecordId, UploadFileMsg, NoFileContentErr); // throws error
end;
GetOcrServiceSetup(true);
HttpWebRequestMgt.Initialize(HttpRequestURL);
HttpWebRequestMgt.SetTraceLogEnabled(false); // Activity Log will log for us
HttpWebRequestMgt.DisableUI();
RsoAddCookie(HttpWebRequestMgt);
RsoAddHeaders(HttpWebRequestMgt);
if HttpRequestReturnType <> '' then
HttpWebRequestMgt.SetReturnType(HttpRequestReturnType);
if HttpRequestContentType <> '' then
HttpWebRequestMgt.SetContentType(HttpRequestContentType);
HttpWebRequestMgt.SetMethod(MethodPostTok);
HttpWebRequestMgt.AddBodyBlob(TempBlob);
HttpWebRequestMgt.CreateInstream(ResponseStr);
if not HttpWebRequestMgt.GetResponse(ResponseStr, HttpStatusCode, ResponseHeaders) then begin
if HttpWebRequestMgt.ProcessFaultXMLResponse('', '/ServiceError/Message', '', '') then;
Session.LogMessage('000089L', UploadFileFailedWithNoResponseMsg, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, UploadFileMsg, '');
LogActivityFailed(LoggingRecordId, UploadFileMsg, '');
exit(false); // in case error text is empty
end;
ResponseStr.ReadText(ResponseText);
if ResponseText = '<BoolValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Value>true</Value></BoolValue>' then begin
Session.LogMessage('00008KI', UploadFileSucceedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, UploadFileMsg, '');
LogActivitySucceeded(LoggingRecordId, UploadFileMsg, '');
if GuiAllowed and (not OfficeMgt.IsAvailable()) then
Message(UploadSuccessMsg);
exit(true);
end;
Session.LogMessage('000089M', StrSubstNo(UploadFileFailedTelemetryMsg, ResponseText), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, UploadFileMsg, StrSubstNo(UploadFileFailedMsg, ResponseText));
LogActivityFailed(LoggingRecordId, UploadFileMsg, StrSubstNo(UploadFileFailedMsg, ResponseText)); // throws error
end;
[Scope('OnPrem')]
procedure UploadAttachment(var TempBlob: Codeunit "Temp Blob"; FileName: Text; ExternalReference: Text[50]; Template: Code[20]; RelatedRecordId: RecordID): Boolean
begin
if not TempBlob.HasValue() then begin
Session.LogMessage('00008LA', NoFileContentTxt, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
Error(NoFileContentErr);
end;
if not StartUpload(1) then
exit(false);
exit(UploadImage(TempBlob, FileName, ExternalReference, Template, RelatedRecordId));
end;
[Scope('OnPrem')]
procedure UploadCorrectedOCRFile(IncomingDocument: Record "Incoming Document"): Boolean
var
TempBlob: Codeunit "Temp Blob";
DocumentId: Text;
begin
if not IncomingDocument."OCR Data Corrected" then begin
Message(NoOCRDataCorrectionMsg);
exit;
end;
DocumentId := GetOCRServiceDocumentReference(IncomingDocument);
CorrectOCRFile(IncomingDocument, TempBlob);
if not TempBlob.HasValue() then
Error(NoFileContentErr);
if not StartUpload(1) then
exit(false);
exit(UploadLearningDocument(TempBlob, DocumentId, IncomingDocument.RecordId));
end;
procedure CorrectOCRFile(IncomingDocument: Record "Incoming Document"; var TempBlob: Codeunit "Temp Blob")
var
OCRFileXMLRootNode: DotNet XmlNode;
OutStream: OutStream;
begin
ValidateUpdatedOCRFields(IncomingDocument);
GetOriginalOCRXMLRootNode(IncomingDocument, OCRFileXMLRootNode);
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor Name"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor Invoice No."));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Order No."));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Document Date"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Due Date"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Amount Excl. VAT"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Amount Incl. VAT"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("VAT Amount"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Currency Code"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor VAT Registration No."));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor IBAN"));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor Bank Branch No."));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor Bank Account No."));
CorrectOCRFileNode(OCRFileXMLRootNode, IncomingDocument, IncomingDocument.FieldNo("Vendor Phone No."));
OnCorrectOCRFileOnAfterCorrectOCRFileNodes(OCRFileXMLRootNode, IncomingDocument);
Clear(TempBlob);
TempBlob.CreateOutStream(OutStream);
OCRFileXMLRootNode.OwnerDocument.Save(OutStream);
end;
[Scope('OnPrem')]
procedure CorrectOCRFileNode(var OCRFileXMLRootNode: DotNet XmlNode; IncomingDocument: Record "Incoming Document"; FieldNo: Integer)
var
XMLDOMManagement: Codeunit "XML DOM Management";
CorrectionXMLNode: DotNet XmlNode;
EmptyCorrectionXMLNode: DotNet XmlNode;
CorrectionXMLNodeParent: DotNet XmlNode;
PositionXMLNode: DotNet XmlNode;
IncomingDocumentRecRef: RecordRef;
IncomingDocumentFieldRef: FieldRef;
XPath: Text;
CorrectionValue: Text;
CorrectionNeeded: Boolean;
CorrectionValueAsDecimal: Decimal;
OriginalValueAsDecimal: Decimal;
begin
IncomingDocumentRecRef.GetTable(IncomingDocument);
XPath := IncomingDocument.GetDataExchangePath(FieldNo);
if XPath = '' then
exit;
if XMLDOMManagement.FindNode(OCRFileXMLRootNode, XPath, CorrectionXMLNode) then begin
IncomingDocumentFieldRef := IncomingDocumentRecRef.Field(FieldNo);
case IncomingDocumentFieldRef.Type of
FieldType::Date:
begin
CorrectionValue := DateConvertXML2YYYYMMDD(Format(IncomingDocumentFieldRef.Value, 0, 9));
CorrectionNeeded := CorrectionXMLNode.InnerText <> CorrectionValue;
end;
FieldType::Decimal:
begin
CorrectionValueAsDecimal := IncomingDocumentFieldRef.Value();
CorrectionValue := Format(IncomingDocumentFieldRef.Value, 0, 9);
if Evaluate(OriginalValueAsDecimal, CorrectionXMLNode.InnerText, 9) then;
CorrectionNeeded := OriginalValueAsDecimal <> CorrectionValueAsDecimal;
end;
else begin
CorrectionValue := Format(IncomingDocumentFieldRef.Value, 0, 9);
CorrectionNeeded := CorrectionXMLNode.InnerText <> CorrectionValue;
end;
end;
if CorrectionNeeded then begin
if XMLDOMManagement.FindNode(CorrectionXMLNode, '../Position', PositionXMLNode) then
PositionXMLNode.InnerText := '0, 0, 0, 0';
if CorrectionValue = '' then begin
CorrectionXMLNodeParent := CorrectionXMLNode.ParentNode;
EmptyCorrectionXMLNode := CorrectionXMLNodeParent.OwnerDocument.CreateElement(CorrectionXMLNode.Name);
CorrectionXMLNodeParent.ReplaceChild(EmptyCorrectionXMLNode, CorrectionXMLNode);
end else
CorrectionXMLNode.InnerText := CorrectionValue
end;
end;
end;
procedure ValidateUpdatedOCRFields(IncomingDocument: Record "Incoming Document")
begin
IncomingDocument.TestField("Vendor Name");
end;
[Scope('OnPrem')]
procedure GetOriginalOCRXMLRootNode(IncomingDocument: Record "Incoming Document"; var OriginalXMLRootNode: DotNet XmlNode)
var
IncomingDocumentAttachment: Record "Incoming Document Attachment";
TempBlob: Codeunit "Temp Blob";
XMLDOMManagement: Codeunit "XML DOM Management";
InStream: InStream;
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeGetOriginalOCRXMLRootNode(IncomingDocument, OriginalXMLRootNode, IsHandled);
if IsHandled then
exit;
IncomingDocument.TestField(Posted, false);
IncomingDocumentAttachment.SetRange("Incoming Document Entry No.", IncomingDocument."Entry No.");
IncomingDocumentAttachment.SetRange("Generated from OCR", true);
IncomingDocumentAttachment.SetRange(Default, true);
if not IncomingDocumentAttachment.FindFirst() then
exit;
TempBlob.FromRecord(IncomingDocumentAttachment, IncomingDocumentAttachment.FieldNo(Content));
TempBlob.CreateInStream(InStream);
XMLDOMManagement.LoadXMLNodeFromInStream(InStream, OriginalXMLRootNode);
end;
procedure GetOCRServiceDocumentReference(IncomingDocument: Record "Incoming Document"): Text[50]
var
IncomingDocumentAttachment: Record "Incoming Document Attachment";
begin
IncomingDocumentAttachment.SetRange("Incoming Document Entry No.", IncomingDocument."Entry No.");
IncomingDocumentAttachment.SetRange("Generated from OCR", true);
if not IncomingDocumentAttachment.FindFirst() then
exit('');
exit(IncomingDocumentAttachment."OCR Service Document Reference");
end;
[Scope('OnPrem')]
procedure GetDocumentList(var ResponseStr: InStream): Boolean
begin
if not RsoGetRequest('currentuser/documents?pageIndex=0&pageSize=1000', ResponseStr) then begin
Session.LogMessage('00008KJ', GettingDocumentsForUserFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
exit(true);
end;
[Scope('OnPrem')]
procedure GetDocuments(ExternalBatchFilter: Text): Integer
var
Regex: Codeunit Regex;
XMLDOMManagement: Codeunit "XML DOM Management";
XMLRootNode: DotNet XmlNode;
XMLNode: DotNet XmlNode;
ChildNode: DotNet XmlNode;
ResponseStr: InStream;
ExternalBatchId: Text[50];
DocId: Text[50];
CountProcessed: Integer;
CountDownloaded: Integer;
begin
GetOcrServiceSetup(true);
if not RsoGetRequest(StrSubstNo('documents/rest/customers/%1/outputdocuments', OCRServiceSetup."Customer ID"), ResponseStr) then
Session.LogMessage('00008KK', GettingDocumentsForCustomerFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
foreach XMLNode in XMLRootNode.ChildNodes do begin
ChildNode := XMLNode.SelectSingleNode('BatchExternalId');
ExternalBatchId := ChildNode.InnerText;
if (ExternalBatchFilter = '') or (ExternalBatchFilter = ExternalBatchId) then
foreach ChildNode in XMLNode.SelectNodes('DocumentId') do begin
CountProcessed += 1;
DocId := ChildNode.InnerText;
if not Regex.IsMatch(DocId, '^[a-zA-Z0-9\-\{\}]*$') then begin
Session.LogMessage('00008LB', InvalidDocumentIdTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailed(OCRServiceSetup.RecordId, GetNewDocumentsMsg, StrSubstNo(NotValidDocIDErr, DocId));
end;
CountDownloaded += DownloadDocument(ExternalBatchId, DocId);
if CountDownloaded > GetMaxDocDownloadCount() then begin
Session.LogMessage('00008KL', StrSubstNo(DocumentsDownloadedTxt, CountDownloaded, CountProcessed), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, GetNewDocumentsMsg, StrSubstNo(NewDocumentsTotalMsg, CountDownloaded, CountProcessed));
exit(CountDownloaded);
end;
end;
end;
Session.LogMessage('00008KM', StrSubstNo(DocumentsDownloadedTxt, CountDownloaded, CountProcessed), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, GetNewDocumentsMsg, StrSubstNo(NewDocumentsTotalMsg, CountDownloaded, CountProcessed));
if (ExternalBatchFilter <> '') and (CountDownloaded > 0) then
exit(CountDownloaded);
if ExternalBatchFilter <> '' then
GetDocumentStatus(ExternalBatchFilter)
else
GetDocumentsExcludeProcessed();
exit(CountDownloaded);
end;
[Scope('OnPrem')]
procedure GetDocumentsExcludeProcessed()
var
IncomingDocument: Record "Incoming Document";
IncomingDocumentAttachment: Record "Incoming Document Attachment";
TempIncomingDocumentAttachment: Record "Incoming Document Attachment" temporary;
begin
GetOcrServiceSetup(true);
IncomingDocument.SetRange("OCR Status", IncomingDocument."OCR Status"::Sent);
if not IncomingDocument.FindSet() then
exit;
repeat
IncomingDocumentAttachment.SetRange("Incoming Document Entry No.", IncomingDocument."Entry No.");
IncomingDocumentAttachment.SetRange(Default, true);
IncomingDocumentAttachment.FindFirst();
TempIncomingDocumentAttachment := IncomingDocumentAttachment;
TempIncomingDocumentAttachment.Insert();
until IncomingDocument.Next() = 0;
GetBatches(TempIncomingDocumentAttachment, '');
end;
[Scope('OnPrem')]
procedure GetDocumentStatus(ExternalBatchFilter: Text)
var
IncomingDocument: Record "Incoming Document";
IncomingDocumentAttachment: Record "Incoming Document Attachment";
TempIncomingDocumentAttachment: Record "Incoming Document Attachment" temporary;
begin
GetOcrServiceSetup(true);
IncomingDocumentAttachment.SetRange("External Document Reference", ExternalBatchFilter);
IncomingDocumentAttachment.SetRange(Default, true);
IncomingDocumentAttachment.FindFirst();
IncomingDocument.Get(IncomingDocumentAttachment."Incoming Document Entry No.");
if (IncomingDocument."OCR Status" <> IncomingDocument."OCR Status"::Sent) and
(IncomingDocument."OCR Status" <> IncomingDocument."OCR Status"::"Awaiting Verification")
then
exit;
TempIncomingDocumentAttachment := IncomingDocumentAttachment;
TempIncomingDocumentAttachment.Insert();
GetBatches(TempIncomingDocumentAttachment, ExternalBatchFilter);
end;
[Scope('OnPrem')]
procedure GetDocumentForAttachment(var IncomingDocumentAttachment: Record "Incoming Document Attachment"): Integer
var
IncomingDocument: Record "Incoming Document";
Status: Integer;
begin
if IncomingDocumentAttachment."External Document Reference" = '' then begin
Session.LogMessage('00008LC', DocumentNotUploadedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
Error(NotUploadedErr);
end;
if GetDocuments(IncomingDocumentAttachment."External Document Reference") > 0 then
Status := IncomingDocument."OCR Status"::Success
else begin
IncomingDocument.Get(IncomingDocumentAttachment."Incoming Document Entry No.");
Status := IncomingDocument."OCR Status";
end;
case Status of
IncomingDocument."OCR Status"::Success:
Message(ImportSuccessMsg);
IncomingDocument."OCR Status"::"Awaiting Verification":
Message(VerifyMsg);
IncomingDocument."OCR Status"::Error:
Message(FailedMsg);
IncomingDocument."OCR Status"::Sent: // Pending Result
Message(DocumentNotReadyMsg);
else
Message(DocumentNotReadyMsg);
end;
exit(Status);
end;
local procedure GetBatchDocuments(var XMLRootNode: DotNet XmlNode; BatchFilter: Text): Boolean
var
XMLDOMManagement: Codeunit "XML DOM Management";
ResponseStr: InStream;
Path: Text;
PageSize: Integer;
CurrentPage: Integer;
begin
PageSize := 200;
CurrentPage := 0;
Path := StrSubstNo(
'documents/rest/customers/%1/batches/%2/documents?pageIndex=%3&pageSize=%4', OCRServiceSetup."Customer ID",
BatchFilter, CurrentPage, PageSize);
if not RsoGetRequest(Path, ResponseStr) then begin
Session.LogMessage('00008KN', GettingBatchDocumentsFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
exit(true);
end;
local procedure GetBatchesApi(var XMLRootNode: DotNet XmlNode; ExternalBatchFilter: Text): Boolean
var
XMLDOMManagement: Codeunit "XML DOM Management";
ResponseStr: InStream;
Path: Text;
PageSize: Integer;
CurrentPage: Integer;
begin
PageSize := 200;
CurrentPage := 0;
if ExternalBatchFilter <> '' then
Path := StrSubstNo(
'documents/rest/customers/%1/batches?pageIndex=%2&pageSize=%3&externalId=%4', OCRServiceSetup."Customer ID",
CurrentPage, PageSize, ExternalBatchFilter)
else
Path := StrSubstNo(
'documents/rest/customers/%1/batches?pageIndex=%2&pageSize=%3&excludeProcessed=1', OCRServiceSetup."Customer ID",
CurrentPage, PageSize);
if not RsoGetRequest(Path, ResponseStr) then begin
Session.LogMessage('00008KO', GettingBatchesFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode);
exit(true);
end;
local procedure GetBatches(var TempIncomingDocumentAttachment: Record "Incoming Document Attachment" temporary; ExternalBatchFilter: Text): Boolean
var
IncomingDocument: Record "Incoming Document";
XMLDOMManagement: Codeunit "XML DOM Management";
SendIncomingDocumentToOCR: Codeunit "Send Incoming Document to OCR";
XMLRootNode: DotNet XmlNode;
CurrentPage: Integer;
TotalPages: Integer;
begin
repeat
if not GetBatchesApi(XMLRootNode, ExternalBatchFilter) then
exit(false);
if not Evaluate(TotalPages, XMLDOMManagement.FindNodeText(XMLRootNode, '//PageCount')) then begin
Session.LogMessage('00008LD', GettingBatchesFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit(false);
end;
XMLDOMManagement.FindNode(XMLRootNode, '//Batches', XMLRootNode);
FindDocumentFromList(XMLRootNode, TempIncomingDocumentAttachment);
CurrentPage += 1;
until (TempIncomingDocumentAttachment.Count = 0) or (CurrentPage > TotalPages);
if TempIncomingDocumentAttachment.FindSet() then
repeat
IncomingDocument.Get(TempIncomingDocumentAttachment."Incoming Document Entry No.");
SendIncomingDocumentToOCR.SetStatusToFailed(IncomingDocument);
until TempIncomingDocumentAttachment.Next() = 0;
exit(true);
end;
[Scope('OnPrem')]
procedure GetDocumentId(ExternalBatchFilter: Text): Text
var
XMLDOMManagement: Codeunit "XML DOM Management";
XMLRootNode: DotNet XmlNode;
BatchID: Text;
DocumentID: Text;
begin
GetOcrServiceSetup(true);
if not GetBatchesApi(XMLRootNode, ExternalBatchFilter) then
exit('');
BatchID := XMLDOMManagement.FindNodeText(XMLRootNode, '/PagedBatches/Batches/Batch/Id');
if not GetBatchDocuments(XMLRootNode, BatchID) then
exit('');
DocumentID := XMLDOMManagement.FindNodeText(XMLRootNode, '/PagedDocuments/Documents/Document/Id');
exit(DocumentID);
end;
local procedure DownloadDocument(ExternalBatchId: Text[50]; DocId: Text[50]): Integer
var
IncomingDocument: Record "Incoming Document";
IncomingDocumentAttachment: Record "Incoming Document Attachment";
XMLDOMManagement: Codeunit "XML DOM Management";
SendIncomingDocumentToOCR: Codeunit "Send Incoming Document to OCR";
FeatureTelemetry: Codeunit "Feature Telemetry";
ImageInStr: InStream;
ResponseStr: InStream;
XMLRootNode: DotNet XmlNode;
AttachmentName: Text[250];
ContentType: Text[50];
TrackId: Text;
IsHandled: Boolean;
Result: Integer;
begin
IsHandled := false;
Result := 0;
OnBeforeDownloadDocument(ExternalBatchId, DocId, Result, IsHandled);
if IsHandled then
exit(Result);
if not RsoGetRequest(StrSubstNo('documents/rest/%1', DocId), ResponseStr) then begin
Session.LogMessage('00008KP', OCRServiceUserFailedToDownloadDocumentTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, StrSubstNo(DocumentNotDownloadedTxt, DocId, ''), '');
exit(0);
end;
if not XMLDOMManagement.LoadXMLNodeFromInStream(ResponseStr, XMLRootNode) then begin
Session.LogMessage('000089N', OCRServiceUserFailedToDownloadDocumentTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, StrSubstNo(DocumentNotDownloadedTxt, DocId, ''), '');
exit(0);
end;
FeatureTelemetry.LogUptake('0000IMN', TelemetryCategoryTok, Enum::"Feature Uptake Status"::Used);
FeatureTelemetry.LogUsage('0000IMO', TelemetryCategoryTok, 'Document imported');
TrackId := XMLDOMManagement.FindNodeText(XMLRootNode, 'TrackId');
if ExternalBatchId <> '' then
IncomingDocumentAttachment.SetRange("External Document Reference", ExternalBatchId);
if (ExternalBatchId <> '') and IncomingDocumentAttachment.FindFirst() then begin
Session.LogMessage('00008KQ', UpdatingIncomingDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
IncomingDocument.Get(IncomingDocumentAttachment."Incoming Document Entry No.");
AttachmentName := IncomingDocumentAttachment.Name;
end else begin // New Incoming Document
Session.LogMessage('00008KR', InsertingIncomingDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
AttachmentName := CopyStr(XMLDOMManagement.FindNodeText(XMLRootNode, 'OriginalFilename'), 1, MaxStrLen(AttachmentName));
IncomingDocument.Init();
IncomingDocument.CreateIncomingDocument(AttachmentName, '');
IncomingDocumentAttachment.SetRange("External Document Reference");
if not RsoGetRequestBinary(StrSubstNo('documents/rest/file/%1/image', DocId), ImageInStr, ContentType) then begin
Session.LogMessage('00008KS', OCRServiceUserFailedToDownloadDocumentTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, StrSubstNo(DocumentNotDownloadedTxt, DocId, TrackId), '');
exit(0);
end;
IncomingDocument.AddAttachmentFromStream(
IncomingDocumentAttachment, AttachmentName, GetExtensionFromContentType(AttachmentName, ContentType), ImageInStr);
end;
IncomingDocument.CheckNotCreated();
IncomingDocumentAttachment.SetRange("External Document Reference");
IncomingDocument.AddAttachmentFromStream(IncomingDocumentAttachment, AttachmentName, 'xml', ResponseStr);
IncomingDocumentAttachment."Generated from OCR" := true;
IncomingDocumentAttachment."OCR Service Document Reference" := DocId;
IncomingDocumentAttachment.Validate(Default, true);
IncomingDocumentAttachment.Modify();
IncomingDocument.Get(IncomingDocument."Entry No.");
SendIncomingDocumentToOCR.SetStatusToReceived(IncomingDocument);
UpdateIncomingDocWithOCRData(IncomingDocument, XMLRootNode);
Session.LogMessage('000089O', OCRServiceUserSuccessfullyDownloadedDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, GetDocumentMsg, StrSubstNo(DocumentDownloadedTxt, DocId, TrackId));
LogActivitySucceeded(IncomingDocument.RecordId, GetDocumentMsg, StrSubstNo(DocumentDownloadedTxt, DocId, TrackId));
if not RsoPutRequest(
StrSubstNo('documents/rest/%1/downloaded', DocId),
'<UploadDataCollection xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />', ResponseStr)
then begin
Session.LogMessage('00008KU', RegisteringDownloadFailedTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivityFailedNoError(OCRServiceSetup.RecordId, StrSubstNo(DownloadNotRegisteredTxt, DocId, TrackId), '');
LogActivityFailedNoError(IncomingDocument.RecordId, StrSubstNo(DownloadNotRegisteredTxt, DocId, TrackId), '');
exit(0);
end;
Session.LogMessage('00008KV', RegisteringDownloadSucceedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
LogActivitySucceeded(OCRServiceSetup.RecordId, GetDocumentConfirmMsg, StrSubstNo(DocumentDownloadedTxt, DocId, TrackId));
LogActivitySucceeded(IncomingDocument.RecordId, GetDocumentConfirmMsg, StrSubstNo(DocumentDownloadedTxt, DocId, TrackId));
Commit();
exit(1);
end;
[Scope('OnPrem')]
procedure UpdateIncomingDocWithOCRData(var IncomingDocument: Record "Incoming Document"; var XMLRootNode: DotNet XmlNode)
var
Vendor: Record Vendor;
IncomingDocumentAttachment: Record "Incoming Document Attachment";
XMLDOMManagement: Codeunit "XML DOM Management";
VendorFound: Boolean;
begin
if IncomingDocument."Data Exchange Type" = '' then
exit;
IncomingDocumentAttachment.SetRange("Incoming Document Entry No.", IncomingDocument."Entry No.");
IncomingDocumentAttachment.SetRange("Generated from OCR", true);
IncomingDocumentAttachment.SetRange(Default, true);
if not IncomingDocumentAttachment.FindFirst() then begin
Session.LogMessage('00008KT', CannotFindAttachmentTxt, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
exit;
end;
IncomingDocumentAttachment.ExtractHeaderFields(XMLRootNode, IncomingDocument);
IncomingDocument.Get(IncomingDocument."Entry No.");
if XMLDOMManagement.FindNodeText(XMLRootNode, 'HeaderFields/HeaderField/Text[../Type/text() = "creditinvoice"]') =
'true'
then
IncomingDocument."Document Type" := IncomingDocument."Document Type"::"Purchase Credit Memo";
IncomingDocument."OCR Track ID" := CopyStr(XMLDOMManagement.FindNodeText(XMLRootNode, 'TrackId'), 1, MaxStrLen(IncomingDocument."OCR Track ID"));
if not IsNullGuid(IncomingDocument."Vendor Id") then
VendorFound := Vendor.GetBySystemId(IncomingDocument."Vendor Id");
if (not VendorFound) and (IncomingDocument."Vendor No." <> '') then
VendorFound := Vendor.Get(IncomingDocument."Vendor No.");
if (not VendorFound) and (IncomingDocument."Vendor VAT Registration No." <> '') then begin
Vendor.SetCurrentKey(Blocked);
Vendor.SetRange("VAT Registration No.", IncomingDocument."Vendor VAT Registration No.");
VendorFound := Vendor.FindFirst();
end;
if VendorFound then begin
if IncomingDocument."Vendor Id" <> Vendor.SystemId then
IncomingDocument.Validate(IncomingDocument."Vendor Id", Vendor.SystemId);
if IncomingDocument."Vendor No." <> Vendor."No." then
IncomingDocument.Validate(IncomingDocument."Vendor No.", Vendor."No.");
end;
IncomingDocument.Modify();
end;
procedure LogActivitySucceeded(RelatedRecordID: RecordID; ActivityDescription: Text; ActivityMessage: Text)
var
ActivityLog: Record "Activity Log";
begin
ActivityLog.LogActivity(RelatedRecordID, ActivityLog.Status::Success, LoggingConstTxt,
ActivityDescription, ActivityMessage);
end;
procedure LogActivityFailed(RelatedRecordID: RecordID; ActivityDescription: Text; ActivityMessage: Text)
begin
LogActivityFailed(RelatedRecordID, ActivityDescription, ActivityMessage, false);
end;
local procedure LogActivityFailedNoError(RelatedRecordID: RecordID; ActivityDescription: Text; ActivityMessage: Text)
begin
LogActivityFailed(RelatedRecordID, ActivityDescription, ActivityMessage, true);
end;
local procedure LogActivityFailed(RelatedRecordID: RecordID; ActivityDescription: Text; ActivityMessage: Text; IgnoreError: Boolean)
var
ActivityLog: Record "Activity Log";
begin
ActivityMessage := GetLastErrorText + ' ' + ActivityMessage;
ClearLastError();
ActivityLog.LogActivity(RelatedRecordID, ActivityLog.Status::Failed, LoggingConstTxt,
ActivityDescription, ActivityMessage);
Commit();
if IgnoreError then
exit;
if DelChr(ActivityMessage, '<>', ' ') <> '' then
Error(ActivityMessage);
end;
[EventSubscriber(ObjectType::Table, Database::"Service Connection", 'OnRegisterServiceConnection', '', false, false)]
procedure HandleOCRRegisterServiceConnection(var ServiceConnection: Record "Service Connection")
var
OCRServiceSetup: Record "OCR Service Setup";
RecRef: RecordRef;
begin
if not OCRServiceSetup.Get() then begin
OCRServiceSetup.Init();
OCRServiceSetup.Insert(true);
end;
RecRef.GetTable(OCRServiceSetup);
if OCRServiceSetup.Enabled then
ServiceConnection.Status := ServiceConnection.Status::Enabled
else
ServiceConnection.Status := ServiceConnection.Status::Disabled;
ServiceConnection.InsertServiceConnection(
ServiceConnection, RecRef.RecordId, OCRServiceSetup.TableCaption(), OCRServiceSetup."Service URL", PAGE::"OCR Service Setup");
end;
local procedure GetMaxDocDownloadCount(): Integer
begin
exit(1000);
end;
local procedure GetDocumentSimplifiedStatus(ObjectStatus: Integer): Integer
var
IncomingDocument: Record "Incoming Document";
begin
// Status definitions can be found at http://docs.readsoftonline.com/help/eng/partner/#reference/batch-statuses.htm%3FTocPath%3DReference%7C_____6
// Status codes can be found at https://services.readsoftonline.com/documentation/rest?s=-940536891&m=173766930
case ObjectStatus of
0: // 'BATCHCREATED'
exit(IncomingDocument."OCR Status"::Sent);
1: // 'BATCHINPUTVALIDATIONFAILED'
exit(IncomingDocument."OCR Status"::Error);
3: // 'BATCHPENDINGPROCESSSTART'
exit(IncomingDocument."OCR Status"::Sent);
7: // 'BATCHCLASSIFICATIONINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
10: // 'BATCHPENDINGCORRECTION'
exit(IncomingDocument."OCR Status"::"Awaiting Verification");
15: // 'BATCHEXTRACTIONINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
20: // 'BATCHMANUALVERIFICATION'
exit(IncomingDocument."OCR Status"::"Awaiting Verification");
23: // 'BATCHREQUESTINFORMATION'
exit(IncomingDocument."OCR Status"::"Awaiting Verification");
25: // 'BATCHAPPROVALINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
26: // 'BATCHPENDINGREGISTRATION'
exit(IncomingDocument."OCR Status"::Sent);
27: // 'BATCHREGISTRATIONINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
28: // 'BATCHPENDINGPOST'
exit(IncomingDocument."OCR Status"::Sent);
29: // 'BATCHPOSTINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
30: // 'BATCHPENDINGEXPORT'
exit(IncomingDocument."OCR Status"::Sent);
33: // 'BATCHEXPORTINPROGRESS'
exit(IncomingDocument."OCR Status"::Success);
35: // 'BATCHEXPORTFAILED'
exit(IncomingDocument."OCR Status"::Error);
40: // 'BATCHSUCCESSFULLYPROCESSED'
exit(IncomingDocument."OCR Status"::Success);
50: // 'BATCHREJECTED'
exit(IncomingDocument."OCR Status"::Error);
100: // 'BATCHDELETED'
exit(IncomingDocument."OCR Status"::Error);
200: // 'BATCHPREPROCESSINGINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
13: // 'BATCHMANUALSEPERATION'
exit(IncomingDocument."OCR Status"::"Awaiting Verification");
14: // 'BATCHSEPERATIONINPROGRESS'
exit(IncomingDocument."OCR Status"::Sent);
95: // 'BATCHDELETEINPROGRESS'
exit(IncomingDocument."OCR Status"::Error);
else
exit(IncomingDocument."OCR Status"::" ");
end;
end;
local procedure FindDocumentFromList(var XMLRootNode: DotNet XmlNode; var TempIncomingDocumentAttachment: Record "Incoming Document Attachment" temporary)
var
IncomingDocument: Record "Incoming Document";
XMLDOMManagement: Codeunit "XML DOM Management";
SendIncomingDocumentToOCR: Codeunit "Send Incoming Document to OCR";
XMLNode: DotNet XmlNode;
DocId: Text;
DocStatus: Integer;
StatusAsInt: Integer;
begin
foreach XMLNode in XMLRootNode.ChildNodes do begin
if TempIncomingDocumentAttachment.IsEmpty() then
exit;
DocId := XMLDOMManagement.FindNodeText(XMLNode, './ExternalId');
TempIncomingDocumentAttachment.SetRange("External Document Reference", DocId);
if TempIncomingDocumentAttachment.FindSet() then
repeat
Evaluate(StatusAsInt, XMLDOMManagement.FindNodeText(XMLNode, './StatusAsInt'));
DocStatus := GetDocumentSimplifiedStatus(StatusAsInt);
IncomingDocument.Get(TempIncomingDocumentAttachment."Incoming Document Entry No.");
case DocStatus of
IncomingDocument."OCR Status"::Error:
SendIncomingDocumentToOCR.SetStatusToFailed(IncomingDocument);
IncomingDocument."OCR Status"::"Awaiting Verification":
SendIncomingDocumentToOCR.SetStatusToVerify(IncomingDocument);
end;
TempIncomingDocumentAttachment.Delete();
until TempIncomingDocumentAttachment.Next() = 0;
// Remove filter
TempIncomingDocumentAttachment.SetRange("External Document Reference");
if TempIncomingDocumentAttachment.FindSet() then;
end;
end;
[Scope('OnPrem')]
procedure TestConnection(var OCRServiceSetup: Record "OCR Service Setup")
begin
if SetupConnection(OCRServiceSetup) then
Message(ConnectionSuccessMsg);
end;
[Scope('OnPrem')]
procedure SetupConnection(var OCRServiceSetup: Record "OCR Service Setup"): Boolean
begin
if not HasCredentials(OCRServiceSetup) then
Error(GetCredentialsErrText());
if not Authenticate() then
Error(ConnectionFailedErr);
UpdateOrganizationInfo(OCRServiceSetup);
UpdateOcrDocumentTemplates();
exit(true);
end;
[EventSubscriber(ObjectType::Page, Page::"Incoming Document", 'OnCloseIncomingDocumentFromAction', '', false, false)]
local procedure OnCloseIncomingDocumentHandler(var IncomingDocument: Record "Incoming Document")
begin
PAGE.Run(PAGE::"Incoming Document", IncomingDocument);
end;
[EventSubscriber(ObjectType::Page, Page::"Incoming Documents", 'OnCloseIncomingDocumentsFromActions', '', false, false)]
local procedure OnCloseIncomingDocumentsHandler(var IncomingDocument: Record "Incoming Document")
begin
PAGE.Run(PAGE::"Incoming Documents", IncomingDocument);
end;
procedure OcrServiceIsEnable(): Boolean
begin
if not OCRServiceSetup.Get() then
exit(false);
if
(OCRServiceSetup."Service URL" = '') or
(OCRServiceSetup.Enabled = false)
then
exit(false);
exit(true);
end;
[Scope('OnPrem')]
procedure GetStatusHyperLink(IncomingDocument: Record "Incoming Document"): Text
var
IncomingDocumentAttachment: Record "Incoming Document Attachment";
DocumentID: Text;
begin
if IncomingDocument."OCR Status" = IncomingDocument."OCR Status"::"Awaiting Verification" then begin
IncomingDocument.GetMainAttachment(IncomingDocumentAttachment);
if IncomingDocumentAttachment."External Document Reference" = '' then
exit('');
DocumentID := GetDocumentId(IncomingDocumentAttachment."External Document Reference");
exit(StrSubstNo('%1/documents/%2', OCRServiceSetup."Sign-in URL", DocumentID));
end;
if OCRServiceSetup.Enabled and (OCRServiceSetup."Sign-in URL" <> '') then
exit(OCRServiceSetup."Sign-in URL");
end;
local procedure GetExtensionFromContentType(AttachmentName: Text; ContentType: Text): Text
var
FileManagement: Codeunit "File Management";
begin
if StrPos(ContentType, 'application/pdf') <> 0 then
exit('pdf');
exit(FileManagement.GetExtension(AttachmentName));
end;
[EventSubscriber(ObjectType::Table, Database::"Incoming Document", 'OnAfterCreateGenJnlLineFromIncomingDocSuccess', '', false, false)]
local procedure LogTelemetryOnAfterCreateGenJnlLineFromIncomingDocSuccess(var IncomingDocument: Record "Incoming Document")
begin
if IncomingDocument."OCR Status" = IncomingDocument."OCR Status"::Success then
Session.LogMessage('000089P', OCRServiceUserCreatedGenJnlLineOutOfOCRedDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Incoming Document", 'OnAfterCreateDocFromIncomingDocSuccess', '', false, false)]
local procedure LogTelemetryOnAfterCreateDocFromIncomingDocSuccess(var IncomingDocument: Record "Incoming Document")
begin
if IncomingDocument."OCR Status" = IncomingDocument."OCR Status"::Success then
Session.LogMessage('000089Q', OCRServiceUserCreatedInvoiceOutOfOCRedDocumentTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTok);
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeGetOriginalOCRXMLRootNode(IncomingDocument: Record "Incoming Document"; var OriginalXMLRootNode: DotNet XmlNode; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeDownloadDocument(ExternalBatchId: Text[50]; DocId: Text[50]; var Result: Integer; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnCorrectOCRFileOnAfterCorrectOCRFileNodes(var OCRFileXMLRootNode: DotNet XmlNode; var IncomingDocument: Record "Incoming Document")
begin
end;
}