Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ table 6129 "EDoc Historical Match Buffer"
{
Caption = 'Match Reason';
}
field(31; "Match Type"; Text[100])
{
Caption = 'Match Type';
}
field(40; "Confidence Score"; Decimal)
{
Caption = 'Confidence Score';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ codeunit 6126 "E-Doc. GL Account Matching" implements "AOAI Function", IEDocAISy
EDocActivityLogBuilder
.Init(Database::"E-Document Purchase Line", Rec.FieldNo("[BC] Purchase Type No."), Rec.SystemId)
.SetExplanation(TempEDocLineMatchBuffer."GL Account Reason")
.SetConfidence(GetConfidenceForNumberOfProposedAccounts(TempEDocLineMatchBuffer."GL Account Candidate Count"))
.SetType(Enum::"Activity Log Type"::"AI")
.SetReferenceSource(Page::"G/L Account Card", RecordRef)
.SetReferenceTitle(StrSubstNo(ActivityLogTitleTxt, Rec."[BC] Purchase Type No."))
Expand All @@ -94,6 +95,18 @@ codeunit 6126 "E-Doc. GL Account Matching" implements "AOAI Function", IEDocAISy
EDocumentPurchaseLine.Validate("[BC] Purchase Type No.", ValueSuggested);
end;

/// <summary>
/// Gets confidence score based on number of proposed accounts.
/// If only one account is proposed, confidence is set to 2 (medium).
/// If more than one account is proposed, confidence is set to 3 (low).
/// </summary>
/// <param name="NumberOfProposedAccounts"></param>
/// <returns></returns>
local procedure GetConfidenceForNumberOfProposedAccounts(NumberOfProposedAccounts: Integer): Text
begin
exit(NumberOfProposedAccounts <= 1 ? 'Medium' : 'Low');
end;

local procedure CreateUserMessage(var EDocumentPurchaseLine: Record "E-Document Purchase Line"): Text
var
EDocumentPurchaseHeader: Record "E-Document Purchase Header";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ using Microsoft.eServices.EDocument.Processing.Import.Purchase;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.History;
using System.AI;
using Microsoft.Purchases.Vendor;
using System.Azure.KeyVault;
using System.Telemetry;
using System.Config;
Expand All @@ -25,9 +26,12 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
TempEDocLineMatchBuffer: Record "EDoc Line Match Buffer" temporary;
TempHistoricalMatchBuffer: Record "EDoc Historical Match Buffer" temporary;
EDocSimilarDescriptions: Codeunit "E-Doc. Similar Descriptions";
Telemetry: Codeunit Telemetry;
EDocumentNo: Integer;
HistoricalDataLoadFailedErr: Label 'Failed to load historical data for e-document line %1. Error: %2', Comment = '%1 = E-Document System Id, %2 = Error message', Locked = true;
AIHistoricalDataLoadEventTok: Label 'Historical Data Load', Locked = true;
ProductCodeTok: Label 'Product Code', Locked = true;
DescriptionTok: Label 'Description', Locked = true;

trigger OnRun()
var
Expand Down Expand Up @@ -71,7 +75,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy

// Update the purchase line with historical data
PurchInvLine.GetBySystemId(TempEDocLineMatchBuffer."Matched PurchInvLine SystemId");
EDocPurchaseHistMapping.UpdateMissingLineValuesFromHistory(PurchInvLine, Rec, TempEDocLineMatchBuffer."Historical Matching Reasoning");
EDocPurchaseHistMapping.UpdateMissingLineValuesFromHistory(PurchInvLine, Rec, TempEDocLineMatchBuffer."Historical Matching Reasoning", GetConfidenceScore(HistoricalMatchingConfig));
Rec.Modify(true);

EDocImpSessionTelemetry.SetLineBool(Rec.SystemId, AIHistoricalMatchEventTok, true);
Expand All @@ -85,6 +89,40 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
FeatureTelemetry.LogUsage('0000PUP', EDocumentAIProcessor.GetEDocumentMatchingAssistanceName(), GetFeatureName(), TelemetryDimensions);
end;

local procedure GetConfidenceScore(ExperimentConfig: Text): Text
begin
// When in control group we match exact vendor, hence score baseline is high confidence, else medium confidence

if TempHistoricalMatchBuffer.Count() > 1 then
// Lower confidence when multiple matches exist
case ExperimentConfig of
'control', '':
exit('Medium');
else
exit('Low');
end;

TempHistoricalMatchBuffer.Reset();
if TempHistoricalMatchBuffer.FindFirst() then
case TempHistoricalMatchBuffer."Match Type" of
ProductCodeTok, DescriptionTok:
case ExperimentConfig of
'control', '':
exit('High');
else
exit('Medium');
end;
else
case ExperimentConfig of
'control', '':
exit('Medium');
else
exit('Low');
end;
end;
end;


local procedure PrepareHistoricalData(var EDocumentPurchaseLine: Record "E-Document Purchase Line"; HistoricalMatchingConfig: Text): Boolean
var
EDocumentPurchaseHeader: Record "E-Document Purchase Header";
Expand All @@ -93,6 +131,9 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
VendorNo: Code[20];
EDocSystemId: Guid;
ErrorMessage: Text;
CouldNotFindHeaderErr: Label 'Could not find E-Document Purchase Header for E-Document Entry No. %1', Comment = '%1 = E-Document Entry No.', Locked = true;
NoUnmatchedLinesErr: Label 'No unmatched E-Document Purchase Lines found for E-Document Entry No. %1', Comment = '%1 = E-Document Entry No.', Locked = true;
HistoricalTempTableIsEmptyErr: Label 'No historical purchase invoice lines found for E-Document Entry No. %1', Comment = '%1 = E-Document Entry No.', Locked = true;
begin
if not EDocumentPurchaseLine.FindFirst() then
exit(false);
Expand All @@ -101,24 +142,29 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy

// Get vendor from header
EDocumentPurchaseHeader.SetRange("E-Document Entry No.", EDocumentPurchaseLine."E-Document Entry No.");
if not EDocumentPurchaseHeader.FindFirst() then
if not EDocumentPurchaseHeader.FindFirst() then begin
Telemetry.LogMessage('0000QLD', StrSubstNo(CouldNotFindHeaderErr, EDocumentPurchaseLine."E-Document Entry No."), Verbosity::Normal, DataClassification::SystemMetadata);
exit(false);
end;
VendorNo := EDocumentPurchaseHeader."[BC] Vendor No.";

// Ensure we only process unmatched lines
EDocumentPurchaseLine.SetRange("[BC] Purchase Type No.", '');
if not EDocumentPurchaseLine.FindSet() then
if not EDocumentPurchaseLine.FindSet() then begin
Telemetry.LogMessage('0000QLE', StrSubstNo(NoUnmatchedLinesErr, EDocumentPurchaseLine."E-Document Entry No."), Verbosity::Normal, DataClassification::SystemMetadata);
exit(false);
end;

// Load historical data with error handling
if not LoadHistoricalDataIntoTempTable(TempPurchInvLine, VendorNo, HistoricalMatchingConfig) then begin
ErrorMessage := GetLastErrorText();
FeatureTelemetry.LogError('0000PUQ', GetFeatureName(), AIHistoricalDataLoadEventTok, StrSubstNo(HistoricalDataLoadFailedErr, EDocSystemId, ErrorMessage), GetLastErrorCallStack());
exit(false);
end;

if TempPurchInvLine.IsEmpty() then
if TempPurchInvLine.IsEmpty() then begin
Telemetry.LogMessage('0000QLF', StrSubstNo(HistoricalTempTableIsEmptyErr, EDocumentPurchaseLine."E-Document Entry No."), Verbosity::Normal, DataClassification::SystemMetadata);
exit(false);
end;

// Collect potential matches
Clear(TempHistoricalMatchBuffer);
Expand Down Expand Up @@ -179,11 +225,11 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
repeat
// Search for exact Product Code matches
if EDocumentPurchaseLine."Product Code" <> '' then
SearchAndAddMatches(EDocumentPurchaseLine, TempPurchInvLine, 'Product Code', EDocumentPurchaseLine."Product Code", 1.0, VendorNo);
SearchAndAddMatches(EDocumentPurchaseLine, TempPurchInvLine, ProductCodeTok, EDocumentPurchaseLine."Product Code", 1.0, VendorNo);

// Search for exact Description matches
if EDocumentPurchaseLine.Description <> '' then
SearchAndAddMatches(EDocumentPurchaseLine, TempPurchInvLine, 'Description', EDocumentPurchaseLine.Description, 0.9, VendorNo);
SearchAndAddMatches(EDocumentPurchaseLine, TempPurchInvLine, DescriptionTok, EDocumentPurchaseLine.Description, 0.9, VendorNo);

// Search for similar descriptions
if EDocumentPurchaseLine.Description <> '' then
Expand All @@ -196,6 +242,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
SimilarDescriptions: List of [Text];
Description: Text;
begin
Telemetry.LogMessage('0000QLG', 'Processing similar descriptions for E-Document Line No. ' + Format(EDocumentPurchaseLine."Line No."), Verbosity::Verbose, DataClassification::SystemMetadata);
// Get similar descriptions using the simplified API
SimilarDescriptions := EDocSimilarDescriptions.GetSimilarDescriptions(EDocumentPurchaseLine.Description);

Expand All @@ -215,6 +262,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
MatchCount: Integer;
MatchReasonLbl: Label 'Matched on %1: %2', Comment = '%1 = Match Type, %2 = Search Value', Locked = true;
begin
Telemetry.LogMessage('0000QLH', 'Searching and adding matches for ' + MatchType + ' for E-Document Line No. ' + Format(EDocumentPurchaseLine."Line No."), Verbosity::Verbose, DataClassification::SystemMetadata);
if SearchValue = '' then
exit;

Expand All @@ -225,14 +273,15 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
MatchCount := 0;
TempPurchInvLine.Reset();
case MatchType of
'Product Code':
ProductCodeTok:
TempPurchInvLine.SetRange("No.", SearchValue);
'Description':
DescriptionTok:
TempPurchInvLine.SetRange(Description, SearchValue);
'Similar Description':
TempPurchInvLine.SetFilter(Description, '%1', '@*' + SearchValue + '*');
end;

Telemetry.LogMessage('0000QLI', 'Executing search for ' + MatchType + ' with value "' + SearchValue + '" for E-Document Line No. ' + Format(EDocumentPurchaseLine."Line No."), Verbosity::Verbose, DataClassification::SystemMetadata);
if TempPurchInvLine.FindSet() then
repeat
if not IsSimilarHistoricalRecordTracked(EDocumentPurchaseLine."Line No.", TempPurchInvLine) then
Expand All @@ -246,10 +295,11 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
Confidence := BaseConfidence * 0.8;

MatchReason := StrSubstNo(MatchReasonLbl, MatchType, SearchValue);
AddHistoricalMatchFromPurchInvLine(EDocumentPurchaseLine."Line No.", TempPurchInvLine, MatchReason, Confidence);
AddHistoricalMatchFromPurchInvLine(EDocumentPurchaseLine."Line No.", TempPurchInvLine, MatchReason, Confidence, MatchType);
MatchCount += 1;
end;
until (TempPurchInvLine.Next() = 0) or (MatchCount >= 5);
Telemetry.LogMessage('0000QLJ', 'Found and added ' + Format(MatchCount) + ' matches for ' + MatchType + ' for E-Document Line No. ' + Format(EDocumentPurchaseLine."Line No."), Verbosity::Verbose, DataClassification::SystemMetadata);
end;

local procedure IsSimilarHistoricalRecordTracked(LineNo: Integer; var PurchInvLine: Record "Purch. Inv. Line"): Boolean
Expand Down Expand Up @@ -284,7 +334,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
exit(SimilarRecordCount >= 5);
end;

local procedure AddHistoricalMatchFromPurchInvLine(LineNo: Integer; var PurchInvLine: Record "Purch. Inv. Line"; MatchReason: Text; Confidence: Decimal)
local procedure AddHistoricalMatchFromPurchInvLine(LineNo: Integer; var PurchInvLine: Record "Purch. Inv. Line"; MatchReason: Text; Confidence: Decimal; MatchType: Text)
begin
TempHistoricalMatchBuffer.Init();
TempHistoricalMatchBuffer."Line No." := LineNo;
Expand All @@ -296,6 +346,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
TempHistoricalMatchBuffer."Product Code" := CopyStr(PurchInvLine."No.", 1, MaxStrLen(TempHistoricalMatchBuffer."Product Code"));
TempHistoricalMatchBuffer.Description := CopyStr(PurchInvLine.Description, 1, MaxStrLen(TempHistoricalMatchBuffer.Description));
TempHistoricalMatchBuffer."Match Reason" := CopyStr(MatchReason, 1, MaxStrLen(TempHistoricalMatchBuffer."Match Reason"));
TempHistoricalMatchBuffer."Match Type" := CopyStr(MatchType, 1, MaxStrLen(TempHistoricalMatchBuffer."Match Type"));
TempHistoricalMatchBuffer."Confidence Score" := Confidence;
TempHistoricalMatchBuffer."Is E-Document History" := false;
TempHistoricalMatchBuffer.Quantity := PurchInvLine.Quantity;
Expand Down Expand Up @@ -415,6 +466,7 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy

local procedure BuildHistoricalMatchesJson(): JsonArray
var
Vendor: Record Vendor;
HistoricalMatches: JsonArray;
LineMatches: JsonObject;
MatchArray: JsonArray;
Expand Down Expand Up @@ -443,6 +495,8 @@ codeunit 6177 "E-Doc. Historical Matching" implements "AOAI Function", IEDocAISy
LineMatch.Add('historicalLineSystemId', TempHistoricalMatchBuffer."Historical Line SystemId");
LineMatch.Add('postingDate', TempHistoricalMatchBuffer."Posting Date");
LineMatch.Add('vendorNo', TempHistoricalMatchBuffer."Vendor No.");
if Vendor.Get(TempHistoricalMatchBuffer."Vendor No.") then
LineMatch.Add('vendorName', Vendor.Name);
LineMatch.Add('purchaseType', Format(TempHistoricalMatchBuffer."Purchase Type"));
LineMatch.Add('itemNo', TempHistoricalMatchBuffer."Purchase Type No.");
LineMatch.Add('productCode', TempHistoricalMatchBuffer."Product Code");
Expand Down
Loading
Loading