Skip to content
Closed
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
111 changes: 111 additions & 0 deletions Prototyping/Cod50100.PVSCaseHistoryMgmt.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/// <summary>
/// Populates the PVS Case History table from historical PrintVis cases.
/// </summary>
codeunit 50100 "PVS Case History Mgmt"
{
Access = Public;

trigger OnRun()
begin
PopulateHistory();
end;

/// <summary>
/// Clears and repopulates the Case History table with cases from the last N months
/// as configured in PVS AI Case Setup.
/// </summary>
procedure PopulateHistory()
var
Setup: Record "PVS AI Case Setup";
CaseHistory: Record "PVS Case History";
PVSCaseHeader: Record "PVS Case";
Customer: Record Customer;
PatternAnalysis: Codeunit "PVS Pattern Analysis";
FromDate: Date;
InsertCount: Integer;
begin
GetOrCreateSetup(Setup);
FromDate := CalcDate('<-' + Format(Setup."History Months") + 'M>', Today());

CaseHistory.DeleteAll(false);

PVSCaseHeader.SetFilter(PVSCaseHeader."Order Date", '>=%1', FromDate);
if PVSCaseHeader.FindSet() then
repeat
CaseHistory.Init();
CaseHistory."Case No." := CopyStr(Format(PVSCaseHeader.ID), 1, MaxStrLen(CaseHistory."Case No."));
CaseHistory."Customer No." := PVSCaseHeader."Customer No.";
CaseHistory."Job Type Code" := PVSCaseHeader."Job Type Code";
CaseHistory."Order Date" := PVSCaseHeader."Order Date";
CaseHistory."Due Date" := PVSCaseHeader."Due Date";
CaseHistory."Quantity" := PVSCaseHeader."Quantity 1";
CaseHistory."Total Amount" := PVSCaseHeader."Total Sales Price";
CaseHistory."Status" := Format(PVSCaseHeader.Status);
CaseHistory."Created Date" := PVSCaseHeader."Order Date";

if (CaseHistory."Order Date" <> 0D) and (CaseHistory."Due Date" <> 0D) and
(CaseHistory."Due Date" >= CaseHistory."Order Date")
then
CaseHistory."Turnaround Days" :=
CaseHistory."Due Date" - CaseHistory."Order Date";

if Customer.Get(PVSCaseHeader."Customer No.") then begin
CaseHistory."Customer Name" :=
CopyStr(Customer.Name, 1, MaxStrLen(CaseHistory."Customer Name"));
CaseHistory."Customer Segment" := Customer."Customer Posting Group";
end;

CaseHistory.Insert(false);
InsertCount += 1;
until PVSCaseHeader.Next() = 0;

Setup."Total Cases Loaded" := InsertCount;
Setup."Last Refresh Date" := CurrentDateTime();
Setup.Modify(true);

PatternAnalysis.AnalyzePatterns();
end;

/// <summary>
/// Creates a recurring Job Queue Entry that refreshes case history nightly.
/// </summary>
procedure CreateJobQueueEntry()
var
Setup: Record "PVS AI Case Setup";
JobQueueEntry: Record "Job Queue Entry";
begin
GetOrCreateSetup(Setup);

JobQueueEntry.Init();
JobQueueEntry."Object Type to Run" := JobQueueEntry."Object Type to Run"::Codeunit;
JobQueueEntry."Object ID to Run" := Codeunit::"PVS Case History Mgmt";
JobQueueEntry."Job Queue Category Code" := Setup."Job Queue Category Code";
JobQueueEntry."Recurring Job" := true;
JobQueueEntry."No. of Minutes between Runs" := 1440;
JobQueueEntry."Starting Time" := 020000T;
JobQueueEntry."Run on Mondays" := true;
JobQueueEntry."Run on Tuesdays" := true;
JobQueueEntry."Run on Wednesdays" := true;
JobQueueEntry."Run on Thursdays" := true;
JobQueueEntry."Run on Fridays" := true;
JobQueueEntry."Run on Saturdays" := false;
JobQueueEntry."Run on Sundays" := false;
JobQueueEntry.Description := CopyStr(
'Refresh PVS Case History (AI)', 1, MaxStrLen(JobQueueEntry.Description));
JobQueueEntry.Insert(true);

Codeunit.Run(Codeunit::"Job Queue - Enqueue", JobQueueEntry);

Message('Job Queue Entry created. Case history will refresh nightly at 02:00.');
end;

local procedure GetOrCreateSetup(var Setup: Record "PVS AI Case Setup")
begin
if not Setup.Get('DEFAULT') then begin
Setup.Init();
Setup.Code := 'DEFAULT';
Setup."History Months" := 12;
Setup.Insert(true);
end;
end;
}
158 changes: 158 additions & 0 deletions Prototyping/Cod50101.PVSPatternAnalysis.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/// <summary>
/// Analyses Case History records to produce pattern summaries stored in PVS Case Pattern.
/// Patterns group cases by Job Type + Material + Customer Segment and compute statistical
/// summaries (averages, min/max, confidence score) used to guide Copilot suggestions.
/// </summary>
codeunit 50101 "PVS Pattern Analysis"
{
Access = Public;

/// <summary>
/// Rebuilds the PVS Case Pattern table from all current PVS Case History records.
/// </summary>
procedure AnalyzePatterns()
var
Setup: Record "PVS AI Case Setup";
CasePattern: Record "PVS Case Pattern";
CaseHistory: Record "PVS Case History";
TotalAmount: Decimal;
MinAmount: Decimal;
MaxAmount: Decimal;
TotalQuantity: Decimal;
TotalTurnaround: Decimal;
CaseCount: Integer;
PatternCount: Integer;
CurrentJobType: Code[20];
CurrentMaterial: Code[20];
CurrentSegment: Text[50];
begin
CasePattern.DeleteAll(false);

CaseHistory.SetCurrentKey(CaseHistory."Job Type Code", CaseHistory."Material Code", CaseHistory."Customer Segment");
if not CaseHistory.FindSet() then
exit;

CurrentJobType := CaseHistory."Job Type Code";
CurrentMaterial := CaseHistory."Material Code";
CurrentSegment := CaseHistory."Customer Segment";
InitAccumulators(TotalAmount, MinAmount, MaxAmount, TotalQuantity, TotalTurnaround, CaseCount);

repeat
if (CaseHistory."Job Type Code" <> CurrentJobType) or
(CaseHistory."Material Code" <> CurrentMaterial) or
(CaseHistory."Customer Segment" <> CurrentSegment)
then begin
WritePattern(
CasePattern, CurrentJobType, CurrentMaterial, CurrentSegment,
TotalAmount, MinAmount, MaxAmount, TotalQuantity, TotalTurnaround, CaseCount);
PatternCount += 1;

CurrentJobType := CaseHistory."Job Type Code";
CurrentMaterial := CaseHistory."Material Code";
CurrentSegment := CaseHistory."Customer Segment";
InitAccumulators(
TotalAmount, MinAmount, MaxAmount, TotalQuantity, TotalTurnaround, CaseCount);
end;

AccumulateCase(
CaseHistory, TotalAmount, MinAmount, MaxAmount,
TotalQuantity, TotalTurnaround, CaseCount);
until CaseHistory.Next() = 0;

// Write the last group
if CaseCount > 0 then begin
WritePattern(
CasePattern, CurrentJobType, CurrentMaterial, CurrentSegment,
TotalAmount, MinAmount, MaxAmount, TotalQuantity, TotalTurnaround, CaseCount);
PatternCount += 1;
end;

if Setup.Get('DEFAULT') then begin
Setup."Total Patterns Found" := PatternCount;
Setup.Modify(true);
end;
end;

local procedure InitAccumulators(
var TotalAmount: Decimal;
var MinAmount: Decimal;
var MaxAmount: Decimal;
var TotalQuantity: Decimal;
var TotalTurnaround: Decimal;
var CaseCount: Integer)
begin
TotalAmount := 0;
MinAmount := 0;
MaxAmount := 0;
TotalQuantity := 0;
TotalTurnaround := 0;
CaseCount := 0;
end;

local procedure AccumulateCase(
CaseHistory: Record "PVS Case History";
var TotalAmount: Decimal;
var MinAmount: Decimal;
var MaxAmount: Decimal;
var TotalQuantity: Decimal;
var TotalTurnaround: Decimal;
var CaseCount: Integer)
begin
CaseCount += 1;
TotalAmount += CaseHistory."Total Amount";
TotalQuantity += CaseHistory.Quantity;
TotalTurnaround += CaseHistory."Turnaround Days";

if CaseCount = 1 then begin
MinAmount := CaseHistory."Total Amount";
MaxAmount := CaseHistory."Total Amount";
end else begin
if CaseHistory."Total Amount" < MinAmount then
MinAmount := CaseHistory."Total Amount";
if CaseHistory."Total Amount" > MaxAmount then
MaxAmount := CaseHistory."Total Amount";
end;
end;

local procedure WritePattern(
var CasePattern: Record "PVS Case Pattern";
JobTypeCode: Code[20];
MaterialCode: Code[20];
CustomerSegment: Text[50];
TotalAmount: Decimal;
MinAmount: Decimal;
MaxAmount: Decimal;
TotalQuantity: Decimal;
TotalTurnaround: Decimal;
CaseCount: Integer)
begin
CasePattern.Init();
CasePattern."Job Type Code" := JobTypeCode;
CasePattern."Material Code" := MaterialCode;
CasePattern."Customer Segment" :=
CopyStr(CustomerSegment, 1, MaxStrLen(CasePattern."Customer Segment"));
CasePattern."Case Count" := CaseCount;
CasePattern."Min Total Amount" := MinAmount;
CasePattern."Max Total Amount" := MaxAmount;
CasePattern."Last Updated" := CurrentDateTime();

if CaseCount > 0 then begin
CasePattern."Avg Total Amount" := Round(TotalAmount / CaseCount, 0.01);
CasePattern."Avg Quantity" := Round(TotalQuantity / CaseCount, 0.01);
CasePattern."Avg Turnaround Days" := Round(TotalTurnaround / CaseCount, 0.1);
end;

// Confidence = min(100, sqrt(CaseCount) * 20) — more cases = higher confidence
CasePattern."Confidence Score" :=
Round(MinDecimal(100, Power(CaseCount, 0.5) * 20), 0.1);

CasePattern.Insert(false);
end;

local procedure MinDecimal(A: Decimal; B: Decimal): Decimal
begin
if A < B then
exit(A);
exit(B);
end;
}
Loading
Loading