diff --git a/Apps/US/HybridGP_US/app/Permissions/HybridGPUSObjects.PermissionSet.al b/Apps/US/HybridGP_US/app/Permissions/HybridGPUSObjects.PermissionSet.al
index eba1882ab5..78a0a0f5b5 100644
--- a/Apps/US/HybridGP_US/app/Permissions/HybridGPUSObjects.PermissionSet.al
+++ b/Apps/US/HybridGP_US/app/Permissions/HybridGPUSObjects.PermissionSet.al
@@ -17,5 +17,6 @@ permissionset 4713 "HybridGPUS - Objects"
codeunit "GP Vendor 1099 Mapping Helpers" = X,
codeunit "GP IRS Form Data" = X,
page "GP 1099 Migration Log" = X,
- page "GP 1099 Migration Log Factbox" = X;
+ page "GP 1099 Migration Log Factbox" = X,
+ codeunit "GP IRS1099 Migration Validator" = X;
}
\ No newline at end of file
diff --git a/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al b/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al
index 2f9e15f05d..2ae37e5881 100644
--- a/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al
+++ b/Apps/US/HybridGP_US/app/src/Codeunits/GPCloudMigrationUS.Codeunit.al
@@ -5,13 +5,12 @@ using Microsoft.Purchases.Vendor;
codeunit 42004 "GP Cloud Migration US"
{
-
- [EventSubscriber(ObjectType::Codeunit, CodeUnit::"Data Migration Mgt.", 'OnAfterMigrationFinished', '', false, false)]
- local procedure OnAfterMigrationFinishedSubscriber(var DataMigrationStatus: Record "Data Migration Status"; WasAborted: Boolean; StartTime: DateTime; Retry: Boolean)
+ [EventSubscriber(ObjectType::Codeunit, CodeUnit::"Data Migration Mgt.", OnCreatePostMigrationData, '', false, false)]
+ local procedure OnCreatePostMigrationDataSubscriber(var DataMigrationStatus: Record "Data Migration Status")
var
HelperFunctions: Codeunit "Helper Functions";
begin
- if not (DataMigrationStatus."Migration Type" = HelperFunctions.GetMigrationTypeTxt()) then
+ if DataMigrationStatus."Migration Type" <> HelperFunctions.GetMigrationTypeTxt() then
exit;
RunPostMigration();
diff --git a/Apps/US/HybridGP_US/app/src/Codeunits/GPIRS1099MigrationValidator.Codeunit.al b/Apps/US/HybridGP_US/app/src/Codeunits/GPIRS1099MigrationValidator.Codeunit.al
new file mode 100644
index 0000000000..9c26b18280
--- /dev/null
+++ b/Apps/US/HybridGP_US/app/src/Codeunits/GPIRS1099MigrationValidator.Codeunit.al
@@ -0,0 +1,203 @@
+namespace Microsoft.DataMigration.GP;
+
+using Microsoft.Purchases.Vendor;
+using Microsoft.Purchases.Payables;
+using Microsoft.Finance.VAT.Reporting;
+using Microsoft.DataMigration;
+
+codeunit 42006 "GP IRS1099 Migration Validator"
+{
+ trigger OnRun()
+ var
+ GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
+ begin
+ if not GPCompanyAdditionalSettings.GetMigrateVendor1099Enabled() then
+ exit;
+
+ ValidatorCodeLbl := GetValidatorCode();
+ CompanyNameTxt := CompanyName();
+
+ RunVendor1099MigrationValidation(GPCompanyAdditionalSettings);
+
+ MigrationValidation.ReportCompanyValidated();
+ end;
+
+ local procedure RunVendor1099MigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GPPM00200: Record "GP PM00200";
+ Vendor: Record Vendor;
+ VendorLedgerEntry: Record "Vendor Ledger Entry";
+ IRS1099VendorFormBoxSetup: Record "IRS 1099 Vendor Form Box Setup";
+ GPVendor1099MappingHelpers: Codeunit "GP Vendor 1099 Mapping Helpers";
+ IRS1099Code: Code[10];
+ ActualIRS1099Code: Code[20];
+ TaxAmount: Decimal;
+ VendorYear1099AmountDictionary: Dictionary of [Code[10], Decimal];
+ EntityType: Text[50];
+ VendorNo: Code[20];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepVendor1099Lbl) then
+ exit;
+
+ EntityType := Vendor1099EntityCaptionLbl;
+
+ if GPCompanyAdditionalSettings.GetMigrateVendor1099Enabled() then begin
+ GPPM00200.SetRange(TEN99TYPE, 2, 5);
+ GPPM00200.SetFilter(VENDORID, '<>%1', '');
+ if GPPM00200.FindSet() then
+ repeat
+ VendorNo := CopyStr(GPPM00200.VENDORID.TrimEnd(), 1, MaxStrLen(VendorNo));
+ Vendor.SetLoadFields("No.", Name, "Federal ID No.");
+ if not Vendor.Get(VendorNo) then
+ continue;
+
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, VendorNo);
+ IRS1099Code := GPVendor1099MappingHelpers.GetIRS1099BoxCode(System.Date2DMY(System.Today(), 3), GPPM00200.TEN99TYPE, GPPM00200.TEN99BOXNUMBER);
+
+ Clear(ActualIRS1099Code);
+ if IRS1099VendorFormBoxSetup.Get(Format(GPCompanyAdditionalSettings.Get1099TaxYear()), VendorNo) then
+ ActualIRS1099Code := IRS1099VendorFormBoxSetup."Form Box No.";
+
+ MigrationValidation.ValidateAreEqual(Test_VEND1099IRS1099CODE_Tok, IRS1099Code, ActualIRS1099Code, IRS1099CodeLbl);
+ MigrationValidation.ValidateAreEqual(Test_VEND1099FEDIDNO_Tok, CopyStr(GPPM00200.TXIDNMBR.TrimEnd(), 1, MaxStrLen(Vendor."Federal ID No.")), Vendor."Federal ID No.", FederalIdNoLbl);
+
+ Clear(VendorYear1099AmountDictionary);
+ BuildVendor1099Entries(VendorNo, VendorYear1099AmountDictionary);
+ foreach IRS1099Code in VendorYear1099AmountDictionary.Keys() do begin
+ TaxAmount := VendorYear1099AmountDictionary.Get(IRS1099Code);
+
+ if TaxAmount > 0 then begin
+ Clear(VendorLedgerEntry);
+ VendorLedgerEntry.SetLoadFields(Description, Amount);
+ VendorLedgerEntry.SetRange("Vendor No.", VendorNo);
+ VendorLedgerEntry.SetRange("Document Type", VendorLedgerEntry."Document Type"::Payment);
+ VendorLedgerEntry.SetRange(Description, IRS1099Code);
+
+ if not MigrationValidation.ValidateRecordExists(Test_VEND1099TRXEXISTS_Tok, VendorLedgerEntry.FindFirst(), StrSubstNo(MissingBoxAndAmountLbl, IRS1099Code, TaxAmount)) then
+ continue;
+
+ VendorLedgerEntry.CalcFields(Amount);
+
+ MigrationValidation.ValidateAreEqual(Test_VEND1099TEN99BOX_Tok, IRS1099Code, VendorLedgerEntry.Description, Vendor1099BoxLbl);
+ MigrationValidation.ValidateAreEqual(Test_VEND1099TEN99TRXAMT_Tok, TaxAmount, VendorLedgerEntry.Amount, Vendor1099BoxAmountLbl);
+ end;
+ end;
+
+ until GPPM00200.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepVendor1099Lbl);
+ Commit();
+ end;
+
+ local procedure BuildVendor1099Entries(VendorNo: Code[20]; var VendorYear1099AmountDictionary: Dictionary of [Code[10], Decimal])
+ var
+ GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
+ GPPM00204: Record "GP PM00204";
+ GPVendor1099MappingHelpers: Codeunit "GP Vendor 1099 Mapping Helpers";
+ IRS1099Code: Code[10];
+ TaxAmount: Decimal;
+ TaxYear: Integer;
+ begin
+ TaxYear := GPCompanyAdditionalSettings.Get1099TaxYear();
+ GPPM00204.SetRange(VENDORID, VendorNo);
+ GPPM00204.SetRange(YEAR1, TaxYear);
+ GPPM00204.SetFilter(TEN99AMNT, '>0');
+ if GPPM00204.FindSet() then
+ repeat
+ IRS1099Code := GPVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, GPPM00204.TEN99TYPE, GPPM00204.TEN99BOXNUMBER);
+ if IRS1099Code <> '' then
+ if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then
+ VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + GPPM00204.TEN99AMNT)
+ else
+ VendorYear1099AmountDictionary.Add(IRS1099Code, GPPM00204.TEN99AMNT);
+ until GPPM00204.Next() = 0;
+ end;
+
+ local procedure LogValidationProgress(ValidationStep: Code[20])
+ begin
+ Clear(CompanyValidationProgress);
+ CompanyValidationProgress.Validate("Company Name", CompanyNameTxt);
+ CompanyValidationProgress.Validate("Validator Code", ValidatorCodeLbl);
+ CompanyValidationProgress.Validate("Validation Step", ValidationStep);
+ CompanyValidationProgress.Insert(true);
+ end;
+
+ internal procedure GetValidatorCode(): Code[20]
+ begin
+ exit('GP-US');
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Hybrid Cloud Management", OnPrepareMigrationValidation, '', false, false)]
+ local procedure OnPrepareMigrationValidation(ProductID: Text[250])
+ var
+ HybridGPWizard: Codeunit "Hybrid GP Wizard";
+ begin
+ if ProductID <> HybridGPWizard.ProductId() then
+ exit;
+
+ RegisterValidator();
+
+ AddTest(Test_VEND1099IRS1099CODE_Tok, Vendor1099EntityCaptionLbl, IRS1099CodeLbl);
+ AddTest(Test_VEND1099FEDIDNO_Tok, Vendor1099EntityCaptionLbl, FederalIdNoLbl);
+ AddTest(Test_VEND1099TRXEXISTS_Tok, Vendor1099EntityCaptionLbl, Vendor1099MissingTrxLbl);
+ AddTest(Test_VEND1099TEN99BOX_Tok, Vendor1099EntityCaptionLbl, Vendor1099TrxBoxNoLbl);
+ AddTest(Test_VEND1099TEN99TRXAMT_Tok, Vendor1099EntityCaptionLbl, Vendor1099TrxAmtLbl);
+ end;
+
+ local procedure RegisterValidator()
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ HybridGPWizard: Codeunit "Hybrid GP Wizard";
+ ValidatorCode: Code[20];
+ MigrationType: Text[250];
+ ValidatorCodeunitId: Integer;
+ begin
+ ValidatorCode := GetValidatorCode();
+ MigrationType := HybridGPWizard.ProductId();
+ ValidatorCodeunitId := Codeunit::"GP IRS1099 Migration Validator";
+ if not MigrationValidatorRegistry.Get(ValidatorCode) then begin
+ MigrationValidatorRegistry.Validate("Validator Code", ValidatorCode);
+ MigrationValidatorRegistry.Validate("Migration Type", MigrationType);
+ MigrationValidatorRegistry.Validate(Description, ValidatorDescriptionLbl);
+ MigrationValidatorRegistry.Validate("Codeunit Id", ValidatorCodeunitId);
+ MigrationValidatorRegistry.Validate(Automatic, false);
+ MigrationValidatorRegistry.Insert(true);
+ end;
+ end;
+
+ local procedure AddTest(Code: Code[30]; Entity: Text[50]; Description: Text)
+ var
+ MigrationValidationTest: Record "Migration Validation Test";
+ begin
+ if not MigrationValidationTest.Get(Code, GetValidatorCode()) then begin
+ MigrationValidationTest.Validate(Code, Code);
+ MigrationValidationTest.Validate("Validator Code", GetValidatorCode());
+ MigrationValidationTest.Validate(Entity, Entity);
+ MigrationValidationTest.Validate("Test Description", Description);
+ MigrationValidationTest.Insert(true);
+ end;
+ end;
+
+ var
+ CompanyValidationProgress: Record "Company Validation Progress";
+ MigrationValidation: Codeunit "Migration Validation";
+ ValidatorCodeLbl: Code[20];
+ CompanyNameTxt: Text;
+ FederalIdNoLbl: Label 'Federal ID No.';
+ IRS1099CodeLbl: Label 'IRS 1099 Code';
+ MissingBoxAndAmountLbl: Label 'Missing 1099 Box Payment. 1099 Box = %1, Amount = %2', Comment = '%1 = 1099 Box Code, %2 = Amount of the payment';
+ Vendor1099BoxLbl: Label '1099 Box';
+ Vendor1099BoxAmountLbl: Label '1099 Box Amount';
+ Vendor1099MissingTrxLbl: Label 'Missing 1099 transaction';
+ Vendor1099TrxBoxNoLbl: Label '1099 transaction Box No/Description';
+ Vendor1099TrxAmtLbl: Label '1099 transaction amount';
+ Vendor1099EntityCaptionLbl: Label 'Vendor 1099', MaxLength = 50;
+ ValidationStepVendor1099Lbl: Label 'VENDOR1099', MaxLength = 20;
+ ValidatorDescriptionLbl: Label 'GP IRS 1099 migration validator', MaxLength = 250;
+ Test_VEND1099IRS1099CODE_Tok: Label 'VEND1099IRS1099CODE', Locked = true;
+ Test_VEND1099FEDIDNO_Tok: Label 'VEND1099FEDIDNO', Locked = true;
+ Test_VEND1099TRXEXISTS_Tok: Label 'VEND1099TRXEXISTS', Locked = true;
+ Test_VEND1099TEN99BOX_Tok: Label 'VEND1099TEN99BOX', Locked = true;
+ Test_VEND1099TEN99TRXAMT_Tok: Label 'VEND1099TEN99TRXAMT', Locked = true;
+}
\ No newline at end of file
diff --git a/Apps/US/HybridGP_US/app/src/PageExt/GPUpgradeSettingsExt.PageExt.al b/Apps/US/HybridGP_US/app/src/PageExt/GPUpgradeSettingsExt.PageExt.al
new file mode 100644
index 0000000000..9a2be1df0c
--- /dev/null
+++ b/Apps/US/HybridGP_US/app/src/PageExt/GPUpgradeSettingsExt.PageExt.al
@@ -0,0 +1,43 @@
+namespace Microsoft.DataMigration.GP;
+
+using Microsoft.DataMigration;
+
+pageextension 41105 "GP Upgrade Settings Ext" extends "GP Upgrade Settings"
+{
+ layout
+ {
+ addafter(GPAutomaticValidation)
+ {
+ field(GPUSAutomaticValidation; GPIRS1099AutoValidation)
+ {
+ ApplicationArea = All;
+ Caption = 'GP-US (1099)';
+ ToolTip = 'Specifies whether automatic validation is enabled for the GP-US (1099) migration.';
+
+ trigger OnValidate()
+ var
+ MigrationValidationRegistry: Record "Migration Validator Registry";
+ GPIRS1099MigrtionValidator: Codeunit "GP IRS1099 Migration Validator";
+ begin
+ if MigrationValidationRegistry.Get(GPIRS1099MigrtionValidator.GetValidatorCode()) then begin
+ MigrationValidationRegistry.Validate(Automatic, GPIRS1099AutoValidation);
+ MigrationValidationRegistry.Modify(true);
+ end;
+ end;
+ }
+ }
+ }
+
+ trigger OnOpenPage()
+ var
+ MigrationValidationRegistry: Record "Migration Validator Registry";
+ GPIRS1099MigrtionValidator: Codeunit "GP IRS1099 Migration Validator";
+ begin
+ if MigrationValidationRegistry.Get(GPIRS1099MigrtionValidator.GetValidatorCode()) then
+ GPIRS1099AutoValidation := MigrationValidationRegistry.Automatic;
+
+ end;
+
+ var
+ GPIRS1099AutoValidation: Boolean;
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/Permissions/HBDObjects.PermissionSet.al b/Apps/W1/HybridBaseDeployment/app/Permissions/HBDObjects.PermissionSet.al
index 1e77694adb..57717e3328 100644
--- a/Apps/W1/HybridBaseDeployment/app/Permissions/HBDObjects.PermissionSet.al
+++ b/Apps/W1/HybridBaseDeployment/app/Permissions/HBDObjects.PermissionSet.al
@@ -9,5 +9,14 @@ permissionset 4006 "HBD - Objects"
Permissions = page "Hybrid DA Approval" = X,
page "Add Migration Table Mappings" = X,
table "Hybrid Company Status" = X,
- table "Hybrid DA Approval" = X;
+ table "Hybrid DA Approval" = X,
+ table "Migration Validation Error" = X,
+ page "Migration Validation Errors" = X,
+ codeunit "Migration Validation" = X,
+ page "Company Migration Status" = X,
+ table "Migration Validation Test" = X,
+ page "Migration Validation Results" = X,
+ table "Company Validation Progress" = X,
+ table "Migration Validation Buffer" = X,
+ codeunit "Migration Validator Warning" = X;
}
diff --git a/Apps/W1/HybridBaseDeployment/app/Permissions/INTELLIGENTCLOUDHBD.PermissionSetExt.al b/Apps/W1/HybridBaseDeployment/app/Permissions/INTELLIGENTCLOUDHBD.PermissionSetExt.al
index fef39ac989..ac434d385f 100644
--- a/Apps/W1/HybridBaseDeployment/app/Permissions/INTELLIGENTCLOUDHBD.PermissionSetExt.al
+++ b/Apps/W1/HybridBaseDeployment/app/Permissions/INTELLIGENTCLOUDHBD.PermissionSetExt.al
@@ -18,5 +18,10 @@ permissionsetextension 4003 "INTELLIGENT CLOUD - HBD" extends "INTELLIGENT CLOUD
tabledata "Hybrid DA Approval" = rmi,
tabledata "Replication Record Link Buffer" = RIMD,
tabledata "Record Link Mapping" = RIMD,
- tabledata "Cloud Migration Warning" = RIMD;
+ tabledata "Cloud Migration Warning" = RIMD,
+ tabledata "Migration Validator Registry" = RIMD,
+ tabledata "Migration Validation Error" = RIMD,
+ tabledata "Migration Validation Test" = RIMD,
+ tabledata "Company Validation Progress" = RIMD,
+ tabledata "Migration Validation Buffer" = RIMD;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicHBD.permissionsetext.al b/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicHBD.permissionsetext.al
index 58ca3aad49..b2bdc6a6b5 100644
--- a/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicHBD.permissionsetext.al
+++ b/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicHBD.permissionsetext.al
@@ -18,5 +18,10 @@ permissionsetextension 4000 "D365 BASIC - HBD" extends "D365 BASIC"
tabledata "Replication Run Completed Arg" = RIMD,
tabledata "Replication Record Link Buffer" = RIMD,
tabledata "Record Link Mapping" = RIMD,
- tabledata "Cloud Migration Warning" = RIMD;
+ tabledata "Cloud Migration Warning" = RIMD,
+ tabledata "Migration Validator Registry" = RIMD,
+ tabledata "Migration Validation Error" = RIMD,
+ tabledata "Migration Validation Test" = RIMD,
+ tabledata "Company Validation Progress" = RIMD,
+ tabledata "Migration Validation Buffer" = RIMD;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicisvHBD.permissionsetext.al b/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicisvHBD.permissionsetext.al
index da1e3cb429..f2cf5e4f9e 100644
--- a/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicisvHBD.permissionsetext.al
+++ b/Apps/W1/HybridBaseDeployment/app/Permissions/d365basicisvHBD.permissionsetext.al
@@ -19,5 +19,10 @@ permissionsetextension 4001 "D365 BASIC ISV - HBD" extends "D365 BASIC ISV"
tabledata "Hybrid DA Approval" = rmi,
tabledata "Replication Record Link Buffer" = RIMD,
tabledata "Record Link Mapping" = RIMD,
- tabledata "Cloud Migration Warning" = RIMD;
+ tabledata "Cloud Migration Warning" = RIMD,
+ tabledata "Migration Validator Registry" = RIMD,
+ tabledata "Migration Validation Error" = RIMD,
+ tabledata "Migration Validation Test" = RIMD,
+ tabledata "Company Validation Progress" = RIMD,
+ tabledata "Migration Validation Buffer" = RIMD;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/Permissions/d365teammemberHBD.permissionsetext.al b/Apps/W1/HybridBaseDeployment/app/Permissions/d365teammemberHBD.permissionsetext.al
index fb4a7b0b25..7432543007 100644
--- a/Apps/W1/HybridBaseDeployment/app/Permissions/d365teammemberHBD.permissionsetext.al
+++ b/Apps/W1/HybridBaseDeployment/app/Permissions/d365teammemberHBD.permissionsetext.al
@@ -17,5 +17,10 @@ permissionsetextension 4002 "D365 TEAM MEMBER - HBD" extends "D365 TEAM MEMBER"
tabledata "Replication Run Completed Arg" = RIMD,
tabledata "Replication Record Link Buffer" = RIMD,
tabledata "Record Link Mapping" = RIMD,
- tabledata "Cloud Migration Warning" = RIMD;
+ tabledata "Cloud Migration Warning" = RIMD,
+ tabledata "Migration Validator Registry" = RIMD,
+ tabledata "Migration Validation Error" = RIMD,
+ tabledata "Migration Validation Test" = RIMD,
+ tabledata "Company Validation Progress" = RIMD,
+ tabledata "Migration Validation Buffer" = RIMD;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/codeunits/HybridCloudManagement.Codeunit.al b/Apps/W1/HybridBaseDeployment/app/src/codeunits/HybridCloudManagement.Codeunit.al
index f3bfa762df..ecb6a68f0d 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/codeunits/HybridCloudManagement.Codeunit.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/codeunits/HybridCloudManagement.Codeunit.al
@@ -649,6 +649,7 @@ codeunit 4001 "Hybrid Cloud Management"
IntelligentCloudSetup.Validate("Replication User", UserId());
IntelligentCloudSetup.Modify();
RestoreDefaultMigrationTableMappings(false);
+ PrepareMigrationValidation();
RefreshIntelligentCloudStatusTable();
CreateCompanies();
@@ -941,6 +942,14 @@ codeunit 4001 "Hybrid Cloud Management"
OnInsertDefaultTableMappings(IntelligentCloudSetup."Product ID", DeleteExisting);
end;
+ procedure PrepareMigrationValidation()
+ IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ begin
+ if IntelligentCloudSetup.Get() then;
+
+ OnPrepareMigrationValidation(IntelligentCloudSetup."Product ID");
+ end;
+
procedure CompleteCloudMigration()
var
IntelligentCloudSetup: Record "Intelligent Cloud Setup";
@@ -2223,6 +2232,23 @@ codeunit 4001 "Hybrid Cloud Management"
RecordLink.SetRange("To User ID", OldUserName);
RecordLink.ModifyAll("To User ID", NewUserName);
end;
+
+ local procedure ClearCompanyMigrationValidation(MigrationType: Text[250])
+ var
+ MigrationValidationError: Record "Migration Validation Error";
+ CompanyValidationProgress: Record "Company Validation Progress";
+ begin
+ if MigrationType <> '' then
+ MigrationValidationError.SetRange("Migration Type", MigrationType);
+
+ MigrationValidationError.SetRange("Company Name", CompanyName());
+ if not MigrationValidationError.IsEmpty() then
+ MigrationValidationError.DeleteAll();
+
+ CompanyValidationProgress.SetRange("Company Name", CompanyName());
+ if not CompanyValidationProgress.IsEmpty() then
+ CompanyValidationProgress.DeleteAll();
+ end;
[EventSubscriber(ObjectType::Page, Page::Companies, 'OnOpenPageEvent', '', false, false)]
local procedure WarnNotToManageCompaniesManually(var Rec: Record Company)
@@ -2245,6 +2271,48 @@ codeunit 4001 "Hybrid Cloud Management"
SendSetupWebhooksNotification.AddAction(DontShowAgainMsg, Codeunit::"Hybrid Cloud Management", 'DontShowCompaniesWarningNotification');
SendSetupWebhooksNotification.Send();
end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Data Migration Mgt.", OnBeforeMigrationStarted, '', false, false)]
+ local procedure BeforeMigrationStarted(var DataMigrationStatus: Record "Data Migration Status"; Retry: Boolean)
+ begin
+ ClearCompanyMigrationValidation(DataMigrationStatus."Migration Type");
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Data Migration Mgt.", OnValidateMigration, '', false, false)]
+ local procedure StartMigrationValidation(var DataCreationFailed: Boolean)
+ begin
+ StartMigrationValidationImp(DataCreationFailed);
+ end;
+
+ internal procedure StartMigrationValidationImp(var DataCreationFailed: Boolean)
+ var
+ IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ MigrationValidationError: Record "Migration Validation Error";
+ MigrationValidation: Codeunit "Migration Validation";
+ begin
+ if DataCreationFailed then
+ exit;
+
+ if not IntelligentCloudSetup.Get() then
+ exit;
+
+ MigrationValidation.StartValidation(IntelligentCloudSetup."Product ID", true);
+
+ MigrationValidationError.SetRange("Migration Type", IntelligentCloudSetup."Product ID");
+ MigrationValidationError.SetRange("Company Name", CompanyName());
+ MigrationValidationError.SetRange("Is Warning", false);
+ if not MigrationValidationError.IsEmpty() then
+ DataCreationFailed := true;
+ end;
+
+ [EventSubscriber(ObjectType::Table, Database::"Company", OnAfterDeleteEvent, '', false, false)]
+ local procedure CleanupAfterCompanyDelete(var Rec: Record Company; RunTrigger: Boolean)
+ begin
+ if Rec.IsTemporary() then
+ exit;
+
+ ClearCompanyMigrationValidation('');
+ end;
[IntegrationEvent(false, false)]
local procedure OnBackupDataPerDatabase(ProductID: Text[250]; var Handled: Boolean)
@@ -2271,6 +2339,11 @@ codeunit 4001 "Hybrid Cloud Management"
begin
end;
+ [IntegrationEvent(false, false)]
+ local procedure OnPrepareMigrationValidation(ProductID: Text[250])
+ begin
+ end;
+
[IntegrationEvent(false, false)]
local procedure OnHandleRunReplication(var Handled: Boolean; var RunId: Text; ReplicationType: Option)
begin
diff --git a/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidation.Codeunit.al b/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidation.Codeunit.al
new file mode 100644
index 0000000000..2170f743d6
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidation.Codeunit.al
@@ -0,0 +1,509 @@
+namespace Microsoft.DataMigration;
+
+using System.Reflection;
+using System.Threading;
+using System.Security.User;
+
+codeunit 40032 "Migration Validation"
+{
+ trigger OnRun()
+ var
+ IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ begin
+ if not IntelligentCloudSetup.Get() then
+ exit;
+
+ StartValidation(IntelligentCloudSetup."Product ID", false);
+ end;
+
+ ///
+ /// Start migration validation for the current company
+ ///
+ /// The type of migration
+ /// Should only run validators that are set to run automatically
+ procedure StartValidation(MigrationType: Text; RunOnlyAutomatic: Boolean)
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ CloudMigrationWarning: Record "Cloud Migration Warning";
+ begin
+ MigrationValidatorRegistry.SetRange("Migration Type", MigrationType);
+
+ if RunOnlyAutomatic then
+ MigrationValidatorRegistry.SetRange(Automatic, true);
+
+ if not MigrationValidatorRegistry.FindSet() then
+ exit;
+
+ repeat
+ Commit();
+ if not Codeunit.Run(MigrationValidatorRegistry."Codeunit Id") then begin
+ Clear(CloudMigrationWarning);
+ CloudMigrationWarning."Entry No." := 0;
+ CloudMigrationWarning."Warning Type" := CloudMigrationWarning."Warning Type"::"Migration Validator";
+ CloudMigrationWarning.Message := CopyStr(StrSubstNo(CloudMigrationWarningErr, MigrationValidatorRegistry."Validator Code", GetLastErrorText()), 1, MaxStrLen(CloudMigrationWarning.Message));
+ CloudMigrationWarning.Insert();
+ end;
+ until MigrationValidatorRegistry.Next() = 0;
+
+ OnMigrationValidated();
+ end;
+
+ ///
+ /// Report that the Company has had validation tests run.
+ ///
+ procedure ReportCompanyValidated()
+ var
+ HybridCompanyStatus: Record "Hybrid Company Status";
+ begin
+ if HybridCompanyStatus.Get(CompanyName()) then
+ if not HybridCompanyStatus.Validated then begin
+ HybridCompanyStatus.Validate(Validated, true);
+ HybridCompanyStatus.Modify(true);
+ end;
+ end;
+
+ ///
+ /// Set the context for this series of migration validation tests.
+ ///
+ /// The validator executing the validation tests.
+ /// The entity type being tested.
+ /// The entity Id context.
+ procedure SetContext(EntryValidatorCode: Code[20]; EntryEntityType: Text[50]; EntryContext: Text[250])
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ begin
+ ContextIsSet := false;
+
+ EntityType := EntryEntityType;
+ Context := EntryContext;
+
+ if ValidatorCode <> EntryValidatorCode then begin
+ ValidatorCode := EntryValidatorCode;
+
+ MigrationValidatorRegistry.Get(ValidatorCode);
+ ContextMigrationType := MigrationValidatorRegistry."Migration Type";
+ end;
+
+ if ContextMigrationType = '' then
+ exit;
+
+ if EntityType = '' then
+ exit;
+
+ if Context = '' then
+ exit;
+
+ ContextIsSet := true;
+ end;
+
+ ///
+ /// Validate that the actual value matches the expected value.
+ ///
+ /// The expected value for the test.
+ /// The actual value being tested.
+ /// Description of the test.
+ procedure ValidateAreEqual(TestCode: Code[30]; Expected: Variant; Actual: Variant; TestDescription: Text[250]): Boolean
+ begin
+ exit(ValidateAreEqual(TestCode, Expected, Actual, TestDescription, false));
+ end;
+
+ ///
+ /// Validate that the actual value matches the expected value.
+ ///
+ /// The expected value for the test.
+ /// The actual value being tested.
+ /// Description of the test.
+ /// Should the test be handled as a warning.
+ procedure ValidateAreEqual(TestCode: Code[30]; Expected: Variant; Actual: Variant; TestDescription: Text[250]; ShouldBeWarning: Boolean): Boolean
+ begin
+ exit(ValidateAreEqual(TestCode, Expected, Actual, TestDescription, ShouldBeWarning, false));
+ end;
+
+ ///
+ /// Validate that the actual value matches the expected value.
+ ///
+ /// The expected value for the test.
+ /// The actual value being tested.
+ /// Description of the test.
+ /// Should the test be handled as a warning.
+ /// Should the Expected and Actual values be redacted when logged as a validation error.
+ procedure ValidateAreEqual(TestCode: Code[30]; Expected: Variant; Actual: Variant; TestDescription: Text[250]; ShouldBeWarning: Boolean; ShouldRedact: Boolean): Boolean
+ begin
+ AssertContextIsSet();
+
+ if Equal(Expected, Actual) then
+ exit(true);
+
+ CreateValidationError(TestCode, Expected, Actual, TestDescription, ShouldBeWarning, ShouldRedact);
+ end;
+
+ ///
+ /// Validate that a record exists.
+ ///
+ /// Return value of the Get() call on the record being validated.
+ /// Description of the test.
+ procedure ValidateRecordExists(TestCode: Code[30]; GetReturnValue: Boolean; TestDescription: Text[250]): Boolean
+ begin
+ AssertContextIsSet();
+
+ if GetReturnValue then
+ exit(true);
+
+ CreateValidationError(TestCode, MissingExpectedLbl, MissingActualLbl, TestDescription, false, false);
+ end;
+
+ ///
+ /// Validate that a record exists.
+ ///
+ /// Return value of the Get() call on the record being validated.
+ /// Description of the test.
+ /// Should the test be handled as a warning.
+ procedure ValidateRecordExists(TestCode: Code[30]; GetReturnValue: Boolean; TestDescription: Text[250]; ShouldBeWarning: Boolean): Boolean
+ begin
+ AssertContextIsSet();
+
+ if GetReturnValue then
+ exit(true);
+
+ CreateValidationError(TestCode, MissingExpectedLbl, MissingActualLbl, TestDescription, ShouldBeWarning, false);
+ end;
+
+ local procedure CreateValidationError(TestCode: Code[30]; Expected: Variant; Actual: Variant; TestDescription: Text[250]; ShouldBeWarning: Boolean; ShouldRedact: Boolean)
+ var
+ MigrationValidationError: Record "Migration Validation Error";
+ MigrationValidationTest: record "Migration Validation Test";
+ OverrideIsWarning: Boolean;
+ OverrideShouldRedact: Boolean;
+ ActualIsWarning: Boolean;
+ ActualShouldRedact: Boolean;
+ begin
+ MigrationValidationTest.SetRange(Code, TestCode);
+ MigrationValidationTest.SetRange("Validator Code", ValidatorCode);
+ MigrationValidationTest.SetRange(Ignore, true);
+ if not MigrationValidationTest.IsEmpty() then
+ exit;
+
+ ActualIsWarning := ShouldBeWarning;
+ OverrideIsWarning := ShouldBeWarning;
+ ActualShouldRedact := ShouldRedact;
+ OverrideShouldRedact := ShouldRedact;
+
+ OnTestOverrideWarning(ValidatorCode, TestCode, Context, ShouldBeWarning, OverrideIsWarning);
+ ActualIsWarning := OverrideIsWarning;
+
+ OnTestOverrideShouldRedact(ValidatorCode, TestCode, Context, ShouldRedact, OverrideShouldRedact);
+ ActualShouldRedact := OverrideShouldRedact;
+
+ if ActualShouldRedact then begin
+ Expected := RedactedLbl;
+ Actual := RedactedLbl;
+ end;
+
+ MigrationValidationError."Entry No." := 0;
+ MigrationValidationError.Validate("Company Name", CompanyName());
+ MigrationValidationError.Validate("Test Code", TestCode);
+ MigrationValidationError.Validate("Validator Code", ValidatorCode);
+ MigrationValidationError.Validate("Migration Type", ContextMigrationType);
+ MigrationValidationError.Validate("Entity Type", EntityType);
+ MigrationValidationError.Validate(Context, Context);
+ MigrationValidationError.Validate("Test Description", TestDescription);
+ MigrationValidationError.Validate(Expected, Expected);
+ MigrationValidationError.Validate(Actual, Actual);
+ MigrationValidationError.Validate("Is Warning", ActualIsWarning);
+ MigrationValidationError.Insert(true);
+ end;
+
+ ///
+ /// Delete the past validation errors and other entries for the current Company.
+ ///
+ /// The Company that needs the migration validation errors deleted.
+ procedure DeleteMigrationValidationEntriesForCompany()
+ begin
+ DeleteMigrationValidationEntriesForCompany(CompanyName());
+ end;
+
+ ///
+ /// Delete the past validation errors and other entries for the specified Company.
+ ///
+ /// The Company that needs the migration validation errors deleted.
+ procedure DeleteMigrationValidationEntriesForCompany(Company: Text)
+ var
+ MigrationValidationError: Record "Migration Validation Error";
+ CompanyValidationProgress: Record "Company Validation Progress";
+ HybridCompanyStatus: Record "Hybrid Company Status";
+ begin
+ MigrationValidationError.SetRange("Company Name", Company);
+ if not MigrationValidationError.IsEmpty() then
+ MigrationValidationError.DeleteAll(true);
+
+ CompanyValidationProgress.SetRange("Company Name", Company);
+ if not CompanyValidationProgress.IsEmpty() then
+ CompanyValidationProgress.DeleteAll(true);
+
+ if not HybridCompanyStatus.Get(Company) then
+ exit;
+
+ HybridCompanyStatus.Validate(Validated, false);
+ HybridCompanyStatus.Modify(true);
+ end;
+
+ ///
+ /// Schedule validation for a Company.
+ ///
+ /// The Company context to schedule validation testing.
+ /// The index number for this scheduled validation. Used to calculate the start time for the scheduled job.
+ procedure ScheduleCompanyValidation(Company: Text; ScheduledEntryNumber: Integer)
+ var
+ TimeoutDuration: Duration;
+ MaxAttempts: Integer;
+ TimeBetweenScheduledJobs: Integer;
+ QueueCategory: Code[10];
+ IsHandled: Boolean;
+ OverrideTimeoutDuration: Duration;
+ OverrideMaxAttempts: Integer;
+ OverrideTimeBetweenScheduledJobs: Integer;
+ StartDateTime: DateTime;
+ FailoverToSession: Boolean;
+ SessionId: Integer;
+ begin
+ TimeoutDuration := GetDefaultJobTimeout();
+ MaxAttempts := GetDefaultJobMaxAttempts();
+ TimeBetweenScheduledJobs := GetDefaultTimeBetweenScheduledJobs();
+ OverrideTimeoutDuration := TimeoutDuration;
+ OverrideMaxAttempts := MaxAttempts;
+ OverrideTimeBetweenScheduledJobs := TimeBetweenScheduledJobs;
+ QueueCategory := GetJobQueueCategory();
+
+ OnBeforeCreateMigrationValidationJob(IsHandled, OverrideTimeoutDuration, OverrideMaxAttempts, OverrideTimeBetweenScheduledJobs);
+ if IsHandled then begin
+ TimeoutDuration := OverrideTimeoutDuration;
+ MaxAttempts := OverrideMaxAttempts;
+ TimeBetweenScheduledJobs := OverrideTimeBetweenScheduledJobs;
+ end;
+
+ FailoverToSession := not CanStartBackgroundJob();
+
+ if not FailoverToSession then begin
+ SendStartValidationResultMessage('', StrSubstNo(TelemetryValidationToBeScheduledMsg, JobQueueLbl), false, false);
+
+ StartDateTime := CurrentDateTime();
+ if ScheduledEntryNumber > 1 then
+ StartDateTime += (TimeBetweenScheduledJobs * (ScheduledEntryNumber - 1));
+
+ CreateAndScheduleBackgroundJob(Company,
+ Codeunit::"Migration Validation",
+ TimeoutDuration,
+ MaxAttempts,
+ QueueCategory,
+ MigrationValidationJobDescriptionTxt,
+ StartDateTime);
+
+ SendStartValidationResultMessage('', StrSubstNo(TelemetryValidationScheduledMsg, JobQueueLbl), false, true);
+ end;
+
+ if FailoverToSession then begin
+ SendStartValidationResultMessage('', StrSubstNo(TelemetryValidationToBeScheduledMsg, SessionLbl), false, false);
+
+ if Session.StartSession(SessionId, Codeunit::"Migration Validation", Company) then
+ SendStartValidationResultMessage('', StrSubstNo(TelemetryValidationScheduledMsg, SessionLbl), false, true)
+ else
+ SendStartValidationResultMessage('', TelemetryValidationFailedToStartSessionMsg, true, true);
+ end;
+ end;
+
+ local procedure Equal(Left: Variant; Right: Variant): Boolean
+ begin
+ if IsNumber(Left) and IsNumber(Right) then
+ exit(EqualNumbers(Left, Right));
+
+ if Left.IsDotNet or Right.IsDotNet then
+ exit((Format(Left, 0, 2) = Format(Right, 0, 2)));
+
+ exit((TypeOf(Left) = TypeOf(Right)) and (Format(Left, 0, 2) = Format(Right, 0, 2)))
+ end;
+
+ local procedure EqualNumbers(Left: Decimal; Right: Decimal): Boolean
+ begin
+ exit(Left = Right)
+ end;
+
+ local procedure IsNumber(Value: Variant): Boolean
+ begin
+ exit(Value.IsDecimal or Value.IsInteger or Value.IsChar)
+ end;
+
+ local procedure TypeOf(Value: Variant): Integer
+ var
+ "Field": Record "Field";
+ begin
+ case true of
+ Value.IsBoolean:
+ exit(Field.Type::Boolean);
+ Value.IsOption or Value.IsInteger or Value.IsByte:
+ exit(Field.Type::Integer);
+ Value.IsBigInteger:
+ exit(Field.Type::BigInteger);
+ Value.IsDecimal:
+ exit(Field.Type::Decimal);
+ Value.IsText or Value.IsCode or Value.IsChar or Value.IsTextConstant:
+ exit(Field.Type::Text);
+ Value.IsDate:
+ exit(Field.Type::Date);
+ Value.IsTime:
+ exit(Field.Type::Time);
+ Value.IsDuration:
+ exit(Field.Type::Duration);
+ Value.IsDateTime:
+ exit(Field.Type::DateTime);
+ Value.IsDateFormula:
+ exit(Field.Type::DateFormula);
+ Value.IsGuid:
+ exit(Field.Type::GUID);
+ Value.IsRecordId:
+ exit(Field.Type::RecordID);
+ else
+ Error(UnsupportedTypeErr, UnsupportedTypeName(Value))
+ end
+ end;
+
+ local procedure UnsupportedTypeName(Value: Variant): Text
+ begin
+ case true of
+ Value.IsRecord:
+ exit('Record');
+ Value.IsRecordRef:
+ exit('RecordRef');
+ Value.IsFieldRef:
+ exit('FieldRef');
+ Value.IsCodeunit:
+ exit('Codeunit');
+ Value.IsAutomation:
+ exit('Automation');
+ Value.IsFile:
+ exit('File');
+ end;
+ exit('Unsupported Type');
+ end;
+
+ local procedure AssertContextIsSet()
+ begin
+ if not ContextIsSet then
+ Error(SetContextErr);
+ end;
+
+ local procedure CanStartBackgroundJob(): Boolean
+ var
+ JobQueueEntry: Record "Job Queue Entry";
+ UserPermissions: Codeunit "User Permissions";
+ begin
+ if not UserPermissions.IsSuper(UserSecurityId()) then
+ exit(false);
+
+ if not TaskScheduler.CanCreateTask() then
+ exit(false);
+
+ if not JobQueueEntry.WritePermission then
+ exit(false);
+
+ exit(true);
+ end;
+
+ local procedure SendStartValidationResultMessage(TelemetryEventId: Text; MessageText: Text; IsError: Boolean; ShouldShowMessage: Boolean)
+ begin
+ if IsError then
+ Session.LogMessage(TelemetryEventId, MessageText, Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CloudMigrationTok)
+ else
+ Session.LogMessage(TelemetryEventId, MessageText, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CloudMigrationTok);
+
+ if ShouldShowMessage and GuiAllowed() then
+ Message(MessageText);
+ end;
+
+ local procedure CreateAndScheduleBackgroundJob(Company: Text; ObjectIdToRun: Integer; TimeoutDuration: Duration; MaxAttempts: Integer; CategoryCode: Code[10]; Description: Text[250]; StartDateTime: DateTime): Guid
+ var
+ JobQueueEntry: Record "Job Queue Entry";
+ JobQueueEntryBuffer: Record "Job Queue Entry Buffer";
+ begin
+ JobQueueEntry.ChangeCompany(Company);
+ JobQueueEntryBuffer.ChangeCompany(Company);
+
+ JobQueueEntry.Init();
+ JobQueueEntry."Object Type to Run" := JobQueueEntry."Object Type to Run"::Codeunit;
+ JobQueueEntry."Object ID to Run" := ObjectIdToRun;
+ JobQueueEntry."Maximum No. of Attempts to Run" := MaxAttempts;
+ JobQueueEntry."Job Queue Category Code" := CategoryCode;
+ JobQueueEntry.Description := Description;
+ JobQueueEntry."Job Timeout" := TimeoutDuration;
+ JobQueueEntry."Earliest Start Date/Time" := StartDateTime;
+ Codeunit.Run(Codeunit::"Job Queue - Enqueue", JobQueueEntry);
+
+ JobQueueEntryBuffer.Init();
+ JobQueueEntryBuffer.TransferFields(JobQueueEntry);
+ JobQueueEntryBuffer."Job Queue Entry ID" := JobQueueEntry.SystemId;
+ JobQueueEntryBuffer."Start Date/Time" := StartDateTime;
+ JobQueueEntryBuffer.Insert();
+
+ exit(JobQueueEntryBuffer.SystemId);
+ end;
+
+ local procedure GetDefaultJobMaxAttempts(): Integer
+ begin
+ exit(10);
+ end;
+
+ local procedure GetDefaultJobTimeout(): Duration
+ begin
+ exit(48 * 60 * 60 * 1000); // 48 hours
+ end;
+
+ internal procedure GetDefaultTimeBetweenScheduledJobs(): Integer
+ begin
+ exit(60 * 1000 * 2); // 2 minutes
+ end;
+
+ local procedure GetJobQueueCategory(): Code[10]
+ begin
+ exit(JobQueueCategoryTok);
+ end;
+
+ var
+ ValidatorCode: Code[20];
+ ContextMigrationType: Text[250];
+ EntityType: Text[50];
+ Context: Text[250];
+ MissingExpectedLbl: Label 'Expected to exist';
+ MissingActualLbl: Label 'Does not exist';
+ RedactedLbl: Label '';
+ ContextIsSet: Boolean;
+ SetContextErr: Label 'Context must be set before calling this procedure.';
+ UnsupportedTypeErr: Label 'Equality assertions only support Boolean, Option, Integer, BigInteger, Decimal, Code, Text, Date, DateFormula, Time, Duration, and DateTime values. Current value:%1.', Comment = '%1 = The unsupported variant type.';
+ CloudMigrationWarningErr: Label '%1 - %2', Comment = '%1 = Validator Code, %2 = Error message';
+ TelemetryValidationToBeScheduledMsg: Label 'Migration validation is about to be scheduled. Mode: %1', Comment = '%1 is the mode.', Locked = true;
+ TelemetryValidationScheduledMsg: Label 'Migration validation is now scheduled. Mode: %1', Comment = '%1 is the mode.', Locked = true;
+ TelemetryValidationFailedToStartSessionMsg: Label 'Migration validation could not start a new Session.', Locked = true;
+ MigrationValidationJobDescriptionTxt: Label 'Migration Validation';
+ JobQueueLbl: Label 'Job Queue', Locked = true;
+ SessionLbl: Label 'Session', Locked = true;
+ JobQueueCategoryTok: Label 'VALIDATION', Locked = true;
+ CloudMigrationTok: Label 'CloudMigration', Locked = true;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnTestOverrideWarning(Validator: Code[20]; Test: Code[30]; TestContext: Text[250]; EntryIsWarning: Boolean; var OverrideIsWarning: Boolean)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnTestOverrideShouldRedact(Validator: Code[20]; Test: Code[30]; TestContext: Text[250]; EntryShouldRedact: Boolean; var OverrideShouldRedact: Boolean)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnBeforeCreateMigrationValidationJob(var IsHandled: Boolean; var TimeoutDuration: Duration; var MaxAttempts: Integer; var TimeBetweenScheduledJobs: Integer)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnMigrationValidated()
+ begin
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidatorWarning.Codeunit.al b/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidatorWarning.Codeunit.al
new file mode 100644
index 0000000000..fc1b98b1d8
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/codeunits/MigrationValidatorWarning.Codeunit.al
@@ -0,0 +1,60 @@
+namespace Microsoft.DataMigration;
+
+codeunit 40031 "Migration Validator Warning" implements "Cloud Migration Warning"
+{
+ var
+ MigrationValidatorWarningMsg: Label 'Number of migration validators that had an error during the last migration.';
+
+ procedure CheckWarning(): Boolean
+ begin
+ exit(GetWarningCount() > 0);
+ end;
+
+ procedure FixWarning()
+ begin
+ // Not sure what to put here.
+ // The migration validators should not fail, and if one does it cannot be fixed by the one running the migration.
+ // The issue should be reported.
+ end;
+
+ procedure ShowWarning(var CloudMigrationWarning: Record "Cloud Migration Warning"): Text
+ var
+ SearchCloudMigrationWarning: Record "Cloud Migration Warning";
+ HybridReplicationSummary: Record "Hybrid Replication Summary";
+ FilterTxt: Text;
+ begin
+ SearchCloudMigrationWarning.SetRange("Warning Type", SearchCloudMigrationWarning."Warning Type"::"Migration Validator");
+ HybridReplicationSummary.SetCurrentKey("Start Time");
+ if HybridReplicationSummary.FindLast() then
+ SearchCloudMigrationWarning.SetFilter(SystemCreatedAt, '>%1', HybridReplicationSummary."End Time");
+
+ if not SearchCloudMigrationWarning.FindSet() then
+ exit;
+
+ repeat
+ FilterTxt := FilterTxt + Format(SearchCloudMigrationWarning."Entry No.") + '|'
+ until SearchCloudMigrationWarning.Next() = 0;
+ FilterTxt := FilterTxt.TrimEnd('|');
+
+ exit(FilterTxt);
+ end;
+
+ procedure GetWarningMessage(): Text[1024]
+ begin
+ exit(MigrationValidatorWarningMsg);
+ end;
+
+ procedure GetWarningCount(): Integer
+ var
+ CloudMigrationWarning: Record "Cloud Migration Warning";
+ HybridReplicationSummary: Record "Hybrid Replication Summary";
+ begin
+ CloudMigrationWarning.SetRange("Warning Type", CloudMigrationWarning."Warning Type"::"Migration Validator");
+ CloudMigrationWarning.SetRange(Ignored, false);
+ HybridReplicationSummary.SetCurrentKey("Start Time");
+ if HybridReplicationSummary.FindLast() then
+ CloudMigrationWarning.SetFilter(SystemCreatedAt, '>%1', HybridReplicationSummary."End Time");
+
+ exit(CloudMigrationWarning.Count());
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/enums/CloudMigrationWarningType.Enum.al b/Apps/W1/HybridBaseDeployment/app/src/enums/CloudMigrationWarningType.Enum.al
index 19f6651f19..777e494f5c 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/enums/CloudMigrationWarningType.Enum.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/enums/CloudMigrationWarningType.Enum.al
@@ -14,4 +14,9 @@ enum 40010 "Cloud Migration Warning Type" implements "Cloud Migration Warning"
Caption = 'Tenant Media';
Implementation = "Cloud Migration Warning" = "Tenant Media Warning";
}
+ value(3; "Migration Validator")
+ {
+ Caption = 'Migration Validator';
+ Implementation = "Cloud Migration Warning" = "Migration Validator Warning";
+ }
}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/CloudMigrationManagement.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/CloudMigrationManagement.Page.al
index 26e71a7ca2..0417667729 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/pages/CloudMigrationManagement.Page.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/CloudMigrationManagement.Page.al
@@ -187,6 +187,24 @@ page 40063 "Cloud Migration Management"
end;
}
}
+ group(MigrationValidationErrors)
+ {
+ ShowCaption = false;
+
+ field("Validation Errors"; ValidationErrors)
+ {
+ ApplicationArea = All;
+ Caption = 'Validation Errors';
+ Style = Unfavorable;
+ StyleExpr = (ValidationErrors > 0);
+ ToolTip = 'Indicates the total number of failed post migration validation tests, for all migrated companies.';
+
+ trigger OnDrillDown()
+ begin
+ Page.Run(Page::"Migration Validation Errors");
+ end;
+ }
+ }
}
}
}
@@ -733,6 +751,7 @@ page 40063 "Cloud Migration Management"
var
HybridReplicationSummary: Record "Hybrid Replication Summary";
HybridReplicationDetail: Record "Hybrid Replication Detail";
+ MigrationValidationError: Record "Migration Validation Error";
TempHybridReplicationDetail: Record "Hybrid Replication Detail" temporary;
HybridReplicationStatistics: Codeunit "Hybrid Replication Statistics";
HybridCloudManagement: Codeunit "Hybrid Cloud Management";
@@ -756,6 +775,8 @@ page 40063 "Cloud Migration Management"
HybridReplicationSummary.CalcFields("Companies Not Initialized");
NotInitializedCompaniesCount := HybridReplicationSummary."Companies Not Initialized";
end;
+
+ ValidationErrors := MigrationValidationError.Count();
end;
local procedure UpdateControlProperties()
@@ -987,4 +1008,5 @@ page 40063 "Cloud Migration Management"
CustomTablesEnabled: Boolean;
LastRefresh: DateTime;
RecordLinkBufferNotEmpty: Boolean;
+ ValidationErrors: Integer;
}
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/CompanyMigrationStatus.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/CompanyMigrationStatus.Page.al
new file mode 100644
index 0000000000..379e2f54f9
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/CompanyMigrationStatus.Page.al
@@ -0,0 +1,97 @@
+namespace Microsoft.DataMigration;
+
+page 40066 "Company Migration Status"
+{
+ ApplicationArea = All;
+ Caption = 'Company Migration Status';
+ PageType = List;
+ SourceTable = "Hybrid Company Status";
+ UsageCategory = None;
+ InsertAllowed = false;
+ Editable = false;
+ DeleteAllowed = false;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(General)
+ {
+ field(Name; Rec.Name)
+ {
+ ToolTip = 'Specifies the value of the Name field.';
+ }
+ field("Upgrade Status"; Rec."Upgrade Status")
+ {
+ ToolTip = 'Specifies the value of the Upgrade Status field.';
+ }
+ field(Validated; Rec.Validated)
+ {
+ ToolTip = 'Indicates if the company has been validated.';
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Promoted)
+ {
+ actionref(RunAllValidation_Promoted; RunAllValidation)
+ {
+ }
+ }
+
+ area(Processing)
+ {
+ action(RunAllValidation)
+ {
+ ApplicationArea = All;
+ Caption = 'Run All Validation';
+ ToolTip = 'Run validation on all migrated companies that have yet to be validated.';
+ Image = Process;
+
+ trigger OnAction()
+ var
+ IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ HybridCompanyStatus: Record "Hybrid Company Status";
+ MigrationValidation: Codeunit "Migration Validation";
+ ScheduledEntryNumber: Integer;
+ ForceRun: Boolean;
+ begin
+ if not IntelligentCloudSetup.Get() then
+ exit;
+
+ ForceRun := Dialog.Confirm(ShouldForceValidateQst, false);
+
+ HybridCompanyStatus.SetFilter(Name, '<>%1', '');
+
+ if not ForceRun then
+ HybridCompanyStatus.SetRange(Validated, false);
+
+ if not HybridCompanyStatus.FindSet() then begin
+ Message(NoCompaniesToValidateMsg);
+ exit;
+ end;
+
+ ScheduledEntryNumber := 1;
+ repeat
+ if ForceRun then
+ MigrationValidation.DeleteMigrationValidationEntriesForCompany(HybridCompanyStatus.Name);
+
+ MigrationValidation.ScheduleCompanyValidation(HybridCompanyStatus.Name, ScheduledEntryNumber);
+
+ ScheduledEntryNumber += 1;
+ until HybridCompanyStatus.Next() = 0;
+
+ Message(ValidationScheduledMsg);
+ end;
+ }
+ }
+ }
+
+ var
+ ShouldForceValidateQst: Label 'Do you want to force validation on all companies, even if it was previously validated?';
+ NoCompaniesToValidateMsg: Label 'No companies need to be validated.';
+ ValidationScheduledMsg: Label 'Validation is scheduled.';
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudManagement.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudManagement.Page.al
index e0b3544445..087b72c330 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudManagement.Page.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudManagement.Page.al
@@ -416,6 +416,22 @@ page 4003 "Intelligent Cloud Management"
HybridCloudManagement.ChangeRemovePermissionsFromUsers();
end;
}
+ action(ValidationStatus)
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Validation Status';
+ Image = Process;
+ ToolTip = 'View the Company migration status page to manually run validation.';
+ Visible = (NumberOfRegisteredValidators > 0);
+
+ trigger OnAction()
+ var
+ CompanyMigrationStatus: Page "Company Migration Status";
+ begin
+ CompanyMigrationStatus.RunModal();
+ CurrPage.Update(false);
+ end;
+ }
}
}
@@ -427,6 +443,7 @@ page 4003 "Intelligent Cloud Management"
trigger OnOpenPage()
var
IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
PermissionManager: Codeunit "Permission Manager";
UserPermissions: Codeunit "User Permissions";
EnvironmentInformation: Codeunit "Environment Information";
@@ -458,9 +475,13 @@ page 4003 "Intelligent Cloud Management"
CanShowUpdateReplicationCompanies(UpdateReplicationCompaniesEnabled);
CanMapCustomTables(CustomTablesEnabled);
- if IntelligentCloudSetup.Get() then
+ if IntelligentCloudSetup.Get() then begin
HybridDeployment.Initialize(IntelligentCloudSetup."Product ID");
+ MigrationValidatorRegistry.SetRange("Migration Type", IntelligentCloudSetup."Product ID");
+ NumberOfRegisteredValidators := MigrationValidatorRegistry.Count();
+ end;
+
IntelligentCloudNotifier.ShowICUpdateNotification();
WarnAboutNonInitializedCompanies();
@@ -663,4 +684,5 @@ page 4003 "Intelligent Cloud Management"
IntelligentCloudNotSetupMsg: Label 'Cloud migration was not set up. To migrate data to the cloud, complete the wizard.';
RunReplicationConfirmQst: Label 'Are you sure you want to trigger migration?';
DataRepairNotCompletedMsg: Label 'Data repair has not completed. Before you complete the cloud migration or trigger an upgrade, invoke the ''Repair Companion Table Records'' action';
+ NumberOfRegisteredValidators: Integer;
}
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudStatFactbox.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudStatFactbox.Page.al
index f9bc242922..47575b3089 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudStatFactbox.Page.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/IntelligentCloudStatFactbox.Page.al
@@ -180,6 +180,54 @@ page 4008 "Intelligent Cloud Stat Factbox"
Page.Run(Page::"Hybrid Companies List");
end;
}
+
+ field(Warnings; NumberOfWarnings)
+ {
+ ApplicationArea = All;
+ Editable = false;
+ Caption = 'Warnings';
+ ToolTip = 'Specifies the number of warnings for the selected migration.';
+ Style = Unfavorable;
+ StyleExpr = (NumberOfWarnings > 0);
+
+ trigger OnDrillDown()
+ begin
+ Page.Run(Page::"Cloud Migration Warnings");
+ end;
+ }
+
+ field("Validation Errors"; ValidationErrors)
+ {
+ ApplicationArea = All;
+ Caption = 'Validation Errors';
+ Style = Unfavorable;
+ StyleExpr = (ValidationErrors > 0);
+ ToolTip = 'Indicates the total number of failed post migration validation tests, for all migrated companies.';
+ Visible = (NumberOfRegisteredValidators > 0);
+
+ trigger OnDrillDown()
+ var
+ MigrationValidationRegistery: Record "Migration Validator Registry";
+ MigrationValidationTest: Record "Migration Validation Test";
+ MigrationValidationResults: Page "Migration Validation Results";
+ ValidatorFilter: Text;
+ SeparatorChar: Text;
+ begin
+ MigrationValidationRegistery.SetRange("Migration Type", MigrationType);
+ if MigrationValidationRegistery.FindSet() then
+ repeat
+ if ValidatorFilter <> '' then
+ SeparatorChar := '|';
+
+ ValidatorFilter := SeparatorChar + ValidatorFilter;
+ until MigrationValidationRegistery.Next() = 0;
+
+ MigrationValidationTest.SetFilter("Validator Code", ValidatorFilter);
+ MigrationValidationResults.SetTableView(MigrationValidationTest);
+ MigrationValidationResults.RunModal();
+ RefreshStats();
+ end;
+ }
}
field(Spacer3; '')
@@ -194,17 +242,26 @@ page 4008 "Intelligent Cloud Stat Factbox"
}
}
- trigger OnOpenPage()
+ trigger OnAfterGetRecord()
+ begin
+ RefreshStats();
+ end;
+
+ procedure RefreshStats()
var
IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ MigrationValidationError: Record "Migration Validation Error";
HybridCloudManagement: Codeunit "Hybrid Cloud Management";
begin
CanShowTablesNotMigrated(TablesNotMigratedEnabled);
if TablesNotMigratedEnabled then
TotalTablesNotMigrated := HybridCloudManagement.GetTotalTablesNotMigrated();
- if IntelligentCloudSetup.Get() then
+ if IntelligentCloudSetup.Get() then begin
NextScheduledRun := IntelligentCloudSetup.GetNextScheduledRunDateTime(CurrentDateTime());
+ MigrationType := IntelligentCloudSetup."Product ID";
+ end;
ShowNextScheduled := NextScheduledRun <> 0DT;
@@ -213,6 +270,14 @@ page 4008 "Intelligent Cloud Stat Factbox"
TotalTablesNotMigrated := HybridCloudManagement.GetTotalTablesNotMigrated();
SourceProduct := HybridCloudManagement.GetChosenProductName();
end;
+
+ UpdateWarningCounts();
+
+ MigrationValidatorRegistry.SetRange("Migration Type", MigrationType);
+ NumberOfRegisteredValidators := MigrationValidatorRegistry.Count();
+
+ MigrationValidationError.SetRange("Migration Type", MigrationType);
+ ValidationErrors := MigrationValidationError.Count();
end;
local procedure ShowTablesNotMigrated()
@@ -262,6 +327,21 @@ page 4008 "Intelligent Cloud Stat Factbox"
Page.Run(4019, TempIntelligentCloudNotMigrated);
end;
+ local procedure UpdateWarningCounts()
+ var
+ ICloudMigrationWarning: Interface "Cloud Migration Warning";
+ CloudMigrationWarningType: Enum "Cloud Migration Warning Type";
+ WarningImplementations: List of [Integer];
+ WarningImplementation: Integer;
+ begin
+ NumberOfWarnings := 0;
+ WarningImplementations := CloudMigrationWarningType.Ordinals();
+ foreach WarningImplementation in WarningImplementations do begin
+ ICloudMigrationWarning := "Cloud Migration Warning Type".FromInteger(WarningImplementation);
+ NumberOfWarnings += ICloudMigrationWarning.GetWarningCount();
+ end;
+ end;
+
[IntegrationEvent(false, false)]
local procedure CanShowTablesNotMigrated(var Enabled: Boolean)
begin
@@ -274,5 +354,9 @@ page 4008 "Intelligent Cloud Stat Factbox"
TotalTablesNotMigrated: Integer;
ShowNextScheduled: Boolean;
TablesNotMigratedEnabled: Boolean;
+ MigrationType: Text;
+ NumberOfWarnings: Integer;
+ NumberOfRegisteredValidators: Integer;
+ ValidationErrors: Integer;
}
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationErrors.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationErrors.Page.al
new file mode 100644
index 0000000000..a9af74ac4d
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationErrors.Page.al
@@ -0,0 +1,86 @@
+namespace Microsoft.DataMigration;
+
+page 40065 "Migration Validation Errors"
+{
+ ApplicationArea = All;
+ Caption = 'Migration Validation Errors';
+ PageType = List;
+ SourceTable = "Migration Validation Error";
+ UsageCategory = Lists;
+ Editable = false;
+ DeleteAllowed = false;
+ InsertAllowed = false;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(General)
+ {
+ field("Company Name"; Rec."Company Name")
+ {
+ ToolTip = 'Specifies the value of the Company Name field.';
+ }
+ field("Entity Type"; Rec."Entity Type")
+ {
+ ToolTip = 'Specifies the value of the Entity Type field.';
+ }
+ field(Context; Rec.Context)
+ {
+ ToolTip = 'Specifies the value of the Context field.';
+ }
+ field("Test Description"; Rec."Test Description")
+ {
+ ToolTip = 'Specifies the value of the Test Description field.';
+ }
+ field(Expected; Rec.Expected)
+ {
+ ToolTip = 'Specifies the value of the Expected field.';
+ }
+ field(Actual; Rec.Actual)
+ {
+ ToolTip = 'Specifies the value of the Actual field.';
+ }
+ field("Is Warning"; Rec."Is Warning")
+ {
+ ToolTip = 'Specifies if the failed validation test should be considered just a warning.';
+ }
+ field("Migration Type"; Rec."Migration Type")
+ {
+ ToolTip = 'Specifies the value of the Migration Type field.';
+ Visible = false;
+ }
+ field("Validator Code"; Rec."Validator Code")
+ {
+ ToolTip = 'Specifies the value of the Validator Code field.';
+ Visible = false;
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Promoted)
+ {
+ actionref(CompanyMigrationStatus_Promoted; CompanyMigrationStatus)
+ {
+ }
+ }
+ area(Navigation)
+ {
+ action(CompanyMigrationStatus)
+ {
+ ApplicationArea = All;
+ Caption = 'Company Migration Status';
+ Image = Navigate;
+ ToolTip = 'Open the Company Migration Status page.';
+
+ trigger OnAction()
+ begin
+ Page.Run(Page::"Company Migration Status");
+ end;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationResults.Page.al b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationResults.Page.al
new file mode 100644
index 0000000000..e29528dad9
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/pages/MigrationValidationResults.Page.al
@@ -0,0 +1,149 @@
+namespace Microsoft.DataMigration;
+
+page 40067 "Migration Validation Results"
+{
+ ApplicationArea = All;
+ Caption = 'Migration Validation Results';
+ PageType = Worksheet;
+ SourceTable = "Migration Validation Test";
+ UsageCategory = Lists;
+ DeleteAllowed = false;
+ InsertAllowed = false;
+
+ layout
+ {
+ area(Content)
+ {
+ group(Filters)
+ {
+ Caption = 'Filters';
+
+ field(CurrentCompany; CurrentCompanyFilter)
+ {
+ Caption = 'Current company only';
+ Visible = false;
+ ToolTip = 'Filter on the current company only or show tests results from all validated companies.';
+
+ trigger OnValidate()
+ begin
+ CurrPage.Update(false);
+ end;
+ }
+
+ field(HidePassingTests; HidePassingTestsFilter)
+ {
+ Caption = 'Hide passing tests';
+ ToolTip = 'Hide test records that do not have any issues found from any validated company.';
+
+ trigger OnValidate()
+ begin
+ ApplyFilterHidePassingTests();
+ CurrPage.Update(false);
+ end;
+ }
+ }
+
+ repeater(General)
+ {
+ field(FailCount; Rec."Fail Count")
+ {
+ Caption = 'Failed';
+ ToolTip = 'Specifies the number of failures within the filter criteria.';
+
+ trigger OnDrillDown()
+ var
+ MigrationValidationError: Record "Migration Validation Error";
+ MigrationValidationErrors: Page "Migration Validation Errors";
+ begin
+ MigrationValidationError.SetRange("Validator Code", Rec."Validator Code");
+ MigrationValidationError.SetRange("Test Code", Rec.Code);
+
+ MigrationValidationErrors.SetTableView(MigrationValidationError);
+ MigrationValidationErrors.Run();
+ end;
+ }
+ field(Entity; Rec.Entity)
+ {
+ Caption = 'Entity';
+ Editable = false;
+ ToolTip = 'Specifies the value of the Test Description field.';
+ }
+ field("Test Description"; Rec."Test Description")
+ {
+ Caption = 'Test Description';
+ Editable = false;
+ ToolTip = 'Specifies the value of the Test Description field.';
+ }
+ field("Code"; Rec."Code")
+ {
+ Caption = 'Code';
+ Editable = false;
+ ToolTip = 'Specifies the value of the Code field.';
+ }
+ field("Validator Code"; Rec."Validator Code")
+ {
+ Caption = 'Validator';
+ Editable = false;
+ ToolTip = 'Specifies the value of the Validator Code field.';
+ }
+ field(Ignore; Rec.Ignore)
+ {
+ ToolTip = 'Specifies the value of the Ignore field.';
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Promoted)
+ {
+ actionref(CompanyMigrationStatus_Promoted; CompanyMigrationStatus)
+ {
+ }
+ }
+
+ area(Navigation)
+ {
+ action(CompanyMigrationStatus)
+ {
+ ApplicationArea = All;
+ Caption = 'Company Migration Status';
+ Image = Process;
+ ToolTip = 'Open the Company Migration Status page.';
+
+ trigger OnAction()
+ var
+ CompanyMigrationStatus: Page "Company Migration Status";
+ begin
+ CompanyMigrationStatus.RunModal();
+ CurrPage.Update(false);
+ end;
+ }
+ }
+ }
+
+ trigger OnOpenPage()
+ begin
+ HidePassingTestsFilter := true;
+
+ ApplyFilterHidePassingTests();
+ Rec.CalcFields("Fail Count");
+ Rec.SetCurrentKey("Fail Count");
+ Rec.Ascending(false);
+
+ CurrPage.SetTableView(Rec);
+ end;
+
+ local procedure ApplyFilterHidePassingTests()
+ begin
+ if HidePassingTestsFilter then
+ Rec.SetFilter("Fail Count", '>0')
+ else
+ Rec.SetRange("Fail Count");
+ end;
+
+ var
+ CurrentCompanyFilter: Boolean;
+ HidePassingTestsFilter: Boolean;
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/CompanyValidationProgress.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/CompanyValidationProgress.Table.al
new file mode 100644
index 0000000000..ebe86c357e
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/CompanyValidationProgress.Table.al
@@ -0,0 +1,31 @@
+namespace Microsoft.DataMigration;
+
+table 40045 "Company Validation Progress"
+{
+ Caption = 'Company Validation Progress';
+ DataClassification = CustomerContent;
+ DataPerCompany = false;
+
+ fields
+ {
+ field(1; "Company Name"; Text[30])
+ {
+ Caption = 'Company Name';
+ }
+ field(2; "Validator Code"; Code[20])
+ {
+ Caption = 'Validator Code';
+ }
+ field(3; "Validation Step"; Code[20])
+ {
+ Caption = 'Validation Step';
+ }
+ }
+ keys
+ {
+ key(PK; "Company Name", "Validator Code", "Validation Step")
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/HybridCompanyStatus.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/HybridCompanyStatus.Table.al
index 1f726f0b1b..488e764813 100644
--- a/Apps/W1/HybridBaseDeployment/app/src/tables/HybridCompanyStatus.Table.al
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/HybridCompanyStatus.Table.al
@@ -60,6 +60,10 @@ table 40027 "Hybrid Company Status"
Description = 'Tenant Media Count';
DataClassification = SystemMetadata;
}
+ field(10; Validated; Boolean)
+ {
+ Description = 'Indicates if the company has been validated.';
+ }
}
keys
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationBuffer.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationBuffer.Table.al
new file mode 100644
index 0000000000..51a586f85f
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationBuffer.Table.al
@@ -0,0 +1,111 @@
+namespace Microsoft.DataMigration;
+
+table 40046 "Migration Validation Buffer"
+{
+ Caption = 'Migration Validation Buffer';
+ DataClassification = CustomerContent;
+ TableType = Temporary;
+
+ fields
+ {
+ field(1; "No."; Text[100])
+ {
+ Caption = 'No.';
+ NotBlank = true;
+ }
+ field(2; "Parent No."; Text[100])
+ {
+ Caption = 'Parent No.';
+ }
+ field(3; "Text 1"; Text[250])
+ {
+ Caption = 'Text 1';
+ }
+ field(4; "Text 2"; Text[250])
+ {
+ Caption = 'Text 2';
+ }
+ field(5; "Text 3"; Text[250])
+ {
+ Caption = 'Text 3';
+ }
+ field(6; "Text 4"; Text[250])
+ {
+ Caption = 'Text 4';
+ }
+ field(7; "Integer 1"; Integer)
+ {
+ Caption = 'Integer 1';
+ }
+ field(8; "Integer 2"; Integer)
+ {
+ Caption = 'Integer 2';
+ }
+ field(9; "Integer 3"; Integer)
+ {
+ Caption = 'Integer 3';
+ }
+ field(10; "Integer 4"; Integer)
+ {
+ Caption = 'Integer 4';
+ }
+ field(11; "Boolean 1"; Boolean)
+ {
+ Caption = 'Boolean 1';
+ }
+ field(12; "Boolean 2"; Boolean)
+ {
+ Caption = 'Boolean 2';
+ }
+ field(13; "Boolean 3"; Boolean)
+ {
+ Caption = 'Boolean 3';
+ }
+ field(14; "Boolean 4"; Boolean)
+ {
+ Caption = 'Boolean 4';
+ }
+ field(15; "Decimal 1"; Decimal)
+ {
+ Caption = 'Decimal 1';
+ }
+ field(16; "Decimal 2"; Decimal)
+ {
+ Caption = 'Decimal 2';
+ }
+ field(17; "Decimal 3"; Decimal)
+ {
+ Caption = 'Decimal 3';
+ }
+ field(18; "Decimal 4"; Decimal)
+ {
+ Caption = 'Decimal 4';
+ }
+ field(19; "Date 1"; Date)
+ {
+ Caption = 'Date 1';
+ }
+ field(20; "Date 2"; Date)
+ {
+ Caption = 'Date 2';
+ }
+ field(21; "Date 3"; Date)
+ {
+ Caption = 'Date 3';
+ }
+ field(22; "Date 4"; Date)
+ {
+ Caption = 'Date 4';
+ }
+ }
+ keys
+ {
+ key(PK; "No.")
+ {
+ Clustered = true;
+ }
+ key(Key2; "Parent No.")
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationError.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationError.Table.al
new file mode 100644
index 0000000000..0128eb84a5
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationError.Table.al
@@ -0,0 +1,70 @@
+namespace Microsoft.DataMigration;
+
+table 40043 "Migration Validation Error"
+{
+ Caption = 'Migration Validation Error';
+ DataClassification = CustomerContent;
+ DataPerCompany = false;
+
+ fields
+ {
+ field(1; "Entry No."; Integer)
+ {
+ Caption = 'Entry No.';
+ AutoIncrement = true;
+ }
+ field(2; "Company Name"; Text[30])
+ {
+ Caption = 'Company Name';
+ NotBlank = true;
+ }
+ field(3; "Test Code"; Code[30])
+ {
+ Caption = 'Test Code';
+ NotBlank = true;
+ TableRelation = "Migration Validation Test";
+ }
+ field(4; "Validator Code"; Code[20])
+ {
+ Caption = 'Validator Code';
+ NotBlank = true;
+ TableRelation = "Migration Validator Registry";
+ }
+ field(5; "Migration Type"; Text[250])
+ {
+ Caption = 'Migration Type';
+ }
+ field(6; "Entity Type"; Text[50])
+ {
+ Caption = 'Entity Type';
+ NotBlank = true;
+ }
+ field(7; Context; Text[250])
+ {
+ Caption = 'Context';
+ }
+ field(8; "Test Description"; Text[250])
+ {
+ Caption = 'Test Description';
+ }
+ field(9; Expected; Text[250])
+ {
+ Caption = 'Expected';
+ }
+ field(10; Actual; Text[250])
+ {
+ Caption = 'Actual';
+ }
+ field(11; "Is Warning"; Boolean)
+ {
+ Caption = 'Is Warning';
+ }
+ }
+ keys
+ {
+ key(PK; "Entry No.")
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationTest.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationTest.Table.al
new file mode 100644
index 0000000000..6e18704e47
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidationTest.Table.al
@@ -0,0 +1,48 @@
+namespace Microsoft.DataMigration;
+
+table 40044 "Migration Validation Test"
+{
+ Caption = 'Migration Validation Test';
+ DataClassification = SystemMetadata;
+ DataPerCompany = false;
+
+ fields
+ {
+ field(1; "Code"; Code[30])
+ {
+ Caption = 'Code';
+ }
+ field(2; "Validator Code"; Code[20])
+ {
+ Caption = 'Validator Code';
+ }
+ field(3; Entity; Text[50])
+ {
+ Caption = 'Test Description';
+ }
+ field(4; "Test Description"; Text[2048])
+ {
+ Caption = 'Test Description';
+ }
+ field(5; "Fail Count"; Integer)
+ {
+ Caption = 'Fail Count';
+ FieldClass = FlowField;
+ CalcFormula = count("Migration Validation Error" where("Validator Code" = field("Validator Code"), "Test Code" = field(Code)));
+ }
+ field(6; Ignore; Boolean)
+ {
+ Caption = 'Ignore';
+ }
+ }
+ keys
+ {
+ key(PK; "Code", "Validator Code")
+ {
+ Clustered = true;
+ }
+ key(Key2; Ignore)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidatorRegistry.Table.al b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidatorRegistry.Table.al
new file mode 100644
index 0000000000..937b881e0e
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/app/src/tables/MigrationValidatorRegistry.Table.al
@@ -0,0 +1,40 @@
+namespace Microsoft.DataMigration;
+
+table 40042 "Migration Validator Registry"
+{
+ Caption = 'Migration Validator Registry';
+ DataClassification = SystemMetadata;
+ DataPerCompany = false;
+
+ fields
+ {
+ field(1; "Validator Code"; Code[20])
+ {
+ Caption = 'Validator Code';
+ }
+ field(2; "Migration Type"; Text[250])
+ {
+ Caption = 'Migration Type';
+ }
+ field(3; "Codeunit Id"; Integer)
+ {
+ Caption = 'Codeunit Id';
+ }
+ field(5; Description; Text[2048])
+ {
+ Caption = 'Description';
+ }
+ field(6; Automatic; Boolean)
+ {
+ Caption = 'Automatic';
+ InitValue = true;
+ }
+ }
+ keys
+ {
+ key(PK; "Validator Code")
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/test/src/DummyMigrationValidator.Codeunit.al b/Apps/W1/HybridBaseDeployment/test/src/DummyMigrationValidator.Codeunit.al
new file mode 100644
index 0000000000..45d6b771a3
--- /dev/null
+++ b/Apps/W1/HybridBaseDeployment/test/src/DummyMigrationValidator.Codeunit.al
@@ -0,0 +1,96 @@
+codeunit 139501 "Dummy Migration Validator"
+{
+ trigger OnRun()
+ begin
+ RunCustomerMigrationValidation();
+
+ MigrationValidation.ReportCompanyValidated();
+ end;
+
+ local procedure RunCustomerMigrationValidation()
+ var
+ Customer: Record Customer;
+ begin
+ // [Customer: Test 1]
+
+ // Set the context of this set of tests
+ MigrationValidation.SetContext(GetValidatorCode(), 'Customer', 'TEST-1');
+
+ // Check for the entity record by the key
+ if not MigrationValidation.ValidateRecordExists(Test_CUSTOMEREXISTS_Tok, Customer.Get('TEST-1'), 'Missing TEST-1') then
+ exit;
+
+ // This is a test that is not a warning, and would fail the migration
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME_Tok, 'Test 1', Customer.Name, 'Name');
+
+ // This is a test that would be just a warning
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME2_Tok, 'Test name 2', Customer."Name 2", 'Name 2', true);
+
+ // [Customer: Test 2]
+
+ // Set the context of this set of tests
+ MigrationValidation.SetContext(GetValidatorCode(), 'Customer', 'TEST-2');
+
+ // Check for the entity record by the key
+ if not MigrationValidation.ValidateRecordExists(Test_CUSTOMEREXISTS_Tok, Customer.Get('TEST-2'), 'Missing TEST-2') then
+ exit;
+
+ // This is a test that is not a warning, and would fail the migration
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME_Tok, 'Test 2', Customer.Name, 'Name');
+
+ // This is a test that would be just a warning
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME2_Tok, 'Test name 2', Customer."Name 2", 'Name 2', true);
+ end;
+
+ internal procedure GetValidatorCode(): Code[20]
+ begin
+ exit('TEST');
+ end;
+
+ // Normally initialized by this event, but called directly for testing.
+ //[EventSubscriber(ObjectType::Codeunit, Codeunit::"Hybrid Cloud Management", OnPrepareMigrationValidation, '', false, false)]
+ internal procedure OnPrepareMigrationValidation(ProductID: Text[250])
+ begin
+ RegisterValidator(ProductID);
+
+ AddTest(Test_CUSTOMEREXISTS_Tok, 'Customer', 'Missing Customer');
+ AddTest(Test_CUSTOMERNAME_Tok, 'Customer', 'Name');
+ AddTest(Test_CUSTOMERNAME2_Tok, 'Customer', 'Name 2');
+ end;
+
+ local procedure RegisterValidator(ProductID: Text[250])
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ ValidatorCodeunitId: Integer;
+ begin
+ ValidatorCodeunitId := Codeunit::"Dummy Migration Validator";
+ if not MigrationValidatorRegistry.Get(GetValidatorCode()) then begin
+ MigrationValidatorRegistry.Validate("Validator Code", GetValidatorCode());
+ MigrationValidatorRegistry.Validate("Migration Type", ProductID);
+ MigrationValidatorRegistry.Validate(Description, ValidatorDescriptionLbl);
+ MigrationValidatorRegistry.Validate("Codeunit Id", ValidatorCodeunitId);
+ MigrationValidatorRegistry.Validate(Automatic, true);
+ MigrationValidatorRegistry.Insert(true);
+ end;
+ end;
+
+ local procedure AddTest(Code: Code[30]; Entity: Text[50]; Description: Text)
+ var
+ MigrationValidationTest: Record "Migration Validation Test";
+ begin
+ if not MigrationValidationTest.Get(Code, GetValidatorCode()) then begin
+ MigrationValidationTest.Validate(Code, Code);
+ MigrationValidationTest.Validate("Validator Code", GetValidatorCode());
+ MigrationValidationTest.Validate(Entity, Entity);
+ MigrationValidationTest.Validate("Test Description", Description);
+ MigrationValidationTest.Insert(true);
+ end;
+ end;
+
+ var
+ MigrationValidation: Codeunit "Migration Validation";
+ ValidatorDescriptionLbl: Label 'Dummy migration validator', MaxLength = 250;
+ Test_CUSTOMEREXISTS_Tok: Label 'CUSTOMEREXISTS', Locked = true;
+ Test_CUSTOMERNAME_Tok: Label 'CUSTOMERNAME', Locked = true;
+ Test_CUSTOMERNAME2_Tok: Label 'CUSTOMERNAME2', Locked = true;
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridBaseDeployment/test/src/HybridCloudManagementTests.Codeunit.al b/Apps/W1/HybridBaseDeployment/test/src/HybridCloudManagementTests.Codeunit.al
index 8d94c25712..6dc9b9bfc9 100644
--- a/Apps/W1/HybridBaseDeployment/test/src/HybridCloudManagementTests.Codeunit.al
+++ b/Apps/W1/HybridBaseDeployment/test/src/HybridCloudManagementTests.Codeunit.al
@@ -706,6 +706,195 @@ codeunit 139656 "Hybrid Cloud Management Tests"
Assert.IsTrue(HybridCompanyStatus."Record Link Move Completed", 'Record links migration status not updated');
end;
+ [Test]
+ procedure TestMigrationValidation()
+ var
+ MigrationValidationError: Record "Migration Validation Error";
+ Customer: Record Customer;
+ HybridCompanyStatus: Record "Hybrid Company Status";
+ MigrationValidation: Codeunit "Migration Validation";
+ HybridCloudManagement: Codeunit "Hybrid Cloud Management";
+ DataCreationFailed: Boolean;
+ begin
+ // [GIVEN] A company migration is being validated
+ InitMigrationValidationTests();
+
+ DataCreationFailed := false;
+
+ // [WHEN] No customers were migrated, but were expected
+ HybridCloudManagement.StartMigrationValidationImp(DataCreationFailed);
+
+ // [THEN] The migration will fail, and there will be corresponding validation error entries
+ HybridCompanyStatus.Get(CompanyName());
+ Assert.IsTrue(HybridCompanyStatus.Validated, 'The company should have been validated.');
+ Assert.RecordCount(MigrationValidationError, 1);
+ MigrationValidationError.FindFirst();
+ Assert.AreEqual('Missing TEST-1', MigrationValidationError."Test Description", 'Incorrect test description');
+ Assert.AreEqual(false, MigrationValidationError."Is Warning", 'Incorrect value for Is Warning');
+ Assert.IsTrue(DataCreationFailed, 'The migration should be in a failed state.');
+
+ // Reset
+ DataCreationFailed := false;
+ MigrationValidation.DeleteMigrationValidationEntriesForCompany();
+
+ // [WHEN] Some of the customers were created
+ // Create Customer TEST-1
+ InitMigrationValidationTest_CustomerTest1();
+ HybridCloudManagement.StartMigrationValidationImp(DataCreationFailed);
+
+ // [THEN] The migration will fail, and there will be corresponding validation error entries
+ Assert.IsTrue(DataCreationFailed, 'The migration should be in a failed state.');
+ Assert.RecordCount(MigrationValidationError, 1);
+ MigrationValidationError.FindFirst();
+ Assert.AreEqual('Missing TEST-2', MigrationValidationError."Test Description", 'Incorrect test description');
+ Assert.AreEqual(false, MigrationValidationError."Is Warning", 'Incorrect value for Is Warning');
+
+ // Reset
+ DataCreationFailed := false;
+ MigrationValidation.DeleteMigrationValidationEntriesForCompany();
+
+ // [WHEN] All the customers were created and correct
+ InitMigrationValidationTest_CustomerTest1();
+ InitMigrationValidationTest_CustomerTest2();
+ HybridCloudManagement.StartMigrationValidationImp(DataCreationFailed);
+
+ // [THEN] The migration will be successful, and there won't be any validation error entries
+ Assert.IsFalse(DataCreationFailed, 'The migration should be in a failed state.');
+ Assert.RecordCount(MigrationValidationError, 0);
+
+ // Reset
+ DataCreationFailed := false;
+ MigrationValidation.DeleteMigrationValidationEntriesForCompany();
+
+ // [WHEN] Some values are unexpected
+ Customer.GET('TEST-1');
+ Customer.Name := 'Wrong name';
+ Customer."Name 2" := 'Wrong name 2';
+ Customer.Modify();
+
+ HybridCloudManagement.StartMigrationValidationImp(DataCreationFailed);
+
+ // [TEST] The correct validation error records will be added
+ // The migration will be in a failed state because there is an entry that isn't a warning
+ Assert.RecordCount(MigrationValidationError, 2);
+ Assert.IsTrue(DataCreationFailed, 'The migration should be in a failed state.');
+
+ MigrationValidationError.FindSet();
+ Assert.AreEqual('Name', MigrationValidationError."Test Description", 'Incorrect test description');
+ Assert.AreEqual('Test 1', MigrationValidationError.Expected, 'Incorrect Expected value');
+ Assert.AreEqual('Wrong name', MigrationValidationError.Actual, 'Incorrect Actual value');
+ Assert.AreEqual(false, MigrationValidationError."Is Warning", 'Incorrect value for Is Warning');
+
+ MigrationValidationError.Next();
+ Assert.AreEqual('Name 2', MigrationValidationError."Test Description", 'Incorrect test description');
+ Assert.AreEqual('Test name 2', MigrationValidationError.Expected, 'Incorrect Expected value');
+ Assert.AreEqual('Wrong name 2', MigrationValidationError.Actual, 'Incorrect Actual value');
+ Assert.AreEqual(true, MigrationValidationError."Is Warning", 'Incorrect value for Is Warning');
+
+ // Reset
+ DataCreationFailed := false;
+ MigrationValidation.DeleteMigrationValidationEntriesForCompany();
+
+ // [WHEN] Some values are unexpected, but nothing considered major
+ Customer.GET('TEST-1');
+ Customer.Name := 'Test 1'; // Back to expected value
+ Customer."Name 2" := 'Wrong name 2';
+ Customer.Modify();
+
+ HybridCloudManagement.StartMigrationValidationImp(DataCreationFailed);
+ Assert.RecordCount(MigrationValidationError, 1);
+ Assert.IsFalse(DataCreationFailed, 'The migration should NOT be in a failed state.');
+ end;
+
+ local procedure InitMigrationValidationTests()
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ MigrationValidationError: Record "Migration Validation Error";
+ DataMigrationStatus: Record "Data Migration Status";
+ HybridCompany: Record "Hybrid Company";
+ HybridCompanyStatus: Record "Hybrid Company Status";
+ IntelligentCloudSetup: Record "Intelligent Cloud Setup";
+ DummyMigrationValidator: Codeunit "Dummy Migration Validator";
+ ValidatorCode: Code[20];
+ MigrationType: Text[250];
+ ValidatorCodeunitId: Integer;
+ begin
+ ValidatorCode := DummyMigrationValidator.GetValidatorCode();
+ ValidatorCodeunitId := Codeunit::"Dummy Migration Validator";
+
+ if not IntelligentCloudSetup.Get() then begin
+ IntelligentCloudSetup."Product ID" := GetDefaultTestMigrationType();
+ IntelligentCloudSetup.Insert();
+ end;
+
+ MigrationType := IntelligentCloudSetup."Product ID";
+
+ if not DataMigrationStatus.IsEmpty() then
+ DataMigrationStatus.DeleteAll();
+
+ if not MigrationValidationError.IsEmpty() then
+ MigrationValidationError.DeleteAll();
+
+ if not MigrationValidatorRegistry.IsEmpty() then
+ MigrationValidatorRegistry.DeleteAll();
+
+ if not MigrationValidatorRegistry.Get(ValidatorCode) then begin
+ MigrationValidatorRegistry.Validate("Validator Code", ValidatorCode);
+ MigrationValidatorRegistry.Validate("Migration Type", MigrationType);
+ MigrationValidatorRegistry.Validate("Codeunit Id", ValidatorCodeunitId);
+ MigrationValidatorRegistry.Insert();
+ end;
+
+ if not HybridCompany.Get(CompanyName()) then begin
+ HybridCompany.Name := CopyStr(CompanyName(), 1, MaxStrLen(HybridCompany.Name));
+ HybridCompany.Insert();
+ end;
+
+ if not HybridCompanyStatus.Get(CompanyName()) then begin
+ HybridCompanyStatus.Name := CopyStr(CompanyName(), 1, MaxStrLen(HybridCompanyStatus.Name));
+ HybridCompanyStatus.Insert();
+ end;
+
+ HybridCompanyStatus.Validated := false;
+ HybridCompany.Modify();
+
+ Clear(DataMigrationStatus);
+ DataMigrationStatus."Migration Type" := IntelligentCloudSetup."Product ID";
+ DataMigrationStatus.Status := DataMigrationStatus.Status::"In Progress";
+ DataMigrationStatus.Insert(true);
+
+ DummyMigrationValidator.OnPrepareMigrationValidation(MigrationType);
+ end;
+
+ local procedure InitMigrationValidationTest_CustomerTest1()
+ var
+ Customer: Record Customer;
+ begin
+ if not Customer.Get('TEST-1') then begin
+ Customer."No." := 'TEST-1';
+ Customer.Name := 'Test 1';
+ Customer."Name 2" := 'Test name 2';
+ Customer.Insert();
+ end;
+ end;
+
+ local procedure InitMigrationValidationTest_CustomerTest2()
+ var
+ Customer: Record Customer;
+ begin
+ if not Customer.Get('TEST-2') then begin
+ Customer."No." := 'TEST-2';
+ Customer.Name := 'Test 2';
+ Customer."Name 2" := 'Test name 2';
+ Customer.Insert();
+ end;
+ end;
+
+ local procedure GetDefaultTestMigrationType(): Code[20]
+ begin
+ exit('TEST');
+ end;
+
local procedure OpenCloudMigSelectTablesPage(var CloudMigSelectTables: TestPage "Cloud Mig - Select Tables")
var
IntelligentCloudStatus: Record "Intelligent Cloud Status";
diff --git a/Apps/W1/HybridGP/app/Permissions/HybridGPObjects.PermissionSet.al b/Apps/W1/HybridGP/app/Permissions/HybridGPObjects.PermissionSet.al
index af444572db..05efe5e7e8 100644
--- a/Apps/W1/HybridGP/app/Permissions/HybridGPObjects.PermissionSet.al
+++ b/Apps/W1/HybridGP/app/Permissions/HybridGPObjects.PermissionSet.al
@@ -157,5 +157,6 @@ permissionset 4029 "HybridGP - Objects"
page "GP Payment Terms" = X,
table "GP IV00104" = X,
table "GP PM00101" = X,
- table "GP PM00203" = X;
+ table "GP PM00203" = X,
+ codeunit "GP Migration Validator" = X;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridGP/app/src/Migration/GPTables/GPPopulateCombinedTables.Codeunit.al b/Apps/W1/HybridGP/app/src/Migration/GPTables/GPPopulateCombinedTables.Codeunit.al
index be96885d3c..805b3e3a10 100644
--- a/Apps/W1/HybridGP/app/src/Migration/GPTables/GPPopulateCombinedTables.Codeunit.al
+++ b/Apps/W1/HybridGP/app/src/Migration/GPTables/GPPopulateCombinedTables.Codeunit.al
@@ -329,8 +329,6 @@ codeunit 40125 "GP Populate Combined Tables"
var
GPCustomer: Record "GP Customer";
GPRM00101: Record "GP RM00101";
- GPRM00103: Record "GP RM00103";
- GPSY01200: Record "GP SY01200";
GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
begin
GPRM00101.SetRange(INACTIVE, false);
@@ -342,53 +340,58 @@ codeunit 40125 "GP Populate Combined Tables"
repeat
Clear(GPCustomer);
+ SetGPCustomerFields(GPCustomer, GPRM00101);
+ GPCustomer.Insert();
+ until GPRM00101.Next() = 0;
+ end;
+
+ internal procedure SetGPCustomerFields(var GPCustomer: Record "GP Customer"; var GPRM00101: Record "GP RM00101")
+ var
+ GPRM00103: Record "GP RM00103";
+ GPSY01200: Record "GP SY01200";
+ begin
#pragma warning disable AA0139
- GPCustomer.CUSTNMBR := GPRM00101.CUSTNMBR.Trim();
- GPCustomer.CUSTNAME := GPRM00101.CUSTNAME.Trim();
- GPCustomer.STMTNAME := GPRM00101.STMTNAME.Trim();
- GPCustomer.ADDRESS1 := GPRM00101.ADDRESS1.Trim();
- GPCustomer.ADDRESS2 := GPRM00101.ADDRESS2.Trim();
- GPCustomer.CITY := GPRM00101.CITY.Trim();
- GPCustomer.CNTCPRSN := GPRM00101.CNTCPRSN.Trim();
- GPCustomer.SALSTERR := GPRM00101.SALSTERR.Trim();
- GPCustomer.CRLMTAMT := GPRM00101.CRLMTAMT;
- GPCustomer.PYMTRMID := GPRM00101.PYMTRMID.Trim();
- GPCustomer.SLPRSNID := GPRM00101.SLPRSNID.Trim();
- GPCustomer.SHIPMTHD := GPRM00101.SHIPMTHD.Trim();
- GPCustomer.COUNTRY := GPRM00101.COUNTRY.Trim();
- GPCustomer.STMTCYCL := GPRM00101.STMTCYCL <> 0;
- GPCustomer.ZIPCODE := GPRM00101.ZIP.Trim();
- GPCustomer.STATE := GPRM00101.STATE.Trim();
- GPCustomer.TAXSCHID := GPRM00101.TAXSCHID.Trim();
- GPCustomer.UPSZONE := GPRM00101.UPSZONE.Trim();
- GPCustomer.TAXEXMT1 := GPRM00101.TAXEXMT1.Trim();
- GPCustomer.CUSTCLAS := GPRM00101.CUSTCLAS.Trim();
- GPCustomer.RMSLSACC := GPRM00101.RMSLSACC;
+ GPCustomer.CUSTNMBR := GPRM00101.CUSTNMBR.Trim();
+ GPCustomer.CUSTNAME := GPRM00101.CUSTNAME.Trim();
+ GPCustomer.STMTNAME := GPRM00101.STMTNAME.Trim();
+ GPCustomer.ADDRESS1 := GPRM00101.ADDRESS1.Trim();
+ GPCustomer.ADDRESS2 := GPRM00101.ADDRESS2.Trim();
+ GPCustomer.CITY := GPRM00101.CITY.Trim();
+ GPCustomer.CNTCPRSN := GPRM00101.CNTCPRSN.Trim();
+ GPCustomer.SALSTERR := GPRM00101.SALSTERR.Trim();
+ GPCustomer.CRLMTAMT := GPRM00101.CRLMTAMT;
+ GPCustomer.PYMTRMID := GPRM00101.PYMTRMID.Trim();
+ GPCustomer.SLPRSNID := GPRM00101.SLPRSNID.Trim();
+ GPCustomer.SHIPMTHD := GPRM00101.SHIPMTHD.Trim();
+ GPCustomer.COUNTRY := GPRM00101.COUNTRY.Trim();
+ GPCustomer.STMTCYCL := GPRM00101.STMTCYCL <> 0;
+ GPCustomer.ZIPCODE := GPRM00101.ZIP.Trim();
+ GPCustomer.STATE := GPRM00101.STATE.Trim();
+ GPCustomer.TAXSCHID := GPRM00101.TAXSCHID.Trim();
+ GPCustomer.UPSZONE := GPRM00101.UPSZONE.Trim();
+ GPCustomer.TAXEXMT1 := GPRM00101.TAXEXMT1.Trim();
#pragma warning restore AA0139
- if GPRM00101.PHONE1.Contains('E+') then
- GPCustomer.PHONE1 := '00000000000000'
- else
- GPCustomer.PHONE1 := CopyStr(GPRM00101.PHONE1.Trim(), 1, MaxStrLen(GPCustomer.PHONE1));
+ if GPRM00101.PHONE1.Contains('E+') then
+ GPCustomer.PHONE1 := '00000000000000'
+ else
+ GPCustomer.PHONE1 := CopyStr(GPRM00101.PHONE1.Trim(), 1, MaxStrLen(GPCustomer.PHONE1));
- if GPRM00101.FAX.Contains('E+') then
- GPCustomer.FAX := '00000000000000'
- else
- GPCustomer.FAX := CopyStr(GPRM00101.FAX.Trim(), 1, MaxStrLen(GPCustomer.FAX));
+ if GPRM00101.FAX.Contains('E+') then
+ GPCustomer.FAX := '00000000000000'
+ else
+ GPCustomer.FAX := CopyStr(GPRM00101.FAX.Trim(), 1, MaxStrLen(GPCustomer.FAX));
- if GPRM00103.Get(GPRM00101.CUSTNMBR) then
- GPCustomer.AMOUNT := GPRM00103.CUSTBLNC;
+ if GPRM00103.Get(GPRM00101.CUSTNMBR) then
+ GPCustomer.AMOUNT := GPRM00103.CUSTBLNC;
- if GPSY01200.Get('CUS', GPRM00101.CUSTNMBR, GPRM00101.ADRSCODE) then begin
- if ((GPSY01200.INET1 <> '') and (GPSY01200.INET1.Contains('@'))) then
- GPCustomer.INET1 := CopyStr(GPSY01200.INET1.Trim(), 1, MaxStrLen(GPCustomer.INET1));
+ if GPSY01200.Get('CUS', GPRM00101.CUSTNMBR, GPRM00101.ADRSCODE) then begin
+ if ((GPSY01200.INET1 <> '') and (GPSY01200.INET1.Contains('@'))) then
+ GPCustomer.INET1 := CopyStr(GPSY01200.INET1.Trim(), 1, MaxStrLen(GPCustomer.INET1));
- if ((GPSY01200.INET2 <> '') and (GPSY01200.INET2.Contains('@'))) then
- GPCustomer.INET2 := CopyStr(GPSY01200.INET2.Trim(), 1, MaxStrLen(GPCustomer.INET2));
- end;
-
- GPCustomer.Insert();
- until GPRM00101.Next() = 0;
+ if ((GPSY01200.INET2 <> '') and (GPSY01200.INET2.Contains('@'))) then
+ GPCustomer.INET2 := CopyStr(GPSY01200.INET2.Trim(), 1, MaxStrLen(GPCustomer.INET2));
+ end;
end;
internal procedure PopulateGPCustomerTransactions()
@@ -527,66 +530,69 @@ codeunit 40125 "GP Populate Combined Tables"
internal procedure PopulateGPVendors()
var
GPPM00200Vendor: Record "GP PM00200";
- GPPM00201VendorSum: Record "GP PM00201";
- GPSY01200NetAddresses: Record "GP SY01200";
GPVendor: Record "GP Vendor";
- GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
+ GPCompanyMigrationSettings: Record "GP Company Migration Settings";
begin
GPPM00200Vendor.SetFilter(VENDSTTS, '1|3');
- if GPCompanyAdditionalSettings.GetMigrateInactiveVendors() then
- GPPM00200Vendor.SetRange(VENDSTTS);
+ if GPCompanyMigrationSettings.Get(CompanyName()) then
+ if GPCompanyMigrationSettings."Migrate Inactive Vendors" then
+ GPPM00200Vendor.SetRange(VENDSTTS);
if not GPPM00200Vendor.FindSet() then
exit;
repeat
Clear(GPVendor);
+ SetGPVendorFields(GPVendor, GPPM00200Vendor);
+ GPVendor.Insert();
+ until GPPM00200Vendor.Next() = 0;
+ end;
+
+ internal procedure SetGPVendorFields(var GPVendor: Record "GP Vendor"; GPPM00200Vendor: Record "GP PM00200")
+ var
+ GPPM00201VendorSum: Record "GP PM00201";
+ GPSY01200NetAddresses: Record "GP SY01200";
+ begin
#pragma warning disable AA0139
- GPVendor.VENDORID := GPPM00200Vendor.VENDORID.TrimEnd();
- GPVendor.VENDNAME := GPPM00200Vendor.VENDNAME.TrimEnd();
- GPVendor.SEARCHNAME := GPPM00200Vendor.VENDNAME.TrimEnd();
- GPVendor.VNDCHKNM := GPPM00200Vendor.VNDCHKNM.TrimEnd();
- GPVendor.ADDRESS1 := GPPM00200Vendor.ADDRESS1.TrimEnd();
- GPVendor.ADDRESS2 := GPPM00200Vendor.ADDRESS2.TrimEnd();
- GPVendor.CITY := GPPM00200Vendor.CITY.TrimEnd();
- GPVendor.VNDCNTCT := GPPM00200Vendor.VNDCNTCT.TrimEnd();
- GPVendor.PYMTRMID := GPPM00200Vendor.PYMTRMID.TrimEnd();
- GPVendor.SHIPMTHD := GPPM00200Vendor.SHIPMTHD.TrimEnd();
- GPVendor.COUNTRY := GPPM00200Vendor.COUNTRY.TrimEnd();
- GPVendor.PYMNTPRI := GPPM00200Vendor.PYMNTPRI.TrimEnd();
- GPVendor.ZIPCODE := GPPM00200Vendor.ZIPCODE.TrimEnd();
- GPVendor.STATE := GPPM00200Vendor.STATE.TrimEnd();
- GPVendor.TAXSCHID := GPPM00200Vendor.TAXSCHID.TrimEnd();
- GPVendor.UPSZONE := GPPM00200Vendor.UPSZONE.TrimEnd();
- GPVendor.TXIDNMBR := GPPM00200Vendor.TXIDNMBR.TrimEnd();
- GPVendor.VNDCLSID := GPPM00200Vendor.VNDCLSID.TrimEnd();
+ GPVendor.VENDORID := GPPM00200Vendor.VENDORID.TrimEnd();
+ GPVendor.VENDNAME := GPPM00200Vendor.VENDNAME.TrimEnd();
+ GPVendor.SEARCHNAME := GPPM00200Vendor.VENDNAME.TrimEnd();
+ GPVendor.VNDCHKNM := GPPM00200Vendor.VNDCHKNM.TrimEnd();
+ GPVendor.ADDRESS1 := GPPM00200Vendor.ADDRESS1.TrimEnd();
+ GPVendor.ADDRESS2 := GPPM00200Vendor.ADDRESS2.TrimEnd();
+ GPVendor.CITY := GPPM00200Vendor.CITY.TrimEnd();
+ GPVendor.VNDCNTCT := GPPM00200Vendor.VNDCNTCT.TrimEnd();
+ GPVendor.PYMTRMID := GPPM00200Vendor.PYMTRMID.TrimEnd();
+ GPVendor.SHIPMTHD := GPPM00200Vendor.SHIPMTHD.TrimEnd();
+ GPVendor.COUNTRY := GPPM00200Vendor.COUNTRY.TrimEnd();
+ GPVendor.PYMNTPRI := GPPM00200Vendor.PYMNTPRI.TrimEnd();
+ GPVendor.ZIPCODE := GPPM00200Vendor.ZIPCODE.TrimEnd();
+ GPVendor.STATE := GPPM00200Vendor.STATE.TrimEnd();
+ GPVendor.TAXSCHID := GPPM00200Vendor.TAXSCHID.TrimEnd();
+ GPVendor.UPSZONE := GPPM00200Vendor.UPSZONE.TrimEnd();
+ GPVendor.TXIDNMBR := GPPM00200Vendor.TXIDNMBR.TrimEnd();
#pragma warning restore AA0139
- GPVendor.PMPRCHIX := GPPM00200Vendor.PMPRCHIX;
-
- if GPPM00200Vendor.PHNUMBR1.Contains('E+') then
- GPVendor.PHNUMBR1 := '00000000000000'
- else
- GPVendor.PHNUMBR1 := CopyStr(GPPM00200Vendor.PHNUMBR1.Trim(), 1, MaxStrLen(GPVendor.PHNUMBR1));
-
- if GPPM00200Vendor.FAXNUMBR.Contains('E+') then
- GPVendor.FAXNUMBR := '00000000000000'
- else
- GPVendor.FAXNUMBR := CopyStr(GPPM00200Vendor.FAXNUMBR.Trim(), 1, MaxStrLen(GPVendor.FAXNUMBR));
+ if GPPM00200Vendor.PHNUMBR1.Contains('E+') then
+ GPVendor.PHNUMBR1 := '00000000000000'
+ else
+ GPVendor.PHNUMBR1 := CopyStr(GPPM00200Vendor.PHNUMBR1.Trim(), 1, MaxStrLen(GPVendor.PHNUMBR1));
- if GPPM00201VendorSum.Get(GPPM00200Vendor.VENDORID) then
- GPVendor.AMOUNT := GPPM00201VendorSum.CURRBLNC;
+ if GPPM00200Vendor.FAXNUMBR.Contains('E+') then
+ GPVendor.FAXNUMBR := '00000000000000'
+ else
+ GPVendor.FAXNUMBR := CopyStr(GPPM00200Vendor.FAXNUMBR.Trim(), 1, MaxStrLen(GPVendor.FAXNUMBR));
- if GPSY01200NetAddresses.Get('VEN', GPPM00200Vendor.VENDORID, GPPM00200Vendor.VADDCDPR) then begin
- if ((GPSY01200NetAddresses.INET1 <> '') and (GPSY01200NetAddresses.INET1.Contains('@'))) then
- GPVendor.INET1 := CopyStr(GPSY01200NetAddresses.INET1.Trim(), 1, MaxStrLen(GPVendor.INET1));
+ if GPPM00201VendorSum.Get(GPPM00200Vendor.VENDORID) then
+ GPVendor.AMOUNT := GPPM00201VendorSum.CURRBLNC;
- if ((GPSY01200NetAddresses.INET2 <> '') and (GPSY01200NetAddresses.INET2.Contains('@'))) then
- GPVendor.INET2 := CopyStr(GPSY01200NetAddresses.INET2.Trim(), 1, MaxStrLen(GPVendor.INET2));
- end;
+ if GPSY01200NetAddresses.Get('VEN', GPPM00200Vendor.VENDORID, GPPM00200Vendor.VADDCDPR) then begin
+ if ((GPSY01200NetAddresses.INET1 <> '') and (GPSY01200NetAddresses.INET1.Contains('@'))) then
+ GPVendor.INET1 := CopyStr(GPSY01200NetAddresses.INET1.Trim(), 1, MaxStrLen(GPVendor.INET1));
- GPVendor.Insert();
- until GPPM00200Vendor.Next() = 0;
+ if ((GPSY01200NetAddresses.INET2 <> '') and (GPSY01200NetAddresses.INET2.Contains('@'))) then
+ GPVendor.INET2 := CopyStr(GPSY01200NetAddresses.INET2.Trim(), 1, MaxStrLen(GPVendor.INET2));
+ end;
end;
internal procedure PopulateGPVendorTransactions()
@@ -667,12 +673,6 @@ codeunit 40125 "GP Populate Combined Tables"
var
GPItem: Record "GP Item";
GPIV00101Inventory: Record "GP IV00101";
- GPIV00102InventoryQty: Record "GP IV00102";
- GPIV00105inventoryCurr: Record "GP IV00105";
- GPIV40201InventoryUom: Record "GP IV40201";
- DummyItem: Record Item;
- GPMC40000: Record "GP MC40000";
- FoundCurrency: Boolean;
begin
UpdateGLSetupUnitRoundingPrecisionIfNeeded();
@@ -682,79 +682,90 @@ codeunit 40125 "GP Populate Combined Tables"
repeat
Clear(GPItem);
if ShouldAddItemToStagingTable(GPIV00101Inventory) then begin
- GPItem.No := CopyStr(GPIV00101Inventory.ITEMNMBR.TrimEnd(), 1, MaxStrLen(DummyItem."No."));
- GPItem.Description := CopyStr(GPIV00101Inventory.ITEMDESC.TrimEnd(), 1, MaxStrLen(GPItem.Description));
- GPItem.SearchDescription := CopyStr(GPIV00101Inventory.ITEMDESC.TrimEnd(), 1, MaxStrLen(GPItem.SearchDescription));
+ SetGPItemFields(GPItem, GPIV00101Inventory);
+ GPItem.Insert();
+ end;
+ until GPIV00101Inventory.Next() = 0;
+ end;
+
+ internal procedure SetGPItemFields(var GPItem: Record "GP Item"; var GPIV00101Inventory: Record "GP IV00101")
+ var
+ GPIV00102InventoryQty: Record "GP IV00102";
+ GPIV00105inventoryCurr: Record "GP IV00105";
+ GPIV40201InventoryUom: Record "GP IV40201";
+ DummyItem: Record Item;
+ GPMC40000: Record "GP MC40000";
+ FoundCurrency: Boolean;
+ begin
+ GPItem.No := CopyStr(GPIV00101Inventory.ITEMNMBR.TrimEnd(), 1, MaxStrLen(DummyItem."No."));
+ GPItem.Description := CopyStr(GPIV00101Inventory.ITEMDESC.TrimEnd(), 1, MaxStrLen(GPItem.Description));
+ GPItem.SearchDescription := CopyStr(GPIV00101Inventory.ITEMDESC.TrimEnd(), 1, MaxStrLen(GPItem.SearchDescription));
#pragma warning disable AA0139
- GPItem.ShortName := GPIV00101Inventory.ITEMNMBR.TrimEnd();
+ GPItem.ShortName := GPIV00101Inventory.ITEMNMBR.TrimEnd();
#pragma warning restore AA0139
- case GPIV00101Inventory.ITEMTYPE of
- 1, 2:
- GPItem.ItemType := 0;
- 4, 5, 6:
- GPItem.ItemType := 1;
- 3:
- GPItem.ItemType := 2;
- end;
+ case GPIV00101Inventory.ITEMTYPE of
+ 1, 2:
+ GPItem.ItemType := 0;
+ 4, 5, 6:
+ GPItem.ItemType := 1;
+ 3:
+ GPItem.ItemType := 2;
+ end;
- case GPIV00101Inventory.VCTNMTHD of
- 1:
- GPItem.CostingMethod := Format(0);
- 2:
- GPItem.CostingMethod := Format(1);
- 3:
- GPItem.CostingMethod := Format(3);
- 4, 5:
- GPItem.CostingMethod := Format(4);
- else
- GPItem.CostingMethod := Format(0);
- end;
+ case GPIV00101Inventory.VCTNMTHD of
+ 1:
+ GPItem.CostingMethod := Format(0);
+ 2:
+ GPItem.CostingMethod := Format(1);
+ 3:
+ GPItem.CostingMethod := Format(3);
+ 4, 5:
+ GPItem.CostingMethod := Format(4);
+ else
+ GPItem.CostingMethod := Format(0);
+ end;
- GPItem.CurrentCost := GPIV00101Inventory.CURRCOST;
- GPItem.StandardCost := GPIV00101Inventory.STNDCOST;
+ GPItem.CurrentCost := GPIV00101Inventory.CURRCOST;
+ GPItem.StandardCost := GPIV00101Inventory.STNDCOST;
#pragma warning disable AA0139
- GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
- GPItem.PurchUnitOfMeasure := GPIV00101Inventory.PRCHSUOM.Trim();
- GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
- GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
+ GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
+ GPItem.PurchUnitOfMeasure := GPIV00101Inventory.PRCHSUOM.Trim();
+ GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
+ GPItem.SalesUnitOfMeasure := GPIV00101Inventory.SELNGUOM.Trim();
#pragma warning restore AA0139
- case GPIV00101Inventory.ITMTRKOP of
- 2:
- GPItem.ItemTrackingCode := ItemTrackingCodeSERIALLbl;
- 3:
- GPItem.ItemTrackingCode := ItemTrackingCodeLOTLbl;
- end;
+ case GPIV00101Inventory.ITMTRKOP of
+ 2:
+ GPItem.ItemTrackingCode := ItemTrackingCodeSERIALLbl;
+ 3:
+ GPItem.ItemTrackingCode := ItemTrackingCodeLOTLbl;
+ end;
- GPItem.ShipWeight := GPIV00101Inventory.ITEMSHWT / 100;
- if GPIV00101Inventory.ITEMTYPE = 2 then
- GPItem.InActive := true
- else
- GPItem.InActive := GPIV00101Inventory.INACTIVE;
+ GPItem.ShipWeight := GPIV00101Inventory.ITEMSHWT / 100;
+ if GPIV00101Inventory.ITEMTYPE = 2 then
+ GPItem.InActive := true
+ else
+ GPItem.InActive := GPIV00101Inventory.INACTIVE;
- GPIV40201InventoryUom.SetRange(UOMSCHDL, GPIV00101Inventory.UOMSCHDL);
- if GPIV40201InventoryUom.FindFirst() then
+ GPIV40201InventoryUom.SetRange(UOMSCHDL, GPIV00101Inventory.UOMSCHDL);
+ if GPIV40201InventoryUom.FindFirst() then
#pragma warning disable AA0139
- GPItem.BaseUnitOfMeasure := GPIV40201InventoryUom.BASEUOFM.Trim();
+ GPItem.BaseUnitOfMeasure := GPIV40201InventoryUom.BASEUOFM.Trim();
#pragma warning restore AA0139
- GPIV00102InventoryQty.SetRange(ITEMNMBR, GPIV00101Inventory.ITEMNMBR);
- GPIV00102InventoryQty.SetRange(LOCNCODE, '');
- GPIV00102InventoryQty.SetRange(RCRDTYPE, 1);
- if GPIV00102InventoryQty.FindFirst() then
- GPItem.QuantityOnHand := GPIV00102InventoryQty.QTYONHND;
-
- GPIV00105inventoryCurr.SetRange(ITEMNMBR, GPIV00101Inventory.ITEMNMBR);
- if GPIV00105inventoryCurr.FindSet() then
- repeat
- FoundCurrency := GPMC40000.Get(GPIV00105inventoryCurr.CURNCYID);
- until (GPIV00105inventoryCurr.Next() = 0) or FoundCurrency;
+ GPIV00102InventoryQty.SetRange(ITEMNMBR, GPIV00101Inventory.ITEMNMBR);
+ GPIV00102InventoryQty.SetRange(LOCNCODE, '');
+ GPIV00102InventoryQty.SetRange(RCRDTYPE, 1);
+ if GPIV00102InventoryQty.FindFirst() then
+ GPItem.QuantityOnHand := GPIV00102InventoryQty.QTYONHND;
- if FoundCurrency then
- GPItem.UnitListPrice := GPIV00105inventoryCurr.LISTPRCE;
+ GPIV00105inventoryCurr.SetRange(ITEMNMBR, GPIV00101Inventory.ITEMNMBR);
+ if GPIV00105inventoryCurr.FindSet() then
+ repeat
+ FoundCurrency := GPMC40000.Get(GPIV00105inventoryCurr.CURNCYID);
+ until (GPIV00105inventoryCurr.Next() = 0) or FoundCurrency;
- GPItem.Insert();
- end;
- until GPIV00101Inventory.Next() = 0;
+ if FoundCurrency then
+ GPItem.UnitListPrice := GPIV00105inventoryCurr.LISTPRCE;
end;
local procedure UpdateGLSetupUnitRoundingPrecisionIfNeeded()
diff --git a/Apps/W1/HybridGP/app/src/Migration/Support/CheckBooks/GPCheckbookTransactions.table.al b/Apps/W1/HybridGP/app/src/Migration/Support/CheckBooks/GPCheckbookTransactions.table.al
index bd805e1af7..75a6e52702 100644
--- a/Apps/W1/HybridGP/app/src/Migration/Support/CheckBooks/GPCheckbookTransactions.table.al
+++ b/Apps/W1/HybridGP/app/src/Migration/Support/CheckBooks/GPCheckbookTransactions.table.al
@@ -220,4 +220,9 @@ table 40101 "GP Checkbook Transactions"
Clustered = true;
}
}
+
+ internal procedure ShouldFlipSign(): Boolean
+ begin
+ exit((Rec.CMTrxType = 3) or (Rec.CMTrxType = 4) or (Rec.CMTrxType = 6));
+ end;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al b/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al
index f39e6ee911..e175bac1af 100644
--- a/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al
+++ b/Apps/W1/HybridGP/app/src/Migration/Support/HelperFunctions.codeunit.al
@@ -1071,7 +1071,7 @@ codeunit 4037 "Helper Functions"
if GPVendor.FindSet() then
repeat
if GPVendorMigrator.ShouldMigrateVendor(GPVendor.VENDORID, IsTemporaryVendor, HasOpenPurchaseOrders, HasOpenTransactions) then
- VendorCount := VendorCount + 1;
+ VendorCount += 1;
until GPVendor.Next() = 0;
@@ -1154,7 +1154,7 @@ codeunit 4037 "Helper Functions"
GenJournalLine.SetRange("Journal Template Name", GenJournalBatch."Journal Template Name");
GenJournalLine.SetRange("Journal Batch Name", GenJournalBatch.Name);
if not GenJournalLine.IsEmpty() then
- UnpostedBatchCount := UnpostedBatchCount + 1;
+ UnpostedBatchCount += 1;
until GenJournalBatch.Next() = 0;
exit(UnpostedBatchCount);
@@ -1177,7 +1177,7 @@ codeunit 4037 "Helper Functions"
repeat
ItemJournalLine.SetRange("Journal Batch Name", ItemJournalBatch.Name);
if not ItemJournalLine.IsEmpty() then
- UnpostedBatchCount += UnpostedBatchCount + 1;
+ UnpostedBatchCount += 1;
until ItemJournalBatch.Next() = 0;
exit(UnpostedBatchCount);
@@ -1200,20 +1200,23 @@ codeunit 4037 "Helper Functions"
repeat
StatisticalAccJournalLine.SetRange("Journal Batch Name", StatisticalAccJournalBatch.Name);
if not StatisticalAccJournalLine.IsEmpty() then
- UnpostedBatchCount += UnpostedBatchCount + 1;
+ UnpostedBatchCount += 1;
until StatisticalAccJournalBatch.Next() = 0;
exit(UnpostedBatchCount);
end;
- internal procedure GetUnpostedBatchCountForCompany(CompanyNameTxt: Text; var TotalGLBatchCount: Integer; var TotalStatisticalBatchCount: Integer; var TotalItemBatchCount: Integer)
+ internal procedure GetUnpostedBatchCountForCompany(CompanyNameTxt: Text; var TotalGLBatchCount: Integer; var TotalStatisticalBatchCount: Integer; var TotalBankBatchCount: Integer; var TotalCustomerBatchCount: Integer; var TotalItemBatchCount: Integer; var TotalVendorBatchCount: Integer)
var
HybridCompanyStatus: Record "Hybrid Company Status";
GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
begin
TotalGLBatchCount := 0;
TotalStatisticalBatchCount := 0;
+ TotalBankBatchCount := 0;
+ TotalCustomerBatchCount := 0;
TotalItemBatchCount := 0;
+ TotalVendorBatchCount := 0;
if not HybridCompanyStatus.Get(CompanyNameTxt) then
exit;
@@ -1231,16 +1234,18 @@ codeunit 4037 "Helper Functions"
end;
if not GPCompanyAdditionalSettings."Skip Posting Customer Batches" then
- TotalGLBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, CustomerBatchNameTxt);
+ TotalCustomerBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, CustomerBatchNameTxt);
if not GPCompanyAdditionalSettings."Skip Posting Vendor Batches" then
- TotalGLBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, VendorBatchNameTxt);
+ TotalVendorBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, VendorBatchNameTxt);
if not GPCompanyAdditionalSettings."Skip Posting Bank Batches" then
- TotalGLBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, BankBatchNameTxt);
+ TotalBankBatchCount += GetGLBatchCountWithUnpostedLinesForCompany(CompanyNameTxt, GeneralTemplateNameTxt, BankBatchNameTxt);
if not GPCompanyAdditionalSettings."Skip Posting Item Batches" then
TotalItemBatchCount := GetItemBatchCountWithUnpostedLinesForCompany(CompanyNameTxt);
+
+ TotalGLBatchCount += TotalGLBatchCount + TotalBankBatchCount + TotalCustomerBatchCount + TotalVendorBatchCount;
end;
procedure PostGLTransactions()
diff --git a/Apps/W1/HybridGP/app/src/codeunits/GPMigrationValidator.Codeunit.al b/Apps/W1/HybridGP/app/src/codeunits/GPMigrationValidator.Codeunit.al
new file mode 100644
index 0000000000..b446859850
--- /dev/null
+++ b/Apps/W1/HybridGP/app/src/codeunits/GPMigrationValidator.Codeunit.al
@@ -0,0 +1,1583 @@
+namespace Microsoft.DataMigration.GP;
+
+using Microsoft.DataMigration;
+using Microsoft.Finance.GeneralLedger.Account;
+using Microsoft.Finance.Analysis.StatisticalAccount;
+using Microsoft.Bank.BankAccount;
+using Microsoft.Sales.Customer;
+using Microsoft.Inventory.Item;
+using Microsoft.Purchases.Vendor;
+using Microsoft.Purchases.Document;
+using Microsoft.Finance.GeneralLedger.Setup;
+using Microsoft.Purchases.Remittance;
+using Microsoft.Finance.Currency;
+
+codeunit 40903 "GP Migration Validator"
+{
+ trigger OnRun()
+ var
+ GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
+ begin
+ if not GPCompanyAdditionalSettings.Get(CompanyName()) then
+ exit;
+
+ ValidatorCodeLbl := GetValidatorCode();
+ CompanyNameTxt := CompanyName();
+ DefaultCurrency.InitRoundingPrecision();
+
+ GetUnpostedBatchCounts();
+
+ RunGLAccountMigrationValidation(GPCompanyAdditionalSettings);
+ RunStatisticalAccountMigrationValidation(GPCompanyAdditionalSettings);
+ RunBankAccountMigrationValidation(GPCompanyAdditionalSettings);
+ RunCustomerMigrationValidation(GPCompanyAdditionalSettings);
+ RunItemMigrationValidation(GPCompanyAdditionalSettings);
+ RunPurchaseOrderMigrationValidation(GPCompanyAdditionalSettings);
+ RunVendorMigrationValidation(GPCompanyAdditionalSettings);
+
+ MigrationValidation.ReportCompanyValidated();
+ end;
+
+ local procedure GetUnpostedBatchCounts()
+ var
+ HelperFunctions: Codeunit "Helper Functions";
+ begin
+ TotalUnpostedGLBatchCount := 0;
+ TotalUnpostedStatisticalBatchCount := 0;
+ TotalUnpostedBankBatchCount := 0;
+ TotalUnpostedCustomerBatchCount := 0;
+ TotalUnpostedItemBatchCount := 0;
+ TotalUnpostedVendorBatchCount := 0;
+
+ HelperFunctions.GetUnpostedBatchCountForCompany(CompanyName(), TotalUnpostedGLBatchCount, TotalUnpostedStatisticalBatchCount, TotalUnpostedBankBatchCount, TotalUnpostedCustomerBatchCount, TotalUnpostedItemBatchCount, TotalUnpostedVendorBatchCount);
+ end;
+
+ local procedure RunGLAccountMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GLAccount: Record "G/L Account";
+ GPAccount: Record "GP Account";
+ FirstAccount: Record "GP GL00100";
+ GPGL00100: Record "GP GL00100";
+ GPGL10111: Record "GP GL10111";
+ GPGL40200: Record "GP GL40200";
+ GPSY00300: Record "GP SY00300";
+ GPGLTransactions: Record "GP GLTransactions";
+ GPFiscalPeriods: Record "GP Fiscal Periods";
+ HelperFunctions: Codeunit "Helper Functions";
+ BalanceFailureShouldBeWarning: Boolean;
+ GPAccountNo: Code[20];
+ GPAccountBeginningBalance: Decimal;
+ AccountFilter: Text;
+ EntityType: Text[50];
+ GPAccountDescription: Text[100];
+ ValidatedAccountNos: List of [Text];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepGLAccountLbl) then
+ exit;
+
+ EntityType := GlAccountEntityCaptionLbl;
+ BalanceFailureShouldBeWarning := (TotalUnpostedGLBatchCount > 0);
+
+ // GP
+ if GPCompanyAdditionalSettings.GetGLModuleEnabled() then begin
+ GPGL00100.SetRange(ACCTTYPE, 1);
+ GPGL00100.SetFilter(MNACSGMT, '<>%1', '');
+ if GPGL00100.FindSet() then
+ repeat
+ GPAccountBeginningBalance := 0;
+ GPAccountNo := CopyStr(GPGL00100.MNACSGMT.TrimEnd(), 1, MaxStrLen(GPAccountNo));
+ if ValidatedAccountNos.Contains(GPAccountNo) then
+ continue;
+
+ ValidatedAccountNos.Add(GPAccountNo);
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, GPAccountNo);
+
+ GPSY00300.SetRange(MNSEGIND, true);
+ if GPSY00300.FindFirst() then begin
+ GPGL40200.SetRange(SGMNTID, GPGL00100.MNACSGMT);
+ GPGL40200.SetRange(SGMTNUMB, GPSY00300.SGMTNUMB);
+ if GPGL40200.FindFirst() then
+ GPAccountDescription := CopyStr(GPGL40200.DSCRIPTN.TrimEnd(), 1, MaxStrLen(GPAccountDescription));
+ end;
+
+ if GPAccountDescription = '' then begin
+ FirstAccount.SetCurrentKey(ACTINDX);
+ FirstAccount.SetRange(MNACSGMT, GPGL00100.MNACSGMT);
+ FirstAccount.SetRange(ACCTTYPE, 1);
+ if FirstAccount.FindFirst() then
+ GPAccountDescription := CopyStr(FirstAccount.ACTDESCR.TrimEnd(), 1, MaxStrLen(GPAccountDescription));
+ end;
+
+ Clear(GPAccount);
+ GPAccount.AcctNum := GPAccountNo;
+ GPAccount.AcctIndex := GPGL00100.ACTINDX;
+ GPAccount.Name := CopyStr(GPAccountDescription.Trim(), 1, MaxStrLen(GLAccount.Name));
+ GPAccount.SearchName := GPAccount.Name;
+ GPAccount.AccountCategory := GPGL00100.ACCATNUM;
+ GPAccount.IncomeBalance := GPGL00100.PSTNGTYP = 1;
+ GPAccount.DebitCredit := GPGL00100.TPCLBLNC;
+ GPAccount.Active := GPGL00100.ACTIVE;
+ GPAccount.DirectPosting := GPGL00100.ACCTENTR;
+ GPAccount.AccountSubcategoryEntryNo := GPGL00100.ACCATNUM;
+ GPAccount.AccountType := GPGL00100.ACCTTYPE;
+
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyGLMaster() then
+ if not GPCompanyAdditionalSettings.GetSkipPostingAccountBatches() then
+ if GPGL00100.PSTNGTYP = 0 then begin
+ AccountFilter := GetAccountFilter(GPAccountNo, 1);
+
+ if AccountFilter <> '' then begin
+
+ // Beginning Balance
+ GPGL10111.SetFilter(ACTINDX, AccountFilter);
+ GPGL10111.SetRange(PERIODID, 0);
+ GPGL10111.SetRange(YEAR1, GPCompanyAdditionalSettings."Oldest GL Year to Migrate");
+ if GPGL10111.FindSet() then
+ repeat
+ GPAccountBeginningBalance += RoundWithSpecPrecision(GPGL10111.PERDBLNC);
+ until GPGL10111.Next() = 0;
+
+ // Trx summary
+ GPGLTransactions.SetCurrentKey(YEAR1, PERIODID, ACTINDX);
+ GPGLTransactions.SetFilter(ACTINDX, AccountFilter);
+
+ if GPCompanyAdditionalSettings."Oldest GL Year to Migrate" > 0 then
+ GPGLTransactions.SetFilter(YEAR1, '>= %1', GPCompanyAdditionalSettings."Oldest GL Year to Migrate");
+
+ if GPGLTransactions.FindSet() then
+ repeat
+ if GPFiscalPeriods.Get(GPGLTransactions.PERIODID, GPGLTransactions.YEAR1) then
+ GPAccountBeginningBalance += RoundWithSpecPrecision(GPGLTransactions.PERDBLNC);
+ until GPGLTransactions.Next() = 0;
+ end;
+ end;
+
+ Clear(GLAccount);
+ GLAccount.SetLoadFields("No.", Name, "Account Type", "Account Category", "Debit/Credit", "Account Subcategory Entry No.", "Income/Balance", Balance);
+ if not MigrationValidation.ValidateRecordExists(Test_ACCOUNTEXISTS_Tok, GLAccount.Get(GPAccount.AcctNum), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ GLAccount.CalcFields(Balance);
+
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTNAME_Tok, GPAccount.Name, GLAccount.Name, AccountNameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTTYPE_Tok, Format(GLAccount."Account Type"::Posting), Format(GLAccount."Account Type"), AccountTypeLbl);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTCATEGORY_Tok, HelperFunctions.ConvertAccountCategory(GPAccount), GLAccount."Account Category".AsInteger(), AccountCategoryLbl);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTDEBCRED_Tok, HelperFunctions.ConvertDebitCreditType(GPAccount), GLAccount."Debit/Credit", AccountDebitCreditLbl);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTSUBCATEGORY_Tok, HelperFunctions.AssignSubAccountCategory(GPAccount), GLAccount."Account Subcategory Entry No.", AccountSubcategoryLbl);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTINCBAL_Tok, HelperFunctions.ConvertIncomeBalanceType(GPAccount), GLAccount."Income/Balance".AsInteger(), AccountIncomeBalanceLbl);
+ MigrationValidation.ValidateAreEqual(Test_ACCOUNTBALANCE_Tok, GPAccountBeginningBalance, GLAccount.Balance, BeginningBalanceLbl, BalanceFailureShouldBeWarning);
+ until GPGL00100.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepGLAccountLbl);
+ Commit();
+ end;
+
+ local procedure RunStatisticalAccountMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GeneralLedgerSetup: Record "General Ledger Setup";
+ FirstAccount: Record "GP GL00100";
+ GPGL00100: Record "GP GL00100";
+ GPGL10111: Record "GP GL10111";
+ GPGL40200: Record "GP GL40200";
+ GPSY00300: Record "GP SY00300";
+ GPGLTransactions: Record "GP GLTransactions";
+ GPFiscalPeriods: Record "GP Fiscal Periods";
+ StatisticalAccount: Record "Statistical Account";
+ BalanceFailureShouldBeWarning: Boolean;
+ DimensionCode1: Code[20];
+ DimensionCode2: Code[20];
+ GPAccountNo: Code[20];
+ GPAccountBeginningBalance: Decimal;
+ AccountFilter: Text;
+ EntityType: Text[50];
+ GPAccountDescription: Text[100];
+ ValidatedAccountNos: List of [Text];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepStatAccountLbl) then
+ exit;
+
+ EntityType := StatisticalAccountEntityCaptionLbl;
+ BalanceFailureShouldBeWarning := (TotalUnpostedStatisticalBatchCount > 0);
+
+ if GeneralLedgerSetup.Get() then begin
+ DimensionCode1 := GeneralLedgerSetup."Global Dimension 1 Code";
+ DimensionCode2 := GeneralLedgerSetup."Global Dimension 2 Code";
+ end;
+
+ if GPCompanyAdditionalSettings.GetGLModuleEnabled() then begin
+ GPGL00100.SetRange(ACCTTYPE, 2);
+ GPGL00100.SetFilter(MNACSGMT, '<>%1', '');
+ if GPGL00100.FindSet() then
+ repeat
+ GPAccountBeginningBalance := 0;
+ GPAccountNo := CopyStr(GPGL00100.MNACSGMT.TrimEnd(), 1, MaxStrLen(GPAccountNo));
+ if ValidatedAccountNos.Contains(GPAccountNo) then
+ continue;
+
+ ValidatedAccountNos.Add(GPAccountNo);
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, GPAccountNo);
+
+ GPSY00300.SetRange(MNSEGIND, true);
+ if GPSY00300.FindFirst() then begin
+ GPGL40200.SetRange(SGMNTID, GPGL00100.MNACSGMT);
+ GPGL40200.SetRange(SGMTNUMB, GPSY00300.SGMTNUMB);
+ if GPGL40200.FindFirst() then
+ GPAccountDescription := CopyStr(GPGL40200.DSCRIPTN.TrimEnd(), 1, MaxStrLen(GPAccountDescription));
+ end;
+
+ if GPAccountDescription = '' then begin
+ FirstAccount.SetCurrentKey(ACTINDX);
+ FirstAccount.SetRange(MNACSGMT, GPGL00100.MNACSGMT);
+ FirstAccount.SetRange(ACCTTYPE, 2);
+ if FirstAccount.FindFirst() then
+ GPAccountDescription := CopyStr(FirstAccount.ACTDESCR.TrimEnd(), 1, MaxStrLen(GPAccountDescription));
+ end;
+
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyGLMaster() then
+ if not GPCompanyAdditionalSettings.GetSkipPostingAccountBatches() then begin
+ Clear(GPGL10111);
+ AccountFilter := GetAccountFilter(GPAccountNo, 2);
+
+ if AccountFilter <> '' then begin
+
+ // Beginning Balance
+ GPGL10111.SetFilter(ACTINDX, AccountFilter);
+ GPGL10111.SetRange(PERIODID, 0);
+ GPGL10111.SetRange(YEAR1, GPCompanyAdditionalSettings."Oldest GL Year to Migrate");
+ if GPGL10111.FindSet() then
+ repeat
+ GPAccountBeginningBalance += RoundWithSpecPrecision(GPGL10111.PERDBLNC);
+ until GPGL10111.Next() = 0;
+
+ // Trx summary
+ GPGLTransactions.SetCurrentKey(YEAR1, PERIODID, ACTINDX);
+ GPGLTransactions.SetFilter(ACTINDX, AccountFilter);
+
+ if GPCompanyAdditionalSettings."Oldest GL Year to Migrate" > 0 then
+ GPGLTransactions.SetFilter(YEAR1, '>= %1', GPCompanyAdditionalSettings."Oldest GL Year to Migrate");
+
+ if GPGLTransactions.FindSet() then
+ repeat
+ if GPFiscalPeriods.Get(GPGLTransactions.PERIODID, GPGLTransactions.YEAR1) then
+ GPAccountBeginningBalance += RoundWithSpecPrecision(GPGLTransactions.PERDBLNC);
+ until GPGLTransactions.Next() = 0;
+ end;
+ end;
+
+ Clear(StatisticalAccount);
+ StatisticalAccount.SetLoadFields("No.", Name, "Global Dimension 1 Code", "Global Dimension 2 Code", Balance);
+ if not MigrationValidation.ValidateRecordExists(Test_STATACCOUNTEXISTS_Tok, StatisticalAccount.Get(GPAccountNo), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ StatisticalAccount.CalcFields(Balance);
+
+ MigrationValidation.ValidateAreEqual(Test_STATACCOUNTNAME_Tok, GPAccountDescription, StatisticalAccount.Name, AccountNameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_STATACCOUNTDIM1_Tok, DimensionCode1, StatisticalAccount."Global Dimension 1 Code", Dimension1Lbl);
+ MigrationValidation.ValidateAreEqual(Test_STATACCOUNTDIM2_Tok, DimensionCode2, StatisticalAccount."Global Dimension 2 Code", Dimension2Lbl);
+ MigrationValidation.ValidateAreEqual(Test_STATACCOUNTBALANCE_Tok, GPAccountBeginningBalance, StatisticalAccount.Balance, BeginningBalanceLbl, BalanceFailureShouldBeWarning);
+ until GPGL00100.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepStatAccountLbl);
+ Commit();
+ end;
+
+ local procedure RunBankAccountMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ BankAccount: Record "Bank Account";
+ GPBankMSTR: Record "GP Bank MSTR";
+ GPCheckbookMSTR: Record "GP Checkbook MSTR";
+ GPCheckbookTransactions: Record "GP Checkbook Transactions";
+ GPCM20600: Record "GP CM20600";
+ BalanceFailureShouldBeWarning: Boolean;
+ ShouldFlipSign: Boolean;
+ ShouldInclude: Boolean;
+ GPAccountNo: Code[20];
+ GPAccountBalance: Decimal;
+ EntityType: Text[50];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepBankAccountLbl) then
+ exit;
+
+ EntityType := BankAccountEntityCaptionLbl;
+ BalanceFailureShouldBeWarning := (TotalUnpostedBankBatchCount > 0);
+
+ if GPCompanyAdditionalSettings.GetBankModuleEnabled() then
+ if GPCheckbookMSTR.FindSet() then
+ repeat
+ GPAccountNo := CopyStr(GPCheckbookMSTR.CHEKBKID.TrimEnd(), 1, MaxStrLen(GPAccountNo));
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, GPAccountNo);
+ ShouldInclude := true;
+ GPAccountBalance := 0;
+
+ if not GPCompanyAdditionalSettings.GetMigrateInactiveCheckbooks() then
+ if GPCheckbookMSTR.INACTIVE then
+ ShouldInclude := false;
+
+ if ShouldInclude then begin
+
+ // Balance
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyBankMaster() then
+ if not GPCompanyAdditionalSettings.GetSkipPostingBankBatches() then begin
+ GPAccountBalance := GPCheckbookMSTR.Last_Reconciled_Balance;
+
+ GPCheckbookTransactions.SetRange(CHEKBKID, GPCheckbookMSTR.CHEKBKID);
+ GPCheckbookTransactions.SetRange(Recond, false);
+ GPCheckbookTransactions.SetFilter(TRXAMNT, '<>%1', 0);
+ if GPCheckbookTransactions.FindSet() then
+ repeat
+ ShouldFlipSign := false;
+
+ if GPCheckbookTransactions.ShouldFlipSign() then
+ ShouldFlipSign := true;
+
+ if GPCheckbookTransactions.CMTrxType = 7 then begin
+ GPCM20600.SetRange(CMXFRNUM, GPCheckbookTransactions.CMTrxNum);
+ GPCM20600.SetRange(CMFRMRECNUM, GPCheckbookTransactions.CMRECNUM);
+ if GPCM20600.FindFirst() then
+ if GPCM20600.Xfr_Record_Number > 0 then
+ ShouldFlipSign := true;
+ end;
+
+ if ShouldFlipSign then
+ GPCheckbookTransactions.TRXAMNT := GPCheckbookTransactions.TRXAMNT * -1;
+
+ GPAccountBalance := GPAccountBalance + RoundWithSpecPrecision(GPCheckbookTransactions.TRXAMNT);
+ until GPCheckbookTransactions.Next() = 0;
+ end;
+
+ Clear(BankAccount);
+ BankAccount.SetLoadFields("No.", Name, "Bank Account No.", Balance, Address, "Address 2", City, "Phone No.", "Transit No.", "Fax No.", County, "Post Code", "Bank Branch No.");
+ if not MigrationValidation.ValidateRecordExists(Test_BANKACCOUNTEXISTS_Tok, BankAccount.Get(GPAccountNo), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ BankAccount.CalcFields(Balance);
+
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTNAME_Tok, CopyStr(GPCheckbookMSTR.DSCRIPTN.TrimEnd(), 1, MaxStrLen(BankAccount.Name)), BankAccount.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTNO_Tok, CopyStr(GPCheckbookMSTR.BNKACTNM.TrimEnd(), 1, MaxStrLen(BankAccount."Bank Account No.")), BankAccount."Bank Account No.", BankAccountNumberLbl, false, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTADDR_Tok, CopyStr(GPBankMSTR.ADDRESS1.TrimEnd(), 1, MaxStrLen(BankAccount.Address)), BankAccount.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTADDR2_Tok, CopyStr(GPBankMSTR.ADDRESS2.TrimEnd(), 1, MaxStrLen(BankAccount."Address 2")), BankAccount."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTCITY_Tok, CopyStr(GPBankMSTR.CITY.TrimEnd(), 1, MaxStrLen(BankAccount.City)), BankAccount.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTCOUNTY_Tok, CopyStr(GPBankMSTR.STATE.TrimEnd(), 1, MaxStrLen(BankAccount.County)), BankAccount.County, CountyLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTPOSTCODE_Tok, CopyStr(GPBankMSTR.ZIPCODE.TrimEnd(), 1, MaxStrLen(BankAccount."Post Code")), BankAccount."Post Code", PostCodeLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTPHN_Tok, CopyStr(GPBankMSTR.PHNUMBR1.TrimEnd(), 1, MaxStrLen(BankAccount."Phone No.")), BankAccount."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTFAX_Tok, CopyStr(GPBankMSTR.FAXNUMBR.TrimEnd(), 1, MaxStrLen(BankAccount."Fax No.")), BankAccount."Fax No.", FaxLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTTRANSITNO_Tok, CopyStr(GPBankMSTR.TRNSTNBR.TrimEnd(), 1, MaxStrLen(BankAccount."Transit No.")), BankAccount."Transit No.", TransitNoLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTBRANCHNO_Tok, CopyStr(GPBankMSTR.BNKBRNCH.TrimEnd(), 1, MaxStrLen(BankAccount."Bank Branch No.")), BankAccount."Bank Branch No.", BankBranchNoLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_BANKACCOUNTBALANCE_Tok, GPAccountBalance, BankAccount.Balance, BalanceLbl, BalanceFailureShouldBeWarning);
+ end;
+ until GPCheckbookMSTR.Next() = 0;
+ LogValidationProgress(ValidationStepBankAccountLbl);
+ Commit();
+ end;
+
+ local procedure RunCustomerMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ Customer: Record Customer;
+ GPCustomer: Record "GP Customer";
+ GPRM00101: Record "GP RM00101";
+ GPRM20101: record "GP RM20101";
+ GPPaymentTerms: Record "GP Payment Terms";
+ GPPopulateCombinedTables: Codeunit "GP Populate Combined Tables";
+ HelperFunctions: Codeunit "Helper Functions";
+ BalanceFailureShouldBeWarning: Boolean;
+ ShouldInclude: Boolean;
+ ClassName: Code[20];
+ CustomerNo: Code[20];
+ TaxLiable: Boolean;
+ PhoneNo: Text[30];
+ FaxNo: Text[30];
+ PaymentTerms: Code[10];
+ GPCustomerBalance: Decimal;
+ EntityType: Text[50];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepCustomerLbl) then
+ exit;
+
+ EntityType := CustomerEntityCaptionLbl;
+ BalanceFailureShouldBeWarning := (TotalUnpostedCustomerBatchCount > 0);
+
+ if GPCompanyAdditionalSettings.GetReceivablesModuleEnabled() then begin
+ GPRM00101.SetFilter(CUSTNMBR, '<>%1', '');
+ if GPRM00101.FindSet() then
+ repeat
+ CustomerNo := CopyStr(GPRM00101.CUSTNMBR.TrimEnd(), 1, MaxStrLen(CustomerNo));
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, CustomerNo);
+ GPCustomerBalance := 0;
+ ShouldInclude := true;
+
+ if not GPCompanyAdditionalSettings.GetMigrateInactiveCustomers() then
+ if GPRM00101.INACTIVE then
+ ShouldInclude := false;
+
+ if ShouldInclude then begin
+ if GPCompanyAdditionalSettings.GetMigrateCustomerClasses() then
+ ClassName := CopyStr(GPRM00101.CUSTCLAS.TrimEnd(), 1, MaxStrLen(ClassName));
+
+ if ClassName = '' then
+ ClassName := DefaultClassNameTok;
+
+ Clear(GPCustomer);
+ GPPopulateCombinedTables.SetGPCustomerFields(GPCustomer, GPRM00101);
+
+ GPCustomer.PHONE1 := HelperFunctions.CleanGPPhoneOrFaxNumber(GPCustomer.PHONE1);
+ GPCustomer.FAX := HelperFunctions.CleanGPPhoneOrFaxNumber(GPCustomer.FAX);
+
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyReceivablesMaster() then
+ if not GPCompanyAdditionalSettings.GetSkipPostingCustomerBatches() then begin
+ GPRM20101.SetRange(CUSTNMBR, GPRM00101.CUSTNMBR);
+ GPRM20101.SetRange(RMDTYPAL, 1, 9);
+ GPRM20101.SetRange(VOIDSTTS, 0);
+ GPRM20101.SetFilter(CURTRXAM, '>=0.01');
+ if GPRM20101.FindSet() then
+ repeat
+ if GPRM20101.RMDTYPAL < 7 then
+ GPCustomerBalance := RoundWithSpecPrecision(GPCustomerBalance + GPRM20101.CURTRXAM)
+ else
+ GPCustomerBalance := GPCustomerBalance + RoundWithSpecPrecision(GPRM20101.CURTRXAM * -1);
+ until GPRM20101.Next() = 0;
+ end;
+
+ if not MigrationValidation.ValidateRecordExists(Test_CUSTOMEREXISTS_Tok, Customer.Get(CustomerNo), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ Customer.CalcFields(Balance);
+
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME_Tok, CopyStr(GPRM00101.CUSTNAME.TrimEnd(), 1, MaxStrLen(Customer.Name)), Customer.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERPOSTINGGROUP_Tok, ClassName, Customer."Customer Posting Group", CustomerPostingGroupLbl);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERADDR_Tok, CopyStr(GPCustomer.ADDRESS1, 1, MaxStrLen(Customer.Address)), Customer.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERADDR2_Tok, CopyStr(GPCustomer.ADDRESS2, 1, MaxStrLen(Customer."Address 2")), Customer."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERCITY_Tok, CopyStr(GPCustomer.CITY, 1, MaxStrLen(Customer.City)), Customer.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERNAME2_Tok, CopyStr(GPCustomer.STMTNAME, 1, MaxStrLen(Customer."Name 2")), Customer."Name 2", Name2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERCREDITLMT_Tok, GPCustomer.CRLMTAMT, Customer."Credit Limit (LCY)", CreditLimitLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERCONTACT_Tok, CopyStr(GPCustomer.CNTCPRSN, 1, MaxStrLen(Customer.Contact)), Customer.Contact, ContactLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERSALESPERSON_Tok, UpperCase(GPCustomer.SLPRSNID.TrimEnd()), Customer."Salesperson Code", SalesPersonLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERSHIPMETHOD_Tok, UpperCase(CopyStr(GPCustomer.SHIPMTHD, 1, MaxStrLen(Customer."Shipment Method Code")).TrimEnd()), Customer."Shipment Method Code", ShipmentMethodLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERTERRITORY_Tok, UpperCase(CopyStr(GPCustomer.SALSTERR, 1, MaxStrLen(Customer."Territory Code")).TrimEnd()), Customer."Territory Code", TerritoryLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERTAXAREA_Tok, UpperCase(GPCustomer.TAXSCHID.TrimEnd()), Customer."Tax Area Code", TaxAreaLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERBALANCE_Tok, GPCustomerBalance, Customer.Balance, BalanceLbl, BalanceFailureShouldBeWarning);
+
+ TaxLiable := (GPCustomer.TAXSCHID <> '');
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERTAXLIABLE_Tok, TaxLiable, Customer."Tax Liable", TaxLiableLbl, true);
+
+ PhoneNo := '';
+ if GPCustomer.PHONE1 <> '' then
+ if not HelperFunctions.ContainsAlphaChars(GPCustomer.PHONE1) then
+ PhoneNo := GPCustomer.PHONE1;
+
+ FaxNo := '';
+ if GPCustomer.FAX <> '' then
+ if not HelperFunctions.ContainsAlphaChars(GPCustomer.FAX) then
+ FaxNo := GPCustomer.FAX;
+
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERPHN_Tok, PhoneNo, Customer."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERFAX_Tok, FaxNo, Customer."Fax No.", FaxLbl, true);
+
+ PaymentTerms := '';
+ if GPCustomer.PYMTRMID <> '' then
+ if GPPaymentTerms.Get(GPCustomer.PYMTRMID) then begin
+ PaymentTerms := CopyStr(GPCustomer.PYMTRMID, 1, MaxStrLen(Customer."Payment Terms Code"));
+ if GPPaymentTerms.PYMTRMID_New <> '' then
+ PaymentTerms := GPPaymentTerms.PYMTRMID_New;
+ end;
+
+ MigrationValidation.ValidateAreEqual(Test_CUSTOMERPMTTERMS_Tok, PaymentTerms, Customer."Payment Terms Code", PaymentTermsLbl, true);
+
+ ValidateCustomerShipToAddresses(Customer);
+ end;
+ until GPRM00101.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepCustomerLbl);
+ Commit();
+ end;
+
+ local procedure ValidateCustomerShipToAddresses(var Customer: Record Customer)
+ var
+ GPCustomerAddress: Record "GP Customer Address";
+ ShipToAddress: Record "Ship-to Address";
+ AddressCode: Code[10];
+ EntityType: Text[50];
+ ContextCode: Text[250];
+ begin
+ EntityType := CustomerAddressEntityCaptionLbl;
+
+ GPCustomerAddress.SetRange(CUSTNMBR, Customer."No.");
+ if GPCustomerAddress.FindSet() then
+ repeat
+ AddressCode := CopyStr(GPCustomerAddress.ADRSCODE, 1, MaxStrLen(AddressCode));
+ ContextCode := Customer."No." + '-' + AddressCode;
+
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, ContextCode);
+
+ if not MigrationValidation.ValidateRecordExists(Test_SHIPADDREXISTS_Tok, ShipToAddress.Get(Customer."No.", AddressCode), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ if (CopyStr(GPCustomerAddress.PHONE1, 1, 14) = '00000000000000') then
+ GPCustomerAddress.PHONE1 := '';
+
+ if (CopyStr(GPCustomerAddress.FAX, 1, 14) = '00000000000000') then
+ GPCustomerAddress.FAX := '';
+
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRNAME_Tok, Customer.Name, ShipToAddress.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRADDR_Tok, GPCustomerAddress.ADDRESS1.TrimEnd(), ShipToAddress.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRADDR2_Tok, CopyStr(GPCustomerAddress.ADDRESS2.TrimEnd(), 1, MaxStrLen(ShipToAddress."Address 2")), ShipToAddress."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRCITY_Tok, CopyStr(GPCustomerAddress.CITY.TrimEnd(), 1, MaxStrLen(ShipToAddress.City)), ShipToAddress.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRPOSTCODE_Tok, UpperCase(GPCustomerAddress.ZIP.TrimEnd()), ShipToAddress."Post Code", PostCodeLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRPHN_Tok, GPCustomerAddress.PHONE1.TrimEnd(), ShipToAddress."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRFAX_Tok, GPCustomerAddress.FAX.TrimEnd(), ShipToAddress."Fax No.", FaxLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRCONTACT_Tok, GPCustomerAddress.CNTCPRSN.TrimEnd(), ShipToAddress.Contact, ContactLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRSHIPMETHOD_Tok, CopyStr(GPCustomerAddress.SHIPMTHD.TrimEnd(), 1, MaxStrLen(ShipToAddress."Shipment Method Code")), ShipToAddress."Shipment Method Code", ShipmentMethodLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRCOUNTY_Tok, GPCustomerAddress.STATE.TrimEnd(), ShipToAddress.County, CountyLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_SHIPADDRTAXAREA_Tok, GPCustomerAddress.TAXSCHID.TrimEnd(), ShipToAddress."Tax Area Code", TaxAreaLbl, true);
+
+ until GPCustomerAddress.Next() = 0;
+ end;
+
+ local procedure RunItemMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GPItem: Record "GP Item";
+ GPIV00101: Record "GP IV00101";
+ GPIV00200: Record "GP IV00200";
+ GPIV00300: Record "GP IV00300";
+ GPIV10200: Record "GP IV10200";
+ GPIV00104: Record "GP IV00104";
+ Item: Record Item;
+ GPPopulateCombinedTables: Codeunit "GP Populate Combined Tables";
+ IsDiscontinued: Boolean;
+ IsInactive: Boolean;
+ IsInventoryOrDiscontinued: Boolean;
+ QuantityFailureShouldBeWarning: Boolean;
+ ShouldInclude: Boolean;
+ ClassName: Code[20];
+ ItemType: Enum "Item Type";
+ CostingMethod: Enum "Costing Method";
+ ItemNo: Code[20];
+ Quantity: Decimal;
+ KitItemNo: Code[20];
+ Kits: List of [Code[20]];
+ EntityType: Text[50];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepItemLbl) then
+ exit;
+
+ EntityType := ItemEntityCaptionLbl;
+ QuantityFailureShouldBeWarning := (TotalUnpostedItemBatchCount > 0);
+
+ if GPCompanyAdditionalSettings.GetInventoryModuleEnabled() then begin
+ if GPCompanyAdditionalSettings.GetMigrateKitItems() then
+ if GPIV00104.FindSet() then
+ repeat
+ KitItemNo := CopyStr(GPIV00104.CMPTITNM.TrimEnd(), 1, MaxStrLen(KitItemNo));
+ if not Kits.Contains(KitItemNo) then
+ Kits.Add(KitItemNo);
+ until GPIV00104.Next() = 0;
+
+ GPIV00101.SetFilter(ITEMNMBR, '<>%1', '');
+ if not GPCompanyAdditionalSettings.GetMigrateKitItems() then
+ GPIV00101.SetFilter(ITEMTYPE, '<>%1', 3);
+
+ if GPIV00101.FindSet() then
+ repeat
+ ItemNo := CopyStr(GPIV00101.ITEMNMBR.TrimEnd(), 1, MaxStrLen(ItemNo));
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, ItemNo);
+
+ Quantity := 0;
+ ClassName := '';
+ ShouldInclude := true;
+ IsInventoryOrDiscontinued := (GPIV00101.ITEMTYPE < 4);
+ IsInactive := (GPIV00101.ITEMTYPE = 2) or (GPIV00101.INACTIVE);
+ IsDiscontinued := GPIV00101.ITEMTYPE = 2;
+
+ if not GPCompanyAdditionalSettings.GetMigrateInactiveItems() then
+ if IsInactive then
+ ShouldInclude := false;
+
+ if ShouldInclude then
+ if not GPCompanyAdditionalSettings.GetMigrateDiscontinuedItems() then
+ if IsDiscontinued then
+ ShouldInclude := false;
+
+ if ShouldInclude then begin
+ if IsInventoryOrDiscontinued then
+ if GPCompanyAdditionalSettings.GetMigrateItemClasses() then begin
+ ClassName := CopyStr(GPIV00101.ITMCLSCD.TrimEnd(), 1, MaxStrLen(ClassName));
+
+ if ClassName = '' then
+ ClassName := DefaultClassNameTok;
+ end else
+ ClassName := DefaultClassNameTok;
+
+ if not GPCompanyAdditionalSettings.GetSkipPostingItemBatches() then
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyInventoryMaster() then begin
+ GPIV10200.SetRange(ITEMNMBR, GPIV00101.ITEMNMBR);
+ GPIV10200.SetRange(RCPTSOLD, false);
+ GPIV10200.SetRange(QTYTYPE, 1);
+ if GPIV10200.FindSet() then
+ repeat
+ // Serial
+ if GPIV00101.ITMTRKOP = 2 then begin
+ GPIV00200.SetRange(ITEMNMBR, GPIV10200.ITEMNMBR);
+ GPIV00200.SetRange(LOCNCODE, GPIV10200.TRXLOCTN);
+ GPIV00200.SetRange(DATERECD, GPIV10200.DATERECD);
+ GPIV00200.SetRange(RCTSEQNM, GPIV10200.RCTSEQNM);
+ GPIV00200.SetRange(QTYTYPE, 1);
+ Quantity := Quantity + GPIV00200.Count();
+ end;
+
+ // Lot
+ if GPIV00101.ITMTRKOP = 3 then begin
+ GPIV00300.SetRange(ITEMNMBR, GPIV00101.ITEMNMBR);
+ GPIV00300.SetRange(LOCNCODE, GPIV10200.TRXLOCTN);
+ GPIV00300.SetRange(DATERECD, GPIV10200.DATERECD);
+ GPIV00300.SetRange(RCTSEQNM, GPIV10200.RCTSEQNM);
+ GPIV00300.SetRange(QTYTYPE, 1);
+ if GPIV00300.FindSet() then
+ repeat
+ Quantity := Quantity + (GPIV00300.QTYRECVD - GPIV00300.QTYSOLD);
+ until GPIV00300.Next() = 0;
+ end;
+
+ if (GPIV00101.ITMTRKOP <> 2) and (GPIV00101.ITMTRKOP <> 3) then
+ Quantity := Quantity + (GPIV10200.QTYRECVD - GPIV10200.QTYSOLD);
+ until GPIV10200.Next() = 0;
+ end;
+
+ Clear(GPItem);
+ GPPopulateCombinedTables.SetGPItemFields(GPItem, GPIV00101);
+
+ if not MigrationValidation.ValidateRecordExists(Test_ITEMEXISTS_Tok, Item.Get(ItemNo), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ Item.CalcFields(Inventory);
+
+ MigrationValidation.ValidateAreEqual(Test_ITEMDESC_Tok, GPItem.Description, Item.Description, DescriptionLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMDESC2_Tok, GPItem.ShortName, Item."Description 2", Description2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMSEARCHDESC_Tok, GPItem.SearchDescription, Item."Search Description", SearchDescription2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMPOSTINGGROUP_Tok, ClassName, Item."Inventory Posting Group", ItemPostingGroupLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMUNITLISTPRICE_Tok, GPItem.UnitListPrice, Item."Unit List Price", UnitListPriceLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMBASEUOFM_Tok, GPItem.BaseUnitOfMeasure, Item."Base Unit of Measure", BaseUofMLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMPURCHUOFM_Tok, GPItem.PurchUnitOfMeasure, Item."Purch. Unit of Measure", PurchUofMLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMTRACKINGCODE_Tok, GPItem.ItemTrackingCode, Item."Item Tracking Code", ItemTrackingCodeLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ITEMINVENTORY_Tok, Quantity, Item.Inventory, QuantityLbl, QuantityFailureShouldBeWarning);
+
+ if (GPItem.ItemType in [0, 2]) then
+ ItemType := ItemType::Inventory
+ else
+ if not Kits.Contains(ItemNo) then
+ ItemType := ItemType::Service
+ else
+ ItemType := ItemType::"Non-Inventory";
+
+ if ItemType = ItemType::Service then
+ CostingMethod := CostingMethod::FIFO
+ else
+ case GPItem.CostingMethod of
+ '0':
+ CostingMethod := CostingMethod::FIFO;
+ '1':
+ CostingMethod := CostingMethod::LIFO;
+ '2':
+ CostingMethod := CostingMethod::Specific;
+ '3':
+ CostingMethod := CostingMethod::Average;
+ '4':
+ CostingMethod := CostingMethod::Standard;
+ end;
+
+ MigrationValidation.ValidateAreEqual(Test_ITEMTYPE_Tok, ItemType, Item.Type, TypeLbl);
+ MigrationValidation.ValidateAreEqual(Test_ITEMCOSTMETHOD_Tok, CostingMethod, Item."Costing Method", CostingMethodLbl, true);
+ end;
+ until GPIV00101.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepItemLbl);
+ Commit();
+ end;
+
+ local procedure RunPurchaseOrderMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GPPOP10100: Record "GP POP10100";
+ PurchaseHeader: Record "Purchase Header";
+ PurchaseLine: Record "Purchase Line";
+ Vendor: Record Vendor;
+ GPPOHeaderValidationBuffer: Record "Migration Validation Buffer";
+ GPPOLineValidationBuffer: Record "Migration Validation Buffer";
+ PONumber: Code[20];
+ EntityType: Text[50];
+ LineEntityType: Text[50];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepPurchaseOrderLbl) then
+ exit;
+
+ EntityType := 'Purchase Order';
+ LineEntityType := 'Purchase Line';
+
+ // GP
+ if GPCompanyAdditionalSettings.GetMigrateOpenPOs() then begin
+ GPPOP10100.SetRange(POTYPE, GPPOP10100.POTYPE::Standard);
+ GPPOP10100.SetRange(POSTATUS, 1, 4);
+ GPPOP10100.SetFilter(VENDORID, '<>%1', '');
+ if GPPOP10100.FindSet() then
+ repeat
+ PONumber := CopyStr(GPPOP10100.PONUMBER.TrimEnd(), 1, MaxStrLen(PurchaseHeader."No."));
+ if Vendor.Get(GPPOP10100.VENDORID) then
+ if not GPPOHeaderValidationBuffer.Get(PONumber) then begin
+ GPPOHeaderValidationBuffer."No." := PONumber;
+ GPPOHeaderValidationBuffer."Text 1" := CopyStr(GPPOP10100.VENDORID.TrimEnd(), 1, MaxStrLen(Vendor."No."));
+ GPPOHeaderValidationBuffer."Date 1" := GPPOP10100.DOCDATE;
+ GPPOHeaderValidationBuffer.Insert();
+
+ if not PopulatePOLineBuffer(PONumber, GPPOLineValidationBuffer) then
+ GPPOHeaderValidationBuffer.Delete();
+ end;
+ until GPPOP10100.Next() = 0;
+ end;
+
+ // Validate - Purchase Orders
+ if GPPOHeaderValidationBuffer.FindSet() then
+ repeat
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, GPPOHeaderValidationBuffer."No.");
+
+ if not MigrationValidation.ValidateRecordExists(Test_POEXISTS_Tok, PurchaseHeader.Get("Purchase Document Type"::Order, GPPOHeaderValidationBuffer."No."), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ MigrationValidation.ValidateAreEqual(Test_POBUYFROMVEND_Tok, GPPOHeaderValidationBuffer."Text 1", PurchaseHeader."Buy-from Vendor No.", PurchaseOrderBuyFromVendorNoLbl);
+ MigrationValidation.ValidateAreEqual(Test_POPAYTOVEND_Tok, GPPOHeaderValidationBuffer."Text 1", PurchaseHeader."Pay-to Vendor No.", PurchaseOrderPayToVendorNoLbl);
+ MigrationValidation.ValidateAreEqual(Test_PODOCDATE_Tok, GPPOHeaderValidationBuffer."Date 1", PurchaseHeader."Document Date", DocumentDateLbl);
+
+ // Lines
+ GPPOLineValidationBuffer.Reset();
+ GPPOLineValidationBuffer.SetRange("Parent No.", GPPOHeaderValidationBuffer."No.");
+ if GPPOLineValidationBuffer.FindSet() then
+ repeat
+ MigrationValidation.SetContext(ValidatorCodeLbl, LineEntityType, GPPOLineValidationBuffer."No.");
+
+ PurchaseLine.SetRange("Document Type", "Purchase Document Type"::Order);
+ PurchaseLine.SetRange("Document No.", GPPOHeaderValidationBuffer."No.");
+ PurchaseLine.SetRange("No.", GPPOLineValidationBuffer."Text 1");
+ if not MigrationValidation.ValidateRecordExists(Test_POLINEEXISTS_Tok, PurchaseLine.FindFirst(), StrSubstNo(MissingEntityTok, LineEntityType)) then
+ continue;
+
+ MigrationValidation.ValidateAreEqual(Test_POLINEQTY_Tok, GPPOLineValidationBuffer."Decimal 1", PurchaseLine.Quantity, QuantityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_POLINEQTYRECV_Tok, GPPOLineValidationBuffer."Decimal 2", PurchaseLine."Quantity Received", QuantityRecLbl, true);
+ until GPPOLineValidationBuffer.Next() = 0;
+ until GPPOHeaderValidationBuffer.Next() = 0;
+
+ LogValidationProgress(ValidationStepPurchaseOrderLbl);
+ Commit();
+ end;
+
+ local procedure PopulatePOLineBuffer(PONumber: Code[20]; var LineBuffer: Record "Migration Validation Buffer"): Boolean
+ var
+ GPPOP10110: Record "GP POP10110";
+ GPPOPReceiptApply: Record GPPOPReceiptApply;
+ GPPOPReceiptApplyLineUnitCost: Record GPPOPReceiptApply;
+ Item: Record Item;
+ HasLines: Boolean;
+ ShouldCreateLine: Boolean;
+ LocationCode: Code[10];
+ LastLineUnitCost: Decimal;
+ LineQtyInvoicedByUnitCost: Decimal;
+ LineQtyReceivedByUnitCost: Decimal;
+ LineQuantityRemaining: Decimal;
+ ItemNo: Text;
+ LastLocation: Text[12];
+ begin
+ GPPOP10110.SetRange(PONUMBER, PONumber);
+ if not GPPOP10110.FindSet() then
+ exit;
+
+ repeat
+ LastLocation := '';
+ LastLineUnitCost := 0;
+ ShouldCreateLine := true;
+
+ ItemNo := CopyStr(GPPOP10110.ITEMNMBR.Trim(), 1, MaxStrLen(Item."No."));
+ Item.SetLoadFields(Blocked);
+ if Item.Get(ItemNo) then begin
+ if Item.Blocked then
+ ShouldCreateLine := false
+ end else
+ if GPPOP10110.NONINVEN = 0 then
+ ShouldCreateLine := false;
+
+ if ShouldCreateLine then begin
+ LineQuantityRemaining := GPPOP10110.QTYORDER - GPPOP10110.QTYCANCE;
+ if LineQuantityRemaining > 0 then begin
+ GPPOPReceiptApplyLineUnitCost.SetLoadFields(TRXLOCTN, PCHRPTCT, UOFM);
+ GPPOPReceiptApplyLineUnitCost.SetCurrentKey(TRXLOCTN, PCHRPTCT);
+ GPPOPReceiptApplyLineUnitCost.SetRange(PONUMBER, GPPOP10110.PONUMBER);
+ GPPOPReceiptApplyLineUnitCost.SetRange(POLNENUM, GPPOP10110.ORD);
+ GPPOPReceiptApplyLineUnitCost.SetRange(Status, GPPOPReceiptApplyLineUnitCost.Status::Posted);
+ GPPOPReceiptApplyLineUnitCost.SetFilter(POPTYPE, '1|3');
+ GPPOPReceiptApplyLineUnitCost.SetFilter(QTYSHPPD, '>%1', 0);
+ GPPOPReceiptApplyLineUnitCost.SetFilter(PCHRPTCT, '>%1', 0);
+
+ if GPPOPReceiptApplyLineUnitCost.FindSet() then
+ repeat
+ if ((LastLocation <> GPPOPReceiptApplyLineUnitCost.TRXLOCTN) or (LastLineUnitCost <> GPPOPReceiptApplyLineUnitCost.PCHRPTCT)) then begin
+ LocationCode := CopyStr(GPPOPReceiptApplyLineUnitCost.TRXLOCTN, 1, MaxStrLen(LocationCode));
+ LineQtyReceivedByUnitCost := GPPOPReceiptApply.GetSumQtyShippedByUnitCost(GPPOP10110.PONUMBER, GPPOP10110.ORD, LocationCode, GPPOPReceiptApplyLineUnitCost.PCHRPTCT);
+ LineQtyInvoicedByUnitCost := GPPOPReceiptApply.GetSumQtyInvoicedByUnitCost(GPPOP10110.PONUMBER, GPPOP10110.ORD, LocationCode, GPPOPReceiptApplyLineUnitCost.PCHRPTCT);
+
+ if (LineQtyReceivedByUnitCost > LineQtyInvoicedByUnitCost) then
+ InsertPOLine(PONumber, GPPOP10110, LineQuantityRemaining, LineQtyReceivedByUnitCost, LineQtyInvoicedByUnitCost, HasLines, LineBuffer)
+ else
+ LineQuantityRemaining := LineQuantityRemaining - LineQtyReceivedByUnitCost;
+
+ LastLocation := GPPOPReceiptApplyLineUnitCost.TRXLOCTN;
+ LastLineUnitCost := GPPOPReceiptApplyLineUnitCost.PCHRPTCT;
+ end;
+ until GPPOPReceiptApplyLineUnitCost.Next() = 0;
+
+ if LineQuantityRemaining > 0 then
+ InsertPOLine(PONumber, GPPOP10110, LineQuantityRemaining, 0, 0, HasLines, LineBuffer);
+ end;
+ end;
+ until GPPOP10110.Next() = 0;
+
+ exit(HasLines);
+ end;
+
+ local procedure InsertPOLine(PONumber: Code[20]; var GPPOP10110: Record "GP POP10110"; var LineQuantityRemaining: Decimal; QuantityReceived: Decimal; QuantityInvoiced: Decimal; var HasLines: Boolean; var LineBuffer: Record "Migration Validation Buffer")
+ var
+ ItemNo: Code[20];
+ AdjustedQuantity: Decimal;
+ AdjustedQuantityReceived: Decimal;
+ QuantityOverReceipt: Decimal;
+ POLineIdTxt: Text[50];
+ begin
+ AdjustedQuantityReceived := SubtractAndZeroIfNegative(QuantityReceived, QuantityInvoiced);
+ if AdjustedQuantityReceived > 0 then
+ AdjustedQuantity := SubtractAndZeroIfNegative(QuantityReceived, QuantityInvoiced)
+ else
+ AdjustedQuantity := SubtractAndZeroIfNegative(LineQuantityRemaining, QuantityInvoiced);
+
+ QuantityOverReceipt := SubtractAndZeroIfNegative(AdjustedQuantityReceived, AdjustedQuantity);
+
+ if QuantityOverReceipt > 0 then
+ AdjustedQuantity := AdjustedQuantityReceived;
+
+ if AdjustedQuantity > 0 then begin
+ POLineIdTxt := CopyStr(PONumber + '_' + CopyStr(GPPOP10110.ITEMNMBR.TrimEnd(), 1, MaxStrLen(ItemNo)), 1, MaxStrLen(POLineIdTxt));
+ LineBuffer.SetRange("No.", POLineIdTxt);
+ if LineBuffer.FindFirst() then begin
+ LineBuffer."Decimal 1" := LineBuffer."Decimal 1" + AdjustedQuantity;
+ LineBuffer."Decimal 2" := LineBuffer."Decimal 2" + AdjustedQuantityReceived;
+ LineBuffer.Modify();
+ end else begin
+ LineBuffer."No." := POLineIdTxt;
+ LineBuffer."Parent No." := PONumber;
+ LineBuffer."Text 1" := CopyStr(CopyStr(GPPOP10110.ITEMNMBR.TrimEnd(), 1, MaxStrLen(ItemNo)), 1, MaxStrLen(LineBuffer."Text 1"));
+ LineBuffer."Decimal 1" := AdjustedQuantity;
+ LineBuffer."Decimal 2" := AdjustedQuantityReceived;
+ LineBuffer.Insert();
+ end;
+
+ HasLines := true;
+ end;
+ LineQuantityRemaining := LineQuantityRemaining - QuantityReceived;
+ end;
+
+ local procedure SubtractAndZeroIfNegative(Minuend: Decimal; Subtrahend: Decimal): Decimal
+ var
+ Difference: Decimal;
+ begin
+ Difference := Minuend - Subtrahend;
+
+ if Difference < 0 then
+ Difference := 0;
+
+ exit(Difference);
+ end;
+
+ local procedure RunVendorMigrationValidation(var GPCompanyAdditionalSettings: Record "GP Company Additional Settings")
+ var
+ GPPM00200: Record "GP PM00200";
+ GPPM20000: Record "GP PM20000";
+ GPVendor: Record "GP Vendor";
+ GPPaymentTerms: Record "GP Payment Terms";
+ Vendor: Record Vendor;
+ GPPopulateCombinedTables: Codeunit "GP Populate Combined Tables";
+ HelperFunctions: Codeunit "Helper Functions";
+ BalanceFailureShouldBeWarning: Boolean;
+ IsActive: Boolean;
+ ShouldInclude: Boolean;
+ ClassName: Code[20];
+ VendorNo: Code[20];
+ Balance: Decimal;
+ EntityType: Text[50];
+ VendorName2: Text[50];
+ PaymentTerms: Code[10];
+ TaxLiable: Boolean;
+ PhoneNo: Text[30];
+ FaxNo: Text[30];
+ begin
+ if CompanyValidationProgress.Get(CompanyNameTxt, ValidatorCodeLbl, ValidationStepVendorLbl) then
+ exit;
+
+ EntityType := VendorEntityCaptionLbl;
+ BalanceFailureShouldBeWarning := (TotalUnpostedVendorBatchCount > 0);
+
+ if GPCompanyAdditionalSettings.GetPayablesModuleEnabled() then begin
+ GPPM00200.SetFilter(VENDORID, '<>%1', '');
+ if GPPM00200.FindSet() then
+ repeat
+ VendorNo := CopyStr(GPPM00200.VENDORID.TrimEnd(), 1, MaxStrLen(VendorNo));
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, VendorNo);
+
+ Balance := 0;
+ ShouldInclude := true;
+ IsActive := (GPPM00200.VENDSTTS = 1) or (GPPM00200.VENDSTTS = 3);
+
+ if not GPCompanyAdditionalSettings.GetMigrateInactiveVendors() then
+ if not IsActive then
+ ShouldInclude := false;
+
+ if ShouldInclude then
+ ShouldInclude := ShouldMigrateTemporaryVendor(GPPM00200.VENDORID);
+
+ if ShouldInclude then begin
+ if GPCompanyAdditionalSettings.GetMigrateVendorClasses() then
+ ClassName := CopyStr(GPPM00200.VNDCLSID.TrimEnd(), 1, MaxStrLen(ClassName));
+
+ if ClassName = '' then
+ ClassName := DefaultClassNameTok;
+
+ Clear(GPVendor);
+ GPPopulateCombinedTables.SetGPVendorFields(GPVendor, GPPM00200);
+
+ GPVendor.PHNUMBR1 := HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendor.PHNUMBR1);
+ GPVendor.FAXNUMBR := HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendor.FAXNUMBR);
+
+ if not GPCompanyAdditionalSettings.GetMigrateOnlyPayablesMaster() then
+ if not GPCompanyAdditionalSettings.GetSkipPostingVendorBatches() then begin
+ GPPM20000.SetRange(VENDORID, GPPM00200.VENDORID);
+ GPPM20000.SetFilter(DOCTYPE, '<=7');
+ GPPM20000.SetFilter(CURTRXAM, '>=0.01');
+ GPPM20000.SetRange(VOIDED, false);
+ if GPPM20000.FindSet() then
+ repeat
+ if GPPM20000.DOCTYPE < 4 then
+ Balance := Balance + RoundWithSpecPrecision(GPPM20000.CURTRXAM)
+ else
+ Balance := Balance + RoundWithSpecPrecision(GPPM20000.CURTRXAM * -1);
+ until GPPM20000.Next() = 0;
+ end;
+
+ if not MigrationValidation.ValidateRecordExists(Test_VENDOREXISTS_Tok, Vendor.Get(VendorNo), StrSubstNo(MissingEntityTok, EntityType)) then
+ continue;
+
+ Vendor.CalcFields(Balance);
+
+ MigrationValidation.ValidateAreEqual(Test_VENDORNAME_Tok, CopyStr(GPVendor.VENDNAME.TrimEnd(), 1, MaxStrLen(Vendor.Name)), Vendor.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORPOSTINGGROUP_Tok, ClassName, Vendor."Vendor Posting Group", VendorPostingGroupLbl);
+ MigrationValidation.ValidateAreEqual(Test_VENDORPREFBANKACCT_Tok, GetPreferredGPVendorBankCode(VendorNo), Vendor."Preferred Bank Account Code", PreferredBankAccountLbl);
+ MigrationValidation.ValidateAreEqual(Test_VENDORADDR_Tok, CopyStr(GPVendor.ADDRESS1, 1, MaxStrLen(Vendor.Address)), Vendor.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORADDR2_Tok, CopyStr(GPVendor.ADDRESS2, 1, MaxStrLen(Vendor."Address 2")), Vendor."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORCITY_Tok, CopyStr(GPVendor.CITY, 1, MaxStrLen(Vendor.City)), Vendor.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORCONTACT_Tok, CopyStr(GPVendor.VNDCNTCT, 1, MaxStrLen(Vendor.Contact)), Vendor.Contact, ContactLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORSHIPMETHOD_Tok, UpperCase(CopyStr(GPVendor.SHIPMTHD, 1, MaxStrLen(Vendor."Shipment Method Code")).TrimEnd()), Vendor."Shipment Method Code", ShipmentMethodLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORTAXAREA_Tok, UpperCase(GPVendor.TAXSCHID.TrimEnd()), Vendor."Tax Area Code", TaxAreaLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORBALANCE_Tok, Balance, Vendor.Balance, BalanceLbl, BalanceFailureShouldBeWarning);
+
+ TaxLiable := (GPVendor.TAXSCHID <> '');
+ MigrationValidation.ValidateAreEqual(Test_VENDORTAXLIABLE_Tok, TaxLiable, Vendor."Tax Liable", TaxLiableLbl, true);
+
+ VendorName2 := CopyStr(GPVendor.VNDCHKNM.TrimEnd(), 1, MaxStrLen(Vendor."Name 2"));
+ if HelperFunctions.StringEqualsCaseInsensitive(VendorName2, Vendor.Name) then
+ VendorName2 := '';
+
+ MigrationValidation.ValidateAreEqual(Test_VENDORNAME2_Tok, VendorName2, Vendor."Name 2", Name2Lbl, true);
+
+ PhoneNo := '';
+ if GPVendor.PHNUMBR1 <> '' then
+ if not HelperFunctions.ContainsAlphaChars(GPVendor.PHNUMBR1) then
+ PhoneNo := GPVendor.PHNUMBR1;
+
+ FaxNo := '';
+ if GPVendor.FAXNUMBR <> '' then
+ if not HelperFunctions.ContainsAlphaChars(GPVendor.FAXNUMBR) then
+ FaxNo := GPVendor.FAXNUMBR;
+
+ MigrationValidation.ValidateAreEqual(Test_VENDORPHN_Tok, PhoneNo, Vendor."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_VENDORFAX_Tok, FaxNo, Vendor."Fax No.", FaxLbl, true);
+
+ PaymentTerms := '';
+ if GPVendor.PYMTRMID <> '' then
+ if GPPaymentTerms.Get(GPVendor.PYMTRMID) then begin
+ PaymentTerms := CopyStr(GPVendor.PYMTRMID, 1, MaxStrLen(Vendor."Payment Terms Code"));
+ if GPPaymentTerms.PYMTRMID_New <> '' then
+ PaymentTerms := GPPaymentTerms.PYMTRMID_New;
+ end;
+
+ MigrationValidation.ValidateAreEqual(Test_VENDORPMTTERMS_Tok, PaymentTerms, Vendor."Payment Terms Code", PaymentTermsLbl, true);
+
+ ValidateVendorAddresses(GPVendor);
+ end;
+ until GPPM00200.Next() = 0;
+ end;
+
+ LogValidationProgress(ValidationStepVendorLbl);
+ Commit();
+ end;
+
+ local procedure ValidateVendorAddresses(var GPVendor: Record "GP Vendor")
+ var
+ GPPM00200: Record "GP PM00200";
+ GPVendorAddress: Record "GP Vendor Address";
+ Vendor: Record Vendor;
+ RemitAddress: Record "Remit Address";
+ OrderAddress: Record "Order Address";
+ HelperFunctions: Codeunit "Helper Functions";
+ AddressCode: Code[10];
+ AssignedPrimaryAddressCode: Code[10];
+ AssignedRemitToAddressCode: Code[10];
+ EntityType: Text[50];
+ ContextCode: Text[250];
+ begin
+ if not Vendor.Get(GPVendor.VENDORID) then
+ exit;
+
+ if GPPM00200.Get(GPVendor.VENDORID) then begin
+ AssignedPrimaryAddressCode := CopyStr(GPPM00200.VADDCDPR.Trim(), 1, MaxStrLen(AssignedPrimaryAddressCode));
+ AssignedRemitToAddressCode := CopyStr(GPPM00200.VADCDTRO.Trim(), 1, MaxStrLen(AssignedRemitToAddressCode));
+ end;
+
+ GPVendorAddress.SetRange(VENDORID, Vendor."No.");
+ if GPVendorAddress.FindSet() then
+ repeat
+ AddressCode := CopyStr(GPVendorAddress.ADRSCODE.Trim(), 1, MaxStrLen(AddressCode));
+ ContextCode := Vendor."No." + '-' + AddressCode;
+
+ if AddressCode = AssignedRemitToAddressCode then begin
+ EntityType := VendorRemitAddressEntityCaptionLbl;
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, ContextCode);
+
+ if MigrationValidation.ValidateRecordExists(Test_REMITADDREXISTS_Tok, RemitAddress.Get(AddressCode, Vendor."No."), StrSubstNo(MissingEntityTok, EntityType)) then begin
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRNAME_Tok, Vendor.Name, RemitAddress.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRADDR_Tok, GPVendorAddress.ADDRESS1.TrimEnd(), RemitAddress.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRADDR2_Tok, CopyStr(GPVendorAddress.ADDRESS2.TrimEnd(), 1, MaxStrLen(RemitAddress."Address 2")), RemitAddress."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRCITY_Tok, CopyStr(GPVendorAddress.CITY.TrimEnd(), 1, MaxStrLen(RemitAddress.City)), RemitAddress.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRPOSTCODE_Tok, UpperCase(GPVendorAddress.ZIPCODE.TrimEnd()), RemitAddress."Post Code", PostCodeLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRPHN_Tok, HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendorAddress.PHNUMBR1), RemitAddress."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRFAX_Tok, HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendorAddress.FAXNUMBR), RemitAddress."Fax No.", FaxLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRCOUNTY_Tok, GPVendorAddress.STATE.TrimEnd(), RemitAddress.County, CountyLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_REMITADDRCONTACT_Tok, GPVendorAddress.VNDCNTCT.TrimEnd(), RemitAddress.Contact, ContactLbl, true);
+ end;
+ end;
+
+ if (AddressCode = AssignedPrimaryAddressCode) or (AddressCode <> AssignedRemitToAddressCode) then begin
+ EntityType := VendorOrderAddressEntityCaptionLbl;
+ MigrationValidation.SetContext(ValidatorCodeLbl, EntityType, ContextCode);
+
+ if MigrationValidation.ValidateRecordExists(Test_ORDERADDREXISTS_Tok, OrderAddress.Get(Vendor."No.", AddressCode), StrSubstNo(MissingEntityTok, EntityType)) then begin
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRNAME_Tok, Vendor.Name, OrderAddress.Name, NameLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRADDR_Tok, GPVendorAddress.ADDRESS1.TrimEnd(), OrderAddress.Address, AddressLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRADDR2_Tok, CopyStr(GPVendorAddress.ADDRESS2.TrimEnd(), 1, MaxStrLen(OrderAddress."Address 2")), OrderAddress."Address 2", Address2Lbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRCITY_Tok, CopyStr(GPVendorAddress.CITY.TrimEnd(), 1, MaxStrLen(OrderAddress.City)), OrderAddress.City, CityLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRPOSTCODE_Tok, UpperCase(GPVendorAddress.ZIPCODE.TrimEnd()), OrderAddress."Post Code", PostCodeLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRPHN_Tok, HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendorAddress.PHNUMBR1), OrderAddress."Phone No.", PhoneLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRFAX_Tok, HelperFunctions.CleanGPPhoneOrFaxNumber(GPVendorAddress.FAXNUMBR), OrderAddress."Fax No.", FaxLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRCOUNTY_Tok, GPVendorAddress.STATE.TrimEnd(), OrderAddress.County, CountyLbl, true);
+ MigrationValidation.ValidateAreEqual(Test_ORDERADDRCONTACT_Tok, GPVendorAddress.VNDCNTCT.TrimEnd(), OrderAddress.Contact, ContactLbl, true);
+ end;
+ end
+ until GPVendorAddress.Next() = 0;
+ end;
+
+ local procedure ShouldMigrateTemporaryVendor(VendorNo: Text[75]): Boolean
+ var
+ GPCompanyAdditionalSettings: Record "GP Company Additional Settings";
+ GPPM00200: Record "GP PM00200";
+ GPPOP10100: Record "GP POP10100";
+ GPVendorTransactions: Record "GP Vendor Transactions";
+ HasOpenPurchaseOrders: Boolean;
+ HasOpenTransactions: Boolean;
+ IsTemporaryVendor: Boolean;
+ begin
+ if GPCompanyAdditionalSettings.GetMigrateTemporaryVendors() then
+ exit(true);
+
+ GPPM00200.SetLoadFields(VENDSTTS);
+ if GPPM00200.Get(VendorNo) then
+ IsTemporaryVendor := GPPM00200.VENDSTTS = 3;
+
+ if not IsTemporaryVendor then
+ exit(true);
+
+ // Check for open POs
+ GPPOP10100.SetRange(POTYPE, GPPOP10100.POTYPE::Standard);
+ GPPOP10100.SetRange(POSTATUS, 1, 4);
+ GPPOP10100.SetRange(VENDORID, VendorNo);
+ HasOpenPurchaseOrders := GPPOP10100.Count() > 0;
+
+ // Check for open AP transactions
+ GPVendorTransactions.SetRange(VENDORID, VendorNo);
+ HasOpenTransactions := GPVendorTransactions.Count() > 0;
+
+ if not HasOpenPurchaseOrders then
+ if not HasOpenTransactions then
+ exit(false);
+
+ exit(true);
+ end;
+
+ local procedure GetPreferredGPVendorBankCode(VendorNo: Code[20]): Code[20]
+ var
+ GPPM00200: Record "GP PM00200";
+ GPSY06000: Record "GP SY06000";
+ SearchGPSY06000: Record "GP SY06000";
+ AddressCode: Code[10];
+ PrimaryAddressCode: Code[10];
+ RemitToAddressCode: Code[10];
+ GeneratedBankCode: Code[20];
+ PreferredBankCode: Code[20];
+ BankAccountCounter: Integer;
+ MaxSupportedVendorNoLength: Integer;
+ NumberOfAccounts: Integer;
+ begin
+ PreferredBankCode := '';
+ BankAccountCounter := 0;
+
+ GPPM00200.SetLoadFields(VADDCDPR, VADCDTRO);
+ if not GPPM00200.Get(VendorNo) then
+ exit;
+
+ GPSY06000.SetLoadFields(CustomerVendor_ID);
+ GPSY06000.SetRange(CustomerVendor_ID, VendorNo);
+ GPSY06000.SetRange(INACTIVE, false);
+ NumberOfAccounts := GPSY06000.Count();
+
+ if NumberOfAccounts = 0 then
+ exit;
+
+ // Single bank account, then return the VendorNo as the bank code
+ if NumberOfAccounts = 1 then
+ exit(VendorNo);
+
+ // Multiple bank accounts, duplicate migration logic for multiple account handling
+ PrimaryAddressCode := CopyStr(GPPM00200.VADDCDPR.Trim(), 1, MaxStrLen(PrimaryAddressCode));
+ RemitToAddressCode := CopyStr(GPPM00200.VADCDTRO.Trim(), 1, MaxStrLen(RemitToAddressCode));
+ MaxSupportedVendorNoLength := MaxStrLen(GeneratedBankCode) - StrLen(Format(NumberOfAccounts)) - 1;
+
+ if StrLen(VendorNo) > MaxSupportedVendorNoLength then
+#pragma warning disable AA0139
+ VendorNo := CopyStr(VendorNo, 1, MaxSupportedVendorNoLength);
+#pragma warning restore AA0139
+
+ GPSY06000.SetLoadFields(ADRSCODE);
+ if GPSY06000.FindSet() then
+ repeat
+ BankAccountCounter := BankAccountCounter + 1;
+ GeneratedBankCode := CopyStr(VendorNo + '-' + Format(BankAccountCounter), 1, MaxStrLen(GeneratedBankCode));
+ AddressCode := CopyStr(GPSY06000.ADRSCODE.TrimEnd(), 1, MaxStrLen(AddressCode));
+
+ if AddressCode = RemitToAddressCode then
+ PreferredBankCode := GeneratedBankCode
+ else
+ if AddressCode = PrimaryAddressCode then begin
+ SearchGPSY06000.SetRange("CustomerVendor_ID", VendorNo);
+ SearchGPSY06000.SetRange("ADRSCODE", RemitToAddressCode);
+ SearchGPSY06000.SetRange("INACTIVE", false);
+ if SearchGPSY06000.IsEmpty() then
+ PreferredBankCode := GeneratedBankCode;
+ end;
+ until GPSY06000.Next() = 0;
+
+ exit(PreferredBankCode);
+ end;
+
+ local procedure GetAccountFilter(AccountNo: Text[50]; ACCTYPE: Integer): Text
+ var
+ GPGL00100: Record "GP GL00100";
+ FilterText: Text;
+ begin
+ GPGL00100.SetLoadFields(ACTINDX);
+ GPGL00100.SetRange(ACCTTYPE, ACCTYPE);
+ GPGL00100.SetRange(MNACSGMT, AccountNo);
+ GPGL00100.SetRange(PSTNGTYP, 0);
+ GPGL00100.SetRange(Clear_Balance, false);
+ if GPGL00100.FindSet() then
+ repeat
+ if FilterText <> '' then
+ FilterText += '|' + Format(GPGL00100.ACTINDX)
+ else
+ FilterText := Format(GPGL00100.ACTINDX);
+ until GPGL00100.Next() = 0;
+
+ exit(FilterText);
+ end;
+
+ internal procedure RoundWithSpecPrecision(Amount: Decimal): Decimal
+ begin
+ exit(Round(Amount, DefaultCurrency."Amount Rounding Precision"));
+ end;
+
+ local procedure LogValidationProgress(ValidationStep: Code[20])
+ begin
+ Clear(CompanyValidationProgress);
+ CompanyValidationProgress.Validate("Company Name", CompanyNameTxt);
+ CompanyValidationProgress.Validate("Validator Code", ValidatorCodeLbl);
+ CompanyValidationProgress.Validate("Validation Step", ValidationStep);
+ CompanyValidationProgress.Insert(true);
+ end;
+
+ internal procedure GetValidatorCode(): Code[20];
+ begin
+ exit('GP');
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Hybrid Cloud Management", OnPrepareMigrationValidation, '', false, false)]
+ local procedure OnPrepareMigrationValidation(ProductID: Text[250])
+ var
+ HybridGPWizard: Codeunit "Hybrid GP Wizard";
+ begin
+ if ProductID <> HybridGPWizard.ProductId() then
+ exit;
+
+ RegisterValidator();
+
+ AddTest(Test_ACCOUNTEXISTS_Tok, 'G/L Account', 'Missing Account');
+ AddTest(Test_ACCOUNTNAME_Tok, 'G/L Account', 'Name');
+ AddTest(Test_ACCOUNTTYPE_Tok, 'G/L Account', 'Account Type');
+ AddTest(Test_ACCOUNTCATEGORY_Tok, 'G/L Account', 'Account Category');
+ AddTest(Test_ACCOUNTDEBCRED_Tok, 'G/L Account', 'Debit/Credit');
+ AddTest(Test_ACCOUNTSUBCATEGORY_Tok, 'G/L Account', 'Account Subcategory');
+ AddTest(Test_ACCOUNTINCBAL_Tok, 'G/L Account', 'Income/Balance');
+ AddTest(Test_ACCOUNTBALANCE_Tok, 'G/L Account', 'Balance');
+ AddTest(Test_STATACCOUNTEXISTS_Tok, 'Statistical Account', 'Missing Account');
+ AddTest(Test_STATACCOUNTNAME_Tok, 'Statistical Account', 'Name');
+ AddTest(Test_STATACCOUNTDIM1_Tok, 'Statistical Account', 'Dimension 1');
+ AddTest(Test_STATACCOUNTDIM2_Tok, 'Statistical Account', 'Dimension 2');
+ AddTest(Test_STATACCOUNTBALANCE_Tok, 'Statistical Account', 'Balance');
+ AddTest(Test_BANKACCOUNTEXISTS_Tok, 'Bank Account', 'Missing Bank Account');
+ AddTest(Test_BANKACCOUNTNAME_Tok, 'Bank Account', 'Name');
+ AddTest(Test_BANKACCOUNTNO_Tok, 'Bank Account', 'Bank Account No.');
+ AddTest(Test_BANKACCOUNTADDR_Tok, 'Bank Account', 'Address');
+ AddTest(Test_BANKACCOUNTADDR2_Tok, 'Bank Account', 'Address 2');
+ AddTest(Test_BANKACCOUNTCITY_Tok, 'Bank Account', 'City');
+ AddTest(Test_BANKACCOUNTCOUNTY_Tok, 'Bank Account', 'County (State)');
+ AddTest(Test_BANKACCOUNTPOSTCODE_Tok, 'Bank Account', 'Post Code');
+ AddTest(Test_BANKACCOUNTPHN_Tok, 'Bank Account', 'Phone');
+ AddTest(Test_BANKACCOUNTFAX_Tok, 'Bank Account', 'Fax');
+ AddTest(Test_BANKACCOUNTTRANSITNO_Tok, 'Bank Account', 'Transit No.');
+ AddTest(Test_BANKACCOUNTBRANCHNO_Tok, 'Bank Account', 'Bank Branch No.');
+ AddTest(Test_BANKACCOUNTBALANCE_Tok, 'Bank Account', 'Balance');
+ AddTest(Test_CUSTOMEREXISTS_Tok, 'Customer', 'Missing Customer');
+ AddTest(Test_CUSTOMERNAME_Tok, 'Customer', 'Name');
+ AddTest(Test_CUSTOMERPOSTINGGROUP_Tok, 'Customer', 'Customer Posting Group');
+ AddTest(Test_CUSTOMERADDR_Tok, 'Customer', 'Address');
+ AddTest(Test_CUSTOMERADDR2_Tok, 'Customer', 'Address 2');
+ AddTest(Test_CUSTOMERCITY_Tok, 'Customer', 'City');
+ AddTest(Test_CUSTOMERPHN_Tok, 'Customer', 'Phone');
+ AddTest(Test_CUSTOMERFAX_Tok, 'Customer', 'Fax');
+ AddTest(Test_CUSTOMERNAME2_Tok, 'Customer', 'Name 2');
+ AddTest(Test_CUSTOMERCREDITLMT_Tok, 'Customer', 'Credit Limit');
+ AddTest(Test_CUSTOMERCONTACT_Tok, 'Customer', 'Contact');
+ AddTest(Test_CUSTOMERSALESPERSON_Tok, 'Customer', 'Sales Person');
+ AddTest(Test_CUSTOMERSHIPMETHOD_Tok, 'Customer', 'Shipment Method');
+ AddTest(Test_CUSTOMERPMTTERMS_Tok, 'Customer', 'Payment Terms');
+ AddTest(Test_CUSTOMERTERRITORY_Tok, 'Customer', 'Territory');
+ AddTest(Test_CUSTOMERTAXAREA_Tok, 'Customer', 'Tax Area');
+ AddTest(Test_CUSTOMERTAXLIABLE_Tok, 'Customer', 'Tax Liable');
+ AddTest(Test_CUSTOMERBALANCE_Tok, 'Customer', 'Balance');
+ AddTest(Test_SHIPADDREXISTS_Tok, 'Customer - Ship-to Address', 'Missing address');
+ AddTest(Test_SHIPADDRNAME_Tok, 'Customer - Ship-to Address', 'Name');
+ AddTest(Test_SHIPADDRADDR_Tok, 'Customer - Ship-to Address', 'Address');
+ AddTest(Test_SHIPADDRADDR2_Tok, 'Customer - Ship-to Address', 'Address 2');
+ AddTest(Test_SHIPADDRCITY_Tok, 'Customer - Ship-to Address', 'City');
+ AddTest(Test_SHIPADDRPOSTCODE_Tok, 'Customer - Ship-to Address', 'Post Code');
+ AddTest(Test_SHIPADDRPHN_Tok, 'Customer - Ship-to Address', 'Phone');
+ AddTest(Test_SHIPADDRFAX_Tok, 'Customer - Ship-to Address', 'Fax');
+ AddTest(Test_SHIPADDRCONTACT_Tok, 'Customer - Ship-to Address', 'Contact');
+ AddTest(Test_SHIPADDRSHIPMETHOD_Tok, 'Customer - Ship-to Address', 'Shipment Method');
+ AddTest(Test_SHIPADDRCOUNTY_Tok, 'Customer - Ship-to Address', 'County (State)');
+ AddTest(Test_SHIPADDRTAXAREA_Tok, 'Customer - Ship-to Address', 'Tax Area');
+ AddTest(Test_ITEMEXISTS_Tok, 'Item', 'Missing Item');
+ AddTest(Test_ITEMTYPE_Tok, 'Item', 'Type');
+ AddTest(Test_ITEMDESC_Tok, 'Item', 'Description');
+ AddTest(Test_ITEMDESC2_Tok, 'Item', 'Description 2');
+ AddTest(Test_ITEMSEARCHDESC_Tok, 'Item', 'Search Description');
+ AddTest(Test_ITEMPOSTINGGROUP_Tok, 'Item', 'Inventory Posting Group');
+ AddTest(Test_ITEMUNITLISTPRICE_Tok, 'Item', 'Unit List Price');
+ AddTest(Test_ITEMCOSTMETHOD_Tok, 'Item', 'Costing Method');
+ AddTest(Test_ITEMBASEUOFM_Tok, 'Item', 'Base Unit of Measure');
+ AddTest(Test_ITEMPURCHUOFM_Tok, 'Item', 'Purch. Unit of Measure');
+ AddTest(Test_ITEMTRACKINGCODE_Tok, 'Item', 'Item Tracking Code');
+ AddTest(Test_ITEMINVENTORY_Tok, 'Item', 'Inventory');
+ AddTest(Test_POEXISTS_Tok, 'Purchase Order', 'Missing Purchase Order');
+ AddTest(Test_POBUYFROMVEND_Tok, 'Purchase Order', 'Buy-from Vendor No.');
+ AddTest(Test_POPAYTOVEND_Tok, 'Purchase Order', 'Pay-to Vendor No.');
+ AddTest(Test_PODOCDATE_Tok, 'Purchase Order', 'Document Date');
+ AddTest(Test_POLINEEXISTS_Tok, 'Purchase Order - Line', 'Missing PO Line');
+ AddTest(Test_POLINEQTY_Tok, 'Purchase Order - Line', 'Quantity');
+ AddTest(Test_POLINEQTYRECV_Tok, 'Purchase Order - Line', 'Quantity Received');
+ AddTest(Test_VENDOREXISTS_Tok, 'Vendor', 'Missing Vendor');
+ AddTest(Test_VENDORNAME_Tok, 'Vendor', 'Name');
+ AddTest(Test_VENDORNAME2_Tok, 'Vendor', 'Name 2');
+ AddTest(Test_VENDORPOSTINGGROUP_Tok, 'Vendor', 'Vendor Posting Group');
+ AddTest(Test_VENDORPREFBANKACCT_Tok, 'Vendor', 'Preferred Bank Account');
+ AddTest(Test_VENDORADDR_Tok, 'Vendor', 'Address');
+ AddTest(Test_VENDORADDR2_Tok, 'Vendor', 'Address 2');
+ AddTest(Test_VENDORCITY_Tok, 'Vendor', 'City');
+ AddTest(Test_VENDORPHN_Tok, 'Vendor', 'Phone');
+ AddTest(Test_VENDORFAX_Tok, 'Vendor', 'Fax');
+ AddTest(Test_VENDORCONTACT_Tok, 'Vendor', 'Contact');
+ AddTest(Test_VENDORSHIPMETHOD_Tok, 'Vendor', 'Shipment Method');
+ AddTest(Test_VENDORPMTTERMS_Tok, 'Vendor', 'Payment Terms');
+ AddTest(Test_VENDORTAXAREA_Tok, 'Vendor', 'Tax Area');
+ AddTest(Test_VENDORTAXLIABLE_Tok, 'Vendor', 'Tax Liable');
+ AddTest(Test_VENDORBALANCE_Tok, 'Vendor', 'Balance');
+ AddTest(Test_ORDERADDREXISTS_Tok, 'Vendor - Order Address', 'Missing address');
+ AddTest(Test_ORDERADDRNAME_Tok, 'Vendor - Order Address', 'Name');
+ AddTest(Test_ORDERADDRADDR_Tok, 'Vendor - Order Address', 'Address');
+ AddTest(Test_ORDERADDRADDR2_Tok, 'Vendor - Order Address', 'Address 2');
+ AddTest(Test_ORDERADDRCITY_Tok, 'Vendor - Order Address', 'City');
+ AddTest(Test_ORDERADDRPOSTCODE_Tok, 'Vendor - Order Address', 'Post Code');
+ AddTest(Test_ORDERADDRPHN_Tok, 'Vendor - Order Address', 'Phone');
+ AddTest(Test_ORDERADDRFAX_Tok, 'Vendor - Order Address', 'Fax');
+ AddTest(Test_ORDERADDRCOUNTY_Tok, 'Vendor - Order Address', 'County (State)');
+ AddTest(Test_ORDERADDRCONTACT_Tok, 'Vendor - Order Address', 'Contact');
+ AddTest(Test_REMITADDREXISTS_Tok, 'Vendor - Remit Address', 'Missing address');
+ AddTest(Test_REMITADDRNAME_Tok, 'Vendor - Remit Address', 'Name');
+ AddTest(Test_REMITADDRADDR_Tok, 'Vendor - Remit Address', 'Address');
+ AddTest(Test_REMITADDRADDR2_Tok, 'Vendor - Remit Address', 'Address 2');
+ AddTest(Test_REMITADDRCITY_Tok, 'Vendor - Remit Address', 'City');
+ AddTest(Test_REMITADDRPOSTCODE_Tok, 'Vendor - Remit Address', 'Post Code');
+ AddTest(Test_REMITADDRPHN_Tok, 'Vendor - Remit Address', 'Phone');
+ AddTest(Test_REMITADDRFAX_Tok, 'Vendor - Remit Address', 'Fax');
+ AddTest(Test_REMITADDRCOUNTY_Tok, 'Vendor - Remit Address', 'County (State)');
+ AddTest(Test_REMITADDRCONTACT_Tok, 'Vendor - Remit Address', 'Contact');
+ end;
+
+ local procedure RegisterValidator()
+ var
+ MigrationValidatorRegistry: Record "Migration Validator Registry";
+ GPMigrationValidator: Codeunit "GP Migration Validator";
+ HybridGPWizard: Codeunit "Hybrid GP Wizard";
+ ValidatorCode: Code[20];
+ MigrationType: Text[250];
+ ValidatorCodeunitId: Integer;
+ begin
+ ValidatorCode := GPMigrationValidator.GetValidatorCode();
+ MigrationType := HybridGPWizard.ProductId();
+ ValidatorCodeunitId := Codeunit::"GP Migration Validator";
+ if not MigrationValidatorRegistry.Get(ValidatorCode) then begin
+ MigrationValidatorRegistry.Validate("Validator Code", ValidatorCode);
+ MigrationValidatorRegistry.Validate("Migration Type", MigrationType);
+ MigrationValidatorRegistry.Validate(Description, ValidatorDescriptionLbl);
+ MigrationValidatorRegistry.Validate("Codeunit Id", ValidatorCodeunitId);
+ MigrationValidatorRegistry.Validate(Automatic, false);
+ MigrationValidatorRegistry.Insert(true);
+ end;
+ end;
+
+ local procedure AddTest(Code: Code[30]; Entity: Text[50]; Description: Text)
+ var
+ MigrationValidationTest: Record "Migration Validation Test";
+ GPMigrationValidator: Codeunit "GP Migration Validator";
+ begin
+ if not MigrationValidationTest.Get(Code, GPMigrationValidator.GetValidatorCode()) then begin
+ MigrationValidationTest.Validate(Code, Code);
+ MigrationValidationTest.Validate("Validator Code", GPMigrationValidator.GetValidatorCode());
+ MigrationValidationTest.Validate(Entity, Entity);
+ MigrationValidationTest.Validate("Test Description", Description);
+ MigrationValidationTest.Insert(true);
+ end;
+ end;
+
+ var
+ DefaultCurrency: Record Currency;
+ CompanyValidationProgress: Record "Company Validation Progress";
+ MigrationValidation: Codeunit "Migration Validation";
+ ValidatorCodeLbl: Code[20];
+ CompanyNameTxt: Text;
+ TotalUnpostedBankBatchCount: Integer;
+ TotalUnpostedCustomerBatchCount: Integer;
+ TotalUnpostedGLBatchCount: Integer;
+ TotalUnpostedItemBatchCount: Integer;
+ TotalUnpostedStatisticalBatchCount: Integer;
+ TotalUnpostedVendorBatchCount: Integer;
+ AccountCategoryLbl: Label 'Account Category';
+ AccountDebitCreditLbl: Label 'Debit/Credit';
+ AccountIncomeBalanceLbl: Label 'Income/Balance';
+ AccountNameLbl: Label 'Name';
+ AccountSubcategoryLbl: Label 'Account Subcategory';
+ AccountTypeLbl: Label 'Account Type';
+ Address2Lbl: Label 'Address 2';
+ AddressLbl: Label 'Address';
+ BalanceLbl: Label 'Balance';
+ BankAccountEntityCaptionLbl: Label 'Bank Account', MaxLength = 50;
+ BankAccountNumberLbl: Label 'Account Number';
+ BankBranchNoLbl: Label 'Bank Branch No.';
+ BaseUofMLbl: Label 'Base UofM';
+ BeginningBalanceLbl: Label 'Beginning Balance';
+ CityLbl: Label 'City';
+ ContactLbl: Label 'Contact';
+ CostingMethodLbl: Label 'Costing Method';
+ CountyLbl: Label 'County';
+ CreditLimitLbl: Label 'Credit Limit';
+ CustomerAddressEntityCaptionLbl: Label 'Customer Ship-to Address', MaxLength = 50;
+ CustomerEntityCaptionLbl: Label 'Customer', MaxLength = 50;
+ CustomerPostingGroupLbl: Label 'Customer Posting Group';
+ DefaultClassNameTok: Label 'GP', MaxLength = 20, Locked = true;
+ Description2Lbl: Label 'Description 2';
+ DescriptionLbl: Label 'Description';
+ Dimension1Lbl: Label 'Dimension 1';
+ Dimension2Lbl: Label 'Dimension 2';
+ DocumentDateLbl: Label 'Document Date';
+ FaxLbl: Label 'Fax';
+ GlAccountEntityCaptionLbl: Label 'G/L Account', MaxLength = 50;
+ ItemEntityCaptionLbl: Label 'Item', MaxLength = 50;
+ ItemPostingGroupLbl: Label 'Item Posting Group';
+ ItemTrackingCodeLbl: Label 'Item Tracking Code';
+ MissingEntityTok: Label 'Missing %1', Comment = '%1 = the entity being validated';
+ Name2Lbl: Label 'Name 2';
+ NameLbl: Label 'Name';
+ PaymentTermsLbl: Label 'Payment Terms';
+ PhoneLbl: Label 'Phone';
+ PostCodeLbl: Label 'Post Code';
+ PreferredBankAccountLbl: Label 'Preferred Bank Account';
+ PurchaseOrderBuyFromVendorNoLbl: Label 'Buy-from Vendor No.';
+ PurchaseOrderPayToVendorNoLbl: Label 'Pay-to Vendor No.';
+ PurchUofMLbl: Label 'Purch. UofM';
+ QuantityLbl: Label 'Quantity';
+ QuantityRecLbl: Label 'Quantity Received';
+ SalesPersonLbl: Label 'Sales Person';
+ SearchDescription2Lbl: Label 'Search Description';
+ ShipmentMethodLbl: Label 'Shipment Method';
+ StatisticalAccountEntityCaptionLbl: Label 'Statistical Account', MaxLength = 50;
+ TaxAreaLbl: Label 'Tax Area';
+ TaxLiableLbl: Label 'Tax Liable';
+ TerritoryLbl: Label 'Territory';
+ TransitNoLbl: Label 'Transit No.';
+ TypeLbl: Label 'Type';
+ UnitListPriceLbl: Label 'Unit List Price';
+ VendorEntityCaptionLbl: Label 'Vendor', MaxLength = 50;
+ VendorOrderAddressEntityCaptionLbl: Label 'Vendor Order Address', MaxLength = 50;
+ VendorPostingGroupLbl: Label 'Vendor Posting Group';
+ VendorRemitAddressEntityCaptionLbl: Label 'Vendor Remit Address', MaxLength = 50;
+ ValidationStepGLAccountLbl: Label 'GLACCOUNT', MaxLength = 20;
+ ValidationStepStatAccountLbl: Label 'STATACCOUNT', MaxLength = 20;
+ ValidationStepBankAccountLbl: Label 'BANKACCOUNT', MaxLength = 20;
+ ValidationStepCustomerLbl: Label 'CUSTOMER', MaxLength = 20;
+ ValidationStepItemLbl: Label 'ITEM', MaxLength = 20;
+ ValidationStepPurchaseOrderLbl: Label 'PURCHASEORDER', MaxLength = 20;
+ ValidationStepVendorLbl: Label 'VENDOR', MaxLength = 20;
+ ValidatorDescriptionLbl: Label 'GP migration validator', MaxLength = 250;
+ Test_ACCOUNTEXISTS_Tok: Label 'ACCOUNTEXISTS', Locked = true;
+ Test_ACCOUNTNAME_Tok: Label 'ACCOUNTNAME', Locked = true;
+ Test_ACCOUNTTYPE_Tok: Label 'ACCOUNTTYPE', Locked = true;
+ Test_ACCOUNTCATEGORY_Tok: Label 'ACCOUNTCATEGORY', Locked = true;
+ Test_ACCOUNTDEBCRED_Tok: Label 'ACCOUNTDEBCRED', Locked = true;
+ Test_ACCOUNTSUBCATEGORY_Tok: Label 'ACCOUNTSUBCATEGORY', Locked = true;
+ Test_ACCOUNTINCBAL_Tok: Label 'ACCOUNTINCBAL', Locked = true;
+ Test_ACCOUNTBALANCE_Tok: Label 'ACCOUNTBALANCE', Locked = true;
+ Test_STATACCOUNTEXISTS_Tok: Label 'STATACCOUNTEXISTS', Locked = true;
+ Test_STATACCOUNTNAME_Tok: Label 'STATACCOUNTNAME', Locked = true;
+ Test_STATACCOUNTDIM1_Tok: Label 'STATACCOUNTDIM1', Locked = true;
+ Test_STATACCOUNTDIM2_Tok: Label 'STATACCOUNTDIM2', Locked = true;
+ Test_STATACCOUNTBALANCE_Tok: Label 'STATACCOUNTBALANCE', Locked = true;
+ Test_BANKACCOUNTEXISTS_Tok: Label 'BANKACCOUNTEXISTS', Locked = true;
+ Test_BANKACCOUNTNAME_Tok: Label 'BANKACCOUNTNAME', Locked = true;
+ Test_BANKACCOUNTNO_Tok: Label 'BANKACCOUNTNO', Locked = true;
+ Test_BANKACCOUNTADDR_Tok: Label 'BANKACCOUNTADDR', Locked = true;
+ Test_BANKACCOUNTADDR2_Tok: Label 'BANKACCOUNTADDR2', Locked = true;
+ Test_BANKACCOUNTCITY_Tok: Label 'BANKACCOUNTCITY', Locked = true;
+ Test_BANKACCOUNTCOUNTY_Tok: Label 'BANKACCOUNTCOUNTY', Locked = true;
+ Test_BANKACCOUNTPOSTCODE_Tok: Label 'BANKACCOUNTPOSTCODE', Locked = true;
+ Test_BANKACCOUNTPHN_Tok: Label 'BANKACCOUNTPHN', Locked = true;
+ Test_BANKACCOUNTFAX_Tok: Label 'BANKACCOUNTFAX', Locked = true;
+ Test_BANKACCOUNTTRANSITNO_Tok: Label 'BANKACCOUNTTRANSITNO', Locked = true;
+ Test_BANKACCOUNTBRANCHNO_Tok: Label 'BANKACCOUNTBRANCHNO', Locked = true;
+ Test_BANKACCOUNTBALANCE_Tok: Label 'BANKACCOUNTBALANCE', Locked = true;
+ Test_CUSTOMEREXISTS_Tok: Label 'CUSTOMEREXISTS', Locked = true;
+ Test_CUSTOMERNAME_Tok: Label 'CUSTOMERNAME', Locked = true;
+ Test_CUSTOMERPOSTINGGROUP_Tok: Label 'CUSTOMERPOSTINGGROUP', Locked = true;
+ Test_CUSTOMERADDR_Tok: Label 'CUSTOMERADDR', Locked = true;
+ Test_CUSTOMERADDR2_Tok: Label 'CUSTOMERADDR2', Locked = true;
+ Test_CUSTOMERCITY_Tok: Label 'CUSTOMERCITY', Locked = true;
+ Test_CUSTOMERPHN_Tok: Label 'CUSTOMERPHN', Locked = true;
+ Test_CUSTOMERFAX_Tok: Label 'CUSTOMERFAX', Locked = true;
+ Test_CUSTOMERNAME2_Tok: Label 'CUSTOMERNAME2', Locked = true;
+ Test_CUSTOMERCREDITLMT_Tok: Label 'CUSTOMERCREDITLMT', Locked = true;
+ Test_CUSTOMERCONTACT_Tok: Label 'CUSTOMERCONTACT', Locked = true;
+ Test_CUSTOMERSALESPERSON_Tok: Label 'CUSTOMERSALESPERSON', Locked = true;
+ Test_CUSTOMERSHIPMETHOD_Tok: Label 'CUSTOMERSHIPMETHOD', Locked = true;
+ Test_CUSTOMERPMTTERMS_Tok: Label 'CUSTOMERPMTTERMS', Locked = true;
+ Test_CUSTOMERTERRITORY_Tok: Label 'CUSTOMERTERRITORY', Locked = true;
+ Test_CUSTOMERTAXAREA_Tok: Label 'CUSTOMERTAXAREA', Locked = true;
+ Test_CUSTOMERTAXLIABLE_Tok: Label 'CUSTOMERTAXLIABLE', Locked = true;
+ Test_CUSTOMERBALANCE_Tok: Label 'CUSTOMERBALANCE', Locked = true;
+ Test_SHIPADDREXISTS_Tok: Label 'SHIPADDREXISTS', Locked = true;
+ Test_SHIPADDRNAME_Tok: Label 'SHIPADDRNAME', Locked = true;
+ Test_SHIPADDRADDR_Tok: Label 'SHIPADDRADDR', Locked = true;
+ Test_SHIPADDRADDR2_Tok: Label 'SHIPADDRADDR2', Locked = true;
+ Test_SHIPADDRCITY_Tok: Label 'SHIPADDRCITY', Locked = true;
+ Test_SHIPADDRPOSTCODE_Tok: Label 'SHIPADDRPOSTCODE', Locked = true;
+ Test_SHIPADDRPHN_Tok: Label 'SHIPADDRPHN', Locked = true;
+ Test_SHIPADDRFAX_Tok: Label 'SHIPADDRFAX', Locked = true;
+ Test_SHIPADDRCONTACT_Tok: Label 'SHIPADDRCONTACT', Locked = true;
+ Test_SHIPADDRSHIPMETHOD_Tok: Label 'SHIPADDRSHIPMETHOD', Locked = true;
+ Test_SHIPADDRCOUNTY_Tok: Label 'SHIPADDRCOUNTY', Locked = true;
+ Test_SHIPADDRTAXAREA_Tok: Label 'SHIPADDRTAXAREA', Locked = true;
+ Test_ITEMEXISTS_Tok: Label 'ITEMEXISTS', Locked = true;
+ Test_ITEMTYPE_Tok: Label 'ITEMTYPE', Locked = true;
+ Test_ITEMDESC_Tok: Label 'ITEMDESC', Locked = true;
+ Test_ITEMDESC2_Tok: Label 'ITEMDESC2', Locked = true;
+ Test_ITEMSEARCHDESC_Tok: Label 'ITEMSEARCHDESC', Locked = true;
+ Test_ITEMPOSTINGGROUP_Tok: Label 'ITEMPOSTINGGROUP', Locked = true;
+ Test_ITEMUNITLISTPRICE_Tok: Label 'ITEMUNITLISTPRICE', Locked = true;
+ Test_ITEMCOSTMETHOD_Tok: Label 'ITEMCOSTMETHOD', Locked = true;
+ Test_ITEMBASEUOFM_Tok: Label 'ITEMBASEUOFM', Locked = true;
+ Test_ITEMPURCHUOFM_Tok: Label 'ITEMPURCHUOFM', Locked = true;
+ Test_ITEMTRACKINGCODE_Tok: Label 'ITEMTRACKINGCODE', Locked = true;
+ Test_ITEMINVENTORY_Tok: Label 'ITEMINVENTORY', Locked = true;
+ Test_POEXISTS_Tok: Label 'POEXISTS', Locked = true;
+ Test_POBUYFROMVEND_Tok: Label 'POBUYFROMVEND', Locked = true;
+ Test_POPAYTOVEND_Tok: Label 'POPAYTOVEND', Locked = true;
+ Test_PODOCDATE_Tok: Label 'PODOCDATE', Locked = true;
+ Test_POLINEEXISTS_Tok: Label 'POLINEEXISTS', Locked = true;
+ Test_POLINEQTY_Tok: Label 'POLINEQTY', Locked = true;
+ Test_POLINEQTYRECV_Tok: Label 'POLINEQTYRECV', Locked = true;
+ Test_VENDOREXISTS_Tok: Label 'VENDOREXISTS', Locked = true;
+ Test_VENDORNAME_Tok: Label 'VENDORNAME', Locked = true;
+ Test_VENDORNAME2_Tok: Label 'VENDORNAME2', Locked = true;
+ Test_VENDORPOSTINGGROUP_Tok: Label 'VENDORPOSTINGGROUP', Locked = true;
+ Test_VENDORPREFBANKACCT_Tok: Label 'VENDORPREFBANKACCT', Locked = true;
+ Test_VENDORADDR_Tok: Label 'VENDORADDR', Locked = true;
+ Test_VENDORADDR2_Tok: Label 'VENDORADDR2', Locked = true;
+ Test_VENDORCITY_Tok: Label 'VENDORCITY', Locked = true;
+ Test_VENDORPHN_Tok: Label 'VENDORPHN', Locked = true;
+ Test_VENDORFAX_Tok: Label 'VENDORFAX', Locked = true;
+ Test_VENDORCONTACT_Tok: Label 'VENDORCONTACT', Locked = true;
+ Test_VENDORSHIPMETHOD_Tok: Label 'VENDORSHIPMETHOD', Locked = true;
+ Test_VENDORPMTTERMS_Tok: Label 'VENDORPMTTERMS', Locked = true;
+ Test_VENDORTAXAREA_Tok: Label 'VENDORTAXAREA', Locked = true;
+ Test_VENDORTAXLIABLE_Tok: Label 'VENDORTAXLIABLE', Locked = true;
+ Test_VENDORBALANCE_Tok: Label 'VENDORBALANCE', Locked = true;
+ Test_ORDERADDREXISTS_Tok: Label 'ORDERADDREXISTS', Locked = true;
+ Test_ORDERADDRNAME_Tok: Label 'ORDERADDRNAME', Locked = true;
+ Test_ORDERADDRADDR_Tok: Label 'ORDERADDRADDR', Locked = true;
+ Test_ORDERADDRADDR2_Tok: Label 'ORDERADDRADDR2', Locked = true;
+ Test_ORDERADDRCITY_Tok: Label 'ORDERADDRCITY', Locked = true;
+ Test_ORDERADDRPOSTCODE_Tok: Label 'ORDERADDRPOSTCODE', Locked = true;
+ Test_ORDERADDRPHN_Tok: Label 'ORDERADDRPHN', Locked = true;
+ Test_ORDERADDRFAX_Tok: Label 'ORDERADDRFAX', Locked = true;
+ Test_ORDERADDRCOUNTY_Tok: Label 'ORDERADDRCOUNTY', Locked = true;
+ Test_ORDERADDRCONTACT_Tok: Label 'ORDERADDRCONTACT', Locked = true;
+ Test_REMITADDREXISTS_Tok: Label 'REMITADDREXISTS', Locked = true;
+ Test_REMITADDRNAME_Tok: Label 'REMITADDRNAME', Locked = true;
+ Test_REMITADDRADDR_Tok: Label 'REMITADDRADDR', Locked = true;
+ Test_REMITADDRADDR2_Tok: Label 'REMITADDRADDR2', Locked = true;
+ Test_REMITADDRCITY_Tok: Label 'REMITADDRCITY', Locked = true;
+ Test_REMITADDRPOSTCODE_Tok: Label 'REMITADDRPOSTCODE', Locked = true;
+ Test_REMITADDRPHN_Tok: Label 'REMITADDRPHN', Locked = true;
+ Test_REMITADDRFAX_Tok: Label 'REMITADDRFAX', Locked = true;
+ Test_REMITADDRCOUNTY_Tok: Label 'REMITADDRCOUNTY', Locked = true;
+ Test_REMITADDRCONTACT_Tok: Label 'REMITADDRCONTACT', Locked = true;
+
+}
\ No newline at end of file
diff --git a/Apps/W1/HybridGP/app/src/codeunits/HybridGPWizard.codeunit.al b/Apps/W1/HybridGP/app/src/codeunits/HybridGPWizard.codeunit.al
index c8e4926357..4604bf594a 100644
--- a/Apps/W1/HybridGP/app/src/codeunits/HybridGPWizard.codeunit.al
+++ b/Apps/W1/HybridGP/app/src/codeunits/HybridGPWizard.codeunit.al
@@ -136,6 +136,7 @@ codeunit 4015 "Hybrid GP Wizard"
HybridReplicationDetail: Record "Hybrid Replication Detail";
GPMigrationErrorOverview: Record "GP Migration Error Overview";
GPMigrationWarnings: Record "GP Migration Warnings";
+ MigrationValidationError: Record "Migration Validation Error";
begin
GPCompanyMigrationSettings.Reset();
if GPCompanyMigrationSettings.FindSet() then
@@ -158,6 +159,9 @@ codeunit 4015 "Hybrid GP Wizard"
if not GPMigrationWarnings.IsEmpty() then
GPMigrationWarnings.DeleteAll();
+
+ if not MigrationValidationError.IsEmpty() then
+ MigrationValidationError.DeleteAll();
end;
[EventSubscriber(ObjectType::Table, Database::"Company", 'OnAfterDeleteEvent', '', false, false)]
@@ -170,6 +174,7 @@ codeunit 4015 "Hybrid GP Wizard"
HybridReplicationDetail: Record "Hybrid Replication Detail";
GPMigrationErrorOverview: Record "GP Migration Error Overview";
GPMigrationWarnings: Record "GP Migration Warnings";
+ MigrationValidationError: Record "Migration Validation Error";
begin
if Rec.IsTemporary() then
exit;
@@ -197,6 +202,10 @@ codeunit 4015 "Hybrid GP Wizard"
GPMigrationWarnings.SetRange("Company Name", Rec.Name);
if not GPMigrationWarnings.IsEmpty() then
GPMigrationWarnings.DeleteAll();
+
+ MigrationValidationError.SetRange("Company Name", Rec.Name);
+ if not MigrationValidationError.IsEmpty() then
+ MigrationValidationError.DeleteAll();
end;
local procedure ProcessesAreRunning(): Boolean
diff --git a/Apps/W1/HybridGP/app/src/pages/GPUpgradeSettings.Page.al b/Apps/W1/HybridGP/app/src/pages/GPUpgradeSettings.Page.al
index 499c2bbc52..fe393bce61 100644
--- a/Apps/W1/HybridGP/app/src/pages/GPUpgradeSettings.Page.al
+++ b/Apps/W1/HybridGP/app/src/pages/GPUpgradeSettings.Page.al
@@ -1,4 +1,5 @@
namespace Microsoft.DataMigration.GP;
+using Microsoft.DataMigration;
page 40043 "GP Upgrade Settings"
{
@@ -12,6 +13,29 @@ page 40043 "GP Upgrade Settings"
{
area(Content)
{
+ group(AutomaticValidation)
+ {
+ Caption = 'Automatic Validation';
+
+ field(GPAutomaticValidation; GPAutoValidation)
+ {
+ ApplicationArea = All;
+ Caption = 'GP';
+ ToolTip = 'Specifies whether automatic validation is enabled for the primary GP migration.';
+
+ trigger OnValidate()
+ var
+ MigrationValidationRegistry: Record "Migration Validator Registry";
+ GPMigrtionValidator: Codeunit "GP Migration Validator";
+ begin
+ if MigrationValidationRegistry.Get(GPMigrtionValidator.GetValidatorCode()) then begin
+ MigrationValidationRegistry.Validate(Automatic, GPAutoValidation);
+ MigrationValidationRegistry.Modify(true);
+ end;
+ end;
+ }
+ }
+
group(ErrorHandling)
{
Caption = 'Error Handling';
@@ -49,7 +73,16 @@ page 40043 "GP Upgrade Settings"
}
trigger OnOpenPage()
+ var
+ MigrationValidationRegistry: Record "Migration Validator Registry";
+ GPMigrtionValidator: Codeunit "GP Migration Validator";
begin
+ if MigrationValidationRegistry.Get(GPMigrtionValidator.GetValidatorCode()) then
+ GPAutoValidation := MigrationValidationRegistry.Automatic;
+
Rec.GetonInsertGPUpgradeSettings(Rec);
end;
+
+ var
+ GPAutoValidation: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/HybridGP/app/src/pages/HybridGPOverviewFb.Page.al b/Apps/W1/HybridGP/app/src/pages/HybridGPOverviewFb.Page.al
index df505e1037..e131808f24 100644
--- a/Apps/W1/HybridGP/app/src/pages/HybridGPOverviewFb.Page.al
+++ b/Apps/W1/HybridGP/app/src/pages/HybridGPOverviewFb.Page.al
@@ -94,7 +94,10 @@ page 40125 "Hybrid GP Overview Fb"
HelperFunctions: Codeunit "Helper Functions";
TotalGLBatchCount: Integer;
TotalStatisticalBatchCount: Integer;
+ TotalBankBatchCount: Integer;
+ TotalCustomerBatchCount: Integer;
TotalItemBatchCount: Integer;
+ TotalVendorBatchCount: Integer;
CompanyHasFailedBatches: Boolean;
FailedBatchMsgBuilder: TextBuilder;
AddComma: Boolean;
@@ -113,9 +116,12 @@ page 40125 "Hybrid GP Overview Fb"
repeat
TotalGLBatchCount := 0;
TotalStatisticalBatchCount := 0;
+ TotalBankBatchCount := 0;
+ TotalCustomerBatchCount := 0;
TotalItemBatchCount := 0;
+ TotalVendorBatchCount := 0;
- HelperFunctions.GetUnpostedBatchCountForCompany(HybridCompanyStatus.Name, TotalGLBatchCount, TotalStatisticalBatchCount, TotalItemBatchCount);
+ HelperFunctions.GetUnpostedBatchCountForCompany(HybridCompanyStatus.Name, TotalGLBatchCount, TotalStatisticalBatchCount, TotalBankBatchCount, TotalCustomerBatchCount, TotalItemBatchCount, TotalVendorBatchCount);
FailedBatchCount := FailedBatchCount + TotalGLBatchCount + TotalStatisticalBatchCount + TotalItemBatchCount;
CompanyHasFailedBatches := (TotalGLBatchCount > 0) or (TotalItemBatchCount > 0);
if CompanyHasFailedBatches then begin
diff --git a/Apps/W1/HybridGP/app/src/pages/IntelligentCloudExtension.PageExt.al b/Apps/W1/HybridGP/app/src/pages/IntelligentCloudExtension.PageExt.al
index 6c124ca695..079ea3f292 100644
--- a/Apps/W1/HybridGP/app/src/pages/IntelligentCloudExtension.PageExt.al
+++ b/Apps/W1/HybridGP/app/src/pages/IntelligentCloudExtension.PageExt.al
@@ -52,8 +52,11 @@ pageextension 4015 "Intelligent Cloud Extension" extends "Intelligent Cloud Mana
trigger OnAction()
var
+ HybridCloudManagement: Codeunit "Hybrid Cloud Management";
GPMigrationConfiguration: Page "GP Migration Configuration";
begin
+ HybridCloudManagement.PrepareMigrationValidation(); // TODO: REMOVE AFTER LOCAL TESTING
+
GPMigrationConfiguration.ShouldShowManagementPromptOnClose(false);
GPMigrationConfiguration.Run();
end;