Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[E-Documents Core] - Manual multiple files import #8

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Apps/W1/EDocument/app/src/Document/EDocument.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,13 @@ page 6121 "E-Document"
Visible = IsIncomingDoc and (not IsProcessed);

trigger OnAction()
var
EDocumentService: Record "E-Document Service";
begin
EDocImport.UploadDocument(Rec);
CurrPage.Update();
if EDocImport.ChooseEDocumentService(EDocumentService) then begin
EDocImport.UploadDocument(Rec, EDocumentService);
CurrPage.Update();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add explicit RunTrigger. Very recommend using false if that's enough for the solution

end;
end;
}
action(TextToAccountMapping)
Expand Down
51 changes: 44 additions & 7 deletions Apps/W1/EDocument/app/src/Document/EDocuments.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,42 @@ page 6122 "E-Documents"
{
area(Processing)
{
#if not CLEAN26
action(ImportManually)
{
Caption = 'New From File';
ToolTip = 'Create an electronic document by manually uploading a file.';
Image = Import;
Visible = false;
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
ObsoleteState = Pending;
ObsoleteTag = '26.0';
ObsoleteReason = 'Functionality moved to "Import Files" action.';

trigger OnAction()
begin
NewFromFile();
end;
}
#endif
fileuploadaction(ImportManuallyMultiple)
{
Caption = 'Import Files';
ToolTip = 'Create electronic documents by uploading single or multiple files.';
Image = Import;
AllowedFileExtensions = '.xml';
AllowMultipleFiles = true;
petemchlk marked this conversation as resolved.
Show resolved Hide resolved

trigger OnAction(Files: List of [FileUpload])
var
EDocumentService: Record "E-Document Service";
EDocImport: Codeunit "E-Doc. Import";
begin
if EDocImport.ChooseEDocumentService(EDocumentService) then begin
EDocImport.UploadDocuments(Files, EDocumentService);
CurrPage.Update(true);
end;
end;
}
action(EDocumentServices)
{
RunObject = Page "E-Document Services";
Expand All @@ -88,7 +113,16 @@ page 6122 "E-Documents"
}
area(Promoted)
{
actionref(Promoted_ImportManually; ImportManually) { }
#if not CLEAN26
actionref(Promoted_ImportManually; ImportManually)
{
Visible = false;
ObsoleteState = Pending;
ObsoleteTag = '26.0';
ObsoleteReason = 'Functionality moved to "Import Files" action.';
}
#endif
actionref(Promoted_ImportManuallyMultiple; ImportManuallyMultiple) { }
actionref(Promoted_EDocumentServices; EDocumentServices) { }
}
}
Expand All @@ -100,15 +134,18 @@ page 6122 "E-Documents"
local procedure NewFromFile()
var
EDocument: Record "E-Document";
EDocumentService: Record "E-Document Service";
EDocImport: Codeunit "E-Doc. Import";
EDocErrorHelper: Codeunit "E-Document Error Helper";
begin
EDocImport.UploadDocument(EDocument);
if EDocument."Entry No" <> 0 then begin
EDocImport.ProcessDocument(EDocument, false);
if EDocErrorHelper.HasErrors(EDocument) then
if Confirm(DocNotCreatedQst, true, EDocument."Document Type") then
Page.Run(Page::"E-Document", EDocument);
if EDocImport.ChooseEDocumentService(EDocumentService) then begin
EDocImport.UploadDocument(EDocument, EDocumentService);
if EDocument."Entry No" <> 0 then begin
EDocImport.ProcessDocument(EDocument, false);
if EDocErrorHelper.HasErrors(EDocument) then
if Confirm(DocNotCreatedQst, true, EDocument."Document Type") then
Page.Run(Page::"E-Document", EDocument);
end;
end;
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ codeunit 6134 "E-Doc. Integration Management"
local procedure ReceiveSingleDocument(var EDocument: Record "E-Document"; var EDocumentService: Record "E-Document Service"; DocumentMetadata: Codeunit "Temp Blob"; IDocumentReceiver: Interface IDocumentReceiver): Boolean
var
ReceiveContext, FetchContextImpl : Codeunit ReceiveContext;
EDocumentContent: Codeunit "Temp Blob";
ErrorCount: Integer;
Success, IsFetchableType : Boolean;
begin
Expand All @@ -209,19 +210,22 @@ codeunit 6134 "E-Doc. Integration Management"
if not Success then
exit(false);

if not ReceiveContext.GetTempBlob().HasValue() then
EDocumentContent := ReceiveContext.GetTempBlob();
if not EDocumentContent.HasValue() then
exit(false);

IsFetchableType := IDocumentReceiver is IReceivedDocumentMarker;
if IsFetchableType then begin
ErrorCount := EDocumentErrorHelper.ErrorMessageCount(EDocument);
RunMarkFetched(EDocument, EDocumentService, ReceiveContext.GetTempBlob(), IDocumentReceiver, FetchContextImpl);
Success := EDocumentErrorHelper.ErrorMessageCount(EDocument) = ErrorCount;

if not Success then
exit(false);
end;

if HasDuplicate(EDocument, EDocumentContent, EDocumentService."Document Format") then
exit(false);

// Only after sucecssfully downloading and (optionally) marking as fetched, the document is considered imported
// Insert logs for downloading document
InsertLogAndEDocumentStatus(EDocument, EDocumentService, ReceiveContext.GetTempBlob(), ReceiveContext.Status().GetStatus());
Expand All @@ -235,6 +239,24 @@ codeunit 6134 "E-Doc. Integration Management"
end;

petemchlk marked this conversation as resolved.
Show resolved Hide resolved

internal procedure HasDuplicate(var IncomingEDocument: Record "E-Document"; var EDocumentContent: Codeunit "Temp Blob"; IEDocument: Interface "E-Document"): Boolean
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
var
EDocument: Record "E-Document";
EDocGetBasicInfo: Codeunit "E-Doc. Get Basic Info";
begin
// Commit before getting basic info with error handling (if Codeunit.Run then)
Commit();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is from another PR, but this commit in here makes me a bit nervous. Because seeing a procedure like HasDuplicates doesn't make you think that the transaction will be commited in here...

You don't have to change anything, because I know you have other fires to fight, and I know MS is quite liberal with their commits as well, but just something to think about...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually tried to avoid this commit, but happened to me while testing manually to get this error. Will keep this in my mind so if there will be some time left will try to handle this :)

EDocGetBasicInfo.SetValues(IEDocument, IncomingEDocument, EDocumentContent);
if not EDocGetBasicInfo.Run() then
exit(false);
EDocGetBasicInfo.GetValues(IEDocument, IncomingEDocument, EDocumentContent);

EDocument.SetFilter("Entry No", '<>%1', IncomingEDocument."Entry No");
EDocument.SetRange("Incoming E-Document No.", IncomingEDocument."Incoming E-Document No.");
EDocument.SetRange("Bill-to/Pay-to No.", IncomingEDocument."Bill-to/Pay-to No.");
EDocument.SetRange("Document Date", IncomingEDocument."Document Date");
exit(not EDocument.IsEmpty());
end;
#endregion

#region Actions
Expand Down Expand Up @@ -564,7 +586,6 @@ codeunit 6134 "E-Doc. Integration Management"
exit(true);
end;


#endregion

var
Expand Down
143 changes: 122 additions & 21 deletions Apps/W1/EDocument/app/src/Processing/EDocImport.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,28 @@ codeunit 6140 "E-Doc. Import"
tabledata "E-Document" = im,
tabledata "E-Doc. Imported Line" = imd;

internal procedure UploadDocument(var EDocument: Record "E-Document")
internal procedure UploadDocument(var EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service")
var
EDocumentService: Record "E-Document Service";
TempBlob: Codeunit "Temp Blob";
OutStr: OutStream;
InStr: InStream;
FileName: Text;
begin
if Page.RunModal(Page::"E-Document Services", EDocumentService) <> Action::LookupOK then
exit;

if not UploadIntoStream('', '', '', FileName, InStr) then
exit;

TempBlob.CreateOutStream(OutStr);
CopyStream(OutStr, InStr);

EDocument.Direction := EDocument.Direction::Incoming;
EDocument."Document Type" := Enum::"E-Document Type"::None;
this.ImportEDocumentFromStream(EDocument, EDocumentService, InStr);
end;

if EDocument."Entry No" = 0 then begin
EDocument.Insert(true);
EDocumentProcessing.InsertServiceStatus(EDocument, EDocumentService, Enum::"E-Document Service Status"::Imported);
end else begin
EDocument.Modify(true);
EDocumentProcessing.ModifyServiceStatus(EDocument, EDocumentService, Enum::"E-Document Service Status"::Imported);
end;
internal procedure UploadDocuments(Documents: List of [FileUpload]; EDocumentService: Record "E-Document Service")
var
EDocument: Record "E-Document";
begin
if Documents.Count() = 0 then
exit;

EDocumentLog.InsertLog(EDocument, EDocumentService, TempBlob, Enum::"E-Document Service Status"::Imported);
EDocumentProcessing.ModifyEDocumentStatus(EDocument, Enum::"E-Document Service Status"::Imported);
if Documents.Count() = 1 then
this.HandleSingleDocumentUpload(Documents.Get(1), EDocument, EDocumentService)
else
this.HandleMultipleDocumentUpload(Documents, EDocument, EDocumentService);
end;

internal procedure GetBasicInfo(var EDocument: Record "E-Document")
Expand Down Expand Up @@ -620,6 +612,34 @@ codeunit 6140 "E-Doc. Import"
until TempEDocImportedLine.Next() = 0;
end;

internal procedure ChooseEDocumentService(var EDocumentService: Record "E-Document Service"): Boolean
begin
exit(Page.RunModal(Page::"E-Document Services", EDocumentService) = Action::LookupOK);
end;

local procedure ImportEDocumentFromStream(var EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service"; var InStr: InStream)
var
TempBlob: Codeunit "Temp Blob";
OutStr: OutStream;
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
begin
TempBlob.CreateOutStream(OutStr);
CopyStream(OutStr, InStr);

EDocument.Direction := EDocument.Direction::Incoming;
EDocument."Document Type" := Enum::"E-Document Type"::None;

if EDocument."Entry No" = 0 then begin
EDocument.Insert(true);
EDocumentProcessing.InsertServiceStatus(EDocument, EDocumentService, Enum::"E-Document Service Status"::Imported);
end else begin
EDocument.Modify(true);
EDocumentProcessing.ModifyServiceStatus(EDocument, EDocumentService, Enum::"E-Document Service Status"::Imported);
end;

EDocumentLog.InsertLog(EDocument, EDocumentService, TempBlob, Enum::"E-Document Service Status"::Imported);
EDocumentProcessing.ModifyEDocumentStatus(EDocument, Enum::"E-Document Service Status"::Imported);
end;

internal procedure SetHideDialogs(Hide: Boolean)
begin
HideDialogs := Hide;
Expand All @@ -637,6 +657,83 @@ codeunit 6140 "E-Doc. Import"
end;
#endif

local procedure HandleMultipleDocumentUpload(var Documents: List of [FileUpload]; var EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service")
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
var
TempBlob: Codeunit "Temp Blob";
Document: FileUpload;
DocumentInstream: InStream;
DocumentOutStream: OutStream;
NotProcessedDocuments: Integer;
begin
this.SetHideDialogs(true);

foreach Document in Documents do begin
Clear(EDocument);
Clear(DocumentInstream);
Clear(TempBlob);
TempBlob.CreateOutStream(DocumentOutStream);
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
Document.CreateInStream(DocumentInstream);
CopyStream(DocumentOutStream, DocumentInstream);
if HasDuplciate(EDocument, TempBlob, EDocumentService."Document Format") then
NotProcessedDocuments += 1
else begin
TempBlob.CreateInStream(DocumentInstream); // Restore InSteram as it was consumed by CopyStream
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
CreateEDocumentFromStream(EDocument, EDocumentService, DocumentInstream);
end;
end;

if NotProcessedDocuments > 0 then
Message(StrSubstNo(DuplicatesMsg, NotProcessedDocuments, Documents.Count()))
else
Message(DocsImportedMsg);
end;

internal procedure HandleSingleDocumentUpload(Document: FileUpload; EDocument: Record "E-Document"; EDocumentService: Record "E-Document Service")
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
var
TempBlob: Codeunit "Temp Blob";
DocumentInstream: InStream;
DocumentOutStream: OutStream;
begin
TempBlob.CreateOutStream(DocumentOutStream);
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
Document.CreateInStream(DocumentInstream);
CopyStream(DocumentOutStream, DocumentInstream);
if HasDuplciate(EDocument, TempBlob, EDocumentService."Document Format") then
Error(
EDocumentAlreadyExistErr,
EDocument.FieldCaption("Incoming E-Document No."),
EDocument."Incoming E-Document No.",
EDocument.FieldCaption("Bill-to/Pay-to No."),
EDocument."Bill-to/Pay-to No.",
EDocument.FieldCaption("Document Date"),
EDocument."Document Date")
else begin
TempBlob.CreateInStream(DocumentInstream); // Restore InStream as it was consumed by CopyStream
CreateEDocumentFromStream(EDocument, EDocumentService, DocumentInstream);
end;

if not this.HideDialogs and EDocErrorHelper.HasErrors(EDocument) then
if Confirm(DocNotCreatedQst, true, EDocument."Document Type") then
Page.Run(Page::"E-Document", EDocument);
end;

local procedure HasDuplciate(var EDocument: Record "E-Document"; var TempBlob: Codeunit "Temp Blob"; IEDocument: Interface "E-Document"): Boolean
var
IntegrationManagement: Codeunit "E-Doc. Integration Management";
begin
if IntegrationManagement.HasDuplicate(EDocument, TempBlob, IEDocument) then
petemchlk marked this conversation as resolved.
Show resolved Hide resolved
exit(true);
end;

internal procedure CreateEDocumentFromStream(
var EDocument: Record "E-Document";
EDocumentService: Record "E-Document Service";
var DocumentInstream: InStream)
begin
this.ImportEDocumentFromStream(EDocument, EDocumentService, DocumentInstream);
this.ProcessDocument(EDocument, false);
end;


var
EDocumentLog: Codeunit "E-Document Log";
EDocImportHelper: Codeunit "E-Document Import Helper";
Expand All @@ -651,6 +748,10 @@ codeunit 6140 "E-Doc. Import"
DocTypeIsNotSupportedErr: Label 'Document type %1 is not supported.', Comment = '%1 - Document Type';
FailedToFindVendorErr: Label 'No vendor is set for Edocument';
CannotProcessEDocumentMsg: Label 'Cannot process E-Document %1 with Purchase Order %2 before Purchase Order has been matched and posted for E-Document %3.', Comment = '%1 - E-Document entry no, %2 - Purchase Order number, %3 - EDocument entry no.';
DocNotCreatedQst: Label 'Failed to create new %1 from E-Document. Do you want to open E-Document and see the reported errors?', Comment = '%1 - E-Document Document Type';
EDocumentAlreadyExistErr: Label 'E-Document with %1 %2, %3 %4 and %5 %6 already exists.', Comment = '%1 - Incoming E-Document No. field caption, %2 - Incoming E-Document No. value, %3 - Bill-to/Pay-to No. field caption, %4 - Bill-to/Pay-to No. value, %5 - Document Date field caption, %6 - Document Date value.';
DuplicatesMsg: Label '%1 of %2 uploaded documents were not imported because they already exist in the system.', Comment = '%1 - Not processed documents, %2 - Total documents';
DocsImportedMsg: Label 'Document(s) imported successfully.';

[IntegrationEvent(false, false)]
local procedure OnAfterProcessImportedDocument(var EDocument: Record "E-Document"; var DocumentHeader: RecordRef)
Expand Down
24 changes: 24 additions & 0 deletions Apps/W1/EDocument/edocument_workspace.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"settings": {
"al.enableCodeAnalysis": true,
"al.codeAnalyzers": [
"${CodeCop}",
"${UICop}",
"${AppSourceCop}"
]
},
"folders": [
{
"name": "app",
"path": "app"
},
{
"name": "test",
"path": "test"
},
{
"name": "demo data",
"path": "demo data"
}
]
}
Loading