Codeunit 5340 CRM Integration Table Synch., source in 29
Source29
src/Layers/W1/BaseApp/Integration/Dataverse/CRMIntegrationTableSynch.Codeunit.al1028 lines, Copyright (c) Microsoft Corporation. MIT
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Integration.Dataverse;
using Microsoft.CRM.Contact;
using Microsoft.Foundation.PaymentTerms;
using Microsoft.Foundation.Shipping;
using Microsoft.Integration.D365Sales;
using Microsoft.Integration.SyncEngine;
using System.Reflection;
using System.Threading;
using System.Utilities;
codeunit 5340 "CRM Integration Table Synch."
{
TableNo = "Integration Table Mapping";
trigger OnRun()
var
"Field": Record "Field";
OriginalJobQueueEntry: Record "Job Queue Entry";
ConnectionName: Text;
LatestModifiedOn: array[2] of DateTime;
isHandled: Boolean;
PrevStatus: Option;
MappingName: Code[20];
begin
OnBeforeRun(Rec, IsHandled);
if IsHandled then
exit;
ConnectionName := InitConnection();
TestConnection();
if Rec."Int. Table UID Field Type" = Field.Type::Option then
SynchOption(Rec)
else begin
Rec.SetOriginalJobQueueEntryOnHold(OriginalJobQueueEntry, PrevStatus);
if Rec.Direction in [Rec.Direction::ToIntegrationTable, Rec.Direction::Bidirectional] then
LatestModifiedOn[DateType::Local] := PerformScheduledSynchToIntegrationTable(Rec);
if Rec.Direction in [Rec.Direction::FromIntegrationTable, Rec.Direction::Bidirectional] then
LatestModifiedOn[DateType::Integration] := PerformScheduledSynchFromIntegrationTable(Rec);
MappingName := Rec.Name;
if not Rec.Find() then
Session.LogMessage('0000GAP', StrSubstNo(UnableToFindMappingErr, MappingName), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTok)
else begin
LocalUpdateTableMappingModifiedOn(Rec, LatestModifiedOn);
Rec.SetOriginalJobQueueEntryStatus(OriginalJobQueueEntry, PrevStatus);
end;
end;
CloseConnection(ConnectionName);
end;
var
CRMIntTableSubscriber: Codeunit "CRM Int. Table. Subscriber";
CRMProductName: Codeunit "CRM Product Name";
TypeHelper: Codeunit "Type Helper";
MappedFieldDictionary: Dictionary of [Text, Boolean];
SupportedSourceType: Option ,RecordID,GUID;
DateType: Option ,Integration,Local;
OutOfMapFilter: Boolean;
ConnectionNotEnabledErr: Label 'The %1 connection is not enabled.', Comment = '%1 = CRM product name';
RecordNotFoundErr: Label 'Cannot find %1 record %2.', Comment = '%1 = Source table caption, %2 = The lookup value when searching for the source record';
SourceRecordIsNotInMappingErr: Label 'Cannot find the mapping %2 in table %1.', Comment = '%1 Integration Table Mapping caption, %2 Integration Table Mapping Name';
CannotDetermineSourceOriginErr: Label 'Cannot determine the source origin: %1.', Comment = '%1 the value of the source id';
SynchronizeEmptySetErr: Label 'Attempted to synchronize an empty set of records.';
NoMappingErr: Label 'No mapping is set for %1.', Comment = '%1=Table Caption';
ModifiedByFieldMustBeGUIDErr: Label 'The field %1 in the table %2 must be of type GUID.', Comment = '%1 - a field name, %2 - a table name';
OptionMappingCannotBeBidirectionalErr: Label 'Option mappings can only synchronize from integration table or to integration table.';
CategoryTok: Label 'AL Dataverse Integration', Locked = true;
ClearCacheTxt: Label 'Clear cache.', Locked = true;
CopyRecordRefFailedTxt: Label 'Copy record reference failed. Dataverse ID: %1', Locked = true, Comment = '%1 - Dataverse record id';
UnableToFindMappingErr: Label 'Unable to find Integration Table Mapping %1', Locked = true, Comment = '%1 - Mapping name';
FieldKeyTxt: Label '%1-%2', Locked = true;
internal procedure InitConnection() ConnectionName: Text
var
CRMConnectionSetup: Record "CRM Connection Setup";
CRMIntegrationManagement: Codeunit "CRM Integration Management";
initConnectionHandled: Boolean;
begin
CRMIntegrationManagement.OnInitCDSConnection(ConnectionName, initConnectionHandled);
if initConnectionHandled then
exit(ConnectionName);
CRMConnectionSetup.Get();
if not CRMConnectionSetup."Is Enabled" then
Error(ConnectionNotEnabledErr, CRMProductName.FULL());
ConnectionName := Format(CreateGuid());
CRMConnectionSetup.RegisterConnectionWithName(ConnectionName);
ClearCache();
end;
local procedure GetIntegrationUserId(): Guid
var
CRMConnectionSetup: Record "CRM Connection Setup";
CRMIntegrationManagement: Codeunit "CRM Integration Management";
IntegrationUserId: Guid;
handled: Boolean;
begin
CRMIntegrationManagement.OnGetCDSIntegrationUserId(IntegrationUserId, handled);
if handled then
exit(IntegrationUserId);
exit(CRMConnectionSetup.GetIntegrationUserID());
end;
internal procedure TestConnection()
var
CRMConnectionSetup: Record "CRM Connection Setup";
CRMIntegrationManagement: Codeunit "CRM Integration Management";
testConnectionHandled: Boolean;
begin
CRMIntegrationManagement.OnTestCDSConnection(testConnectionHandled);
if not testConnectionHandled then
if not CRMConnectionSetup.TryReadSystemUsers() then
Error(GetLastErrorText);
end;
internal procedure CloseConnection(ConnectionName: Text)
var
CRMConnectionSetup: Record "CRM Connection Setup";
CRMIntegrationManagement: Codeunit "CRM Integration Management";
closeConnectionHandled: Boolean;
begin
ClearCache();
CRMIntegrationManagement.OnCloseCDSConnection(ConnectionName, closeConnectionHandled);
if closeConnectionHandled then
exit;
CRMConnectionSetup.UnregisterConnectionWithName(ConnectionName);
end;
local procedure ClearCache()
begin
Session.LogMessage('0000CM6', ClearCacheTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTok);
CRMIntTableSubscriber.ClearCache();
Clear(CRMIntTableSubscriber);
end;
local procedure IsModifiedByFilterNeeded(IntegrationTableMapping: Record "Integration Table Mapping"): Boolean
var
IsHandled: Boolean;
IsNeeded: Boolean;
begin
OnIsModifiedByFilterNeeded(IntegrationTableMapping, IsHandled, IsNeeded);
if IsHandled then
exit(IsNeeded);
if IntegrationTableMapping."Delete After Synchronization" then
exit(false);
if IntegrationTableMapping.Direction = IntegrationTableMapping.Direction::Bidirectional then
exit(not HasUnidirectionalFieldMappingFromIntegrationTable(IntegrationTableMapping));
exit(false);
end;
local procedure FindModifiedCRMRecords(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping"; var FailedNotSkippedIdDictionary: Dictionary of [Guid, Boolean])
var
IntegrationRecordSynch: Codeunit "Integration Record Synch.";
CRMRecordRef: RecordRef;
CRMID: Guid;
ModifiedByFilterNeeded: Boolean;
TableFilter: Text;
FilterList: List of [Text];
begin
ModifiedByFilterNeeded := IsModifiedByFilterNeeded(IntegrationTableMapping);
IntegrationRecordSynch.SplitIntegrationTableFilter(IntegrationTableMapping, FilterList);
CRMRecordRef.Open(IntegrationTableMapping."Integration Table ID");
foreach TableFilter in FilterList do begin
SetIntRecordRefFilter(CRMRecordRef, TableFilter, ModifiedByFilterNeeded, IntegrationTableMapping);
if CRMRecordRef.FindSet() then
repeat
CRMID := CRMRecordRef.Field(IntegrationTableMapping."Integration Table UID Fld. No.").Value();
if not FailedNotSkippedIdDictionary.ContainsKey(CRMID) then
if not TryCopyRecordReference(IntegrationTableMapping, CRMRecordRef, TempCRMRecordRef, false) then
Session.LogMessage('0000ECC', StrSubstNo(CopyRecordRefFailedTxt, CRMID), Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTok);
until CRMRecordRef.Next() = 0;
end;
CRMRecordRef.Close();
end;
[TryFunction]
local procedure TryCopyRecordReference(var IntegrationTableMapping: Record "Integration Table Mapping"; FromRec: RecordRef; var ToRec: RecordRef; ValidateOnInsert: Boolean)
begin
CopyRecordReference(IntegrationTableMapping, FromRec, ToRec, ValidateOnInsert);
end;
local procedure CopyRecordReference(var IntegrationTableMapping: Record "Integration Table Mapping"; FromRec: RecordRef; var ToRec: RecordRef; ValidateOnInsert: Boolean)
var
TempBlob: Codeunit "Temp Blob";
FromField: FieldRef;
ToField: FieldRef;
Counter: Integer;
begin
if FromRec.Number <> ToRec.Number then
exit;
ToRec.Init();
for Counter := 1 to FromRec.FieldCount do begin
FromField := FromRec.FieldIndex(Counter);
if FromField.Type <> FieldType::TableFilter then
if FromField.Type <> FieldType::Blob then begin
ToField := ToRec.Field(FromField.Number);
ToField.Value := FromField.Value();
end else
if IsFieldMapped(IntegrationTableMapping, FromRec.Number(), FromField.Number()) then begin
ToField := ToRec.Field(FromField.Number);
TempBlob.FromFieldRef(FromField);
TempBlob.ToFieldRef(ToField);
end;
end;
ToRec.Insert(ValidateOnInsert);
end;
local procedure IsFieldMapped(var IntegrationTableMapping: Record "Integration Table Mapping"; TableNo: Integer; FieldNo: Integer): Boolean
var
IntegrationFieldMapping: Record "Integration Field Mapping";
FieldKey: Text;
IsMapped: Boolean;
begin
FieldKey := StrSubstNo(FieldKeyTxt, TableNo, FieldNo);
if not MappedFieldDictionary.ContainsKey(FieldKey) then begin
IntegrationFieldMapping.SetRange("Integration Table Mapping Name", IntegrationTableMapping.Name);
if TableNo = IntegrationTableMapping."Integration Table ID" then
IntegrationFieldMapping.SetRange("Integration Table Field No.", FieldNo)
else
IntegrationFieldMapping.SetRange("Field No.", FieldNo);
IsMapped := not IntegrationFieldMapping.IsEmpty();
MappedFieldDictionary.Add(FieldKey, IsMapped);
end;
exit(MappedFieldDictionary.Get(FieldKey));
end;
local procedure SetIntRecordRefFilter(var IntRecordRef: RecordRef; TableFilter: Text; ModifiedByFilterNeeded: Boolean; IntegrationTableMapping: Record "Integration Table Mapping")
var
ModifiedOnFieldRef: FieldRef;
ModifyByFieldRef: FieldRef;
ModifyByFieldNo: Integer;
begin
if TableFilter <> '' then
IntRecordRef.SetView(TableFilter);
if IntegrationTableMapping."Synch. Modified On Filter" <> 0DT then begin
ModifiedOnFieldRef := IntRecordRef.Field(IntegrationTableMapping."Int. Tbl. Modified On Fld. No.");
ModifiedOnFieldRef.SetFilter('>%1', IntegrationTableMapping."Synch. Modified On Filter" - 999);
end;
if ModifiedByFilterNeeded then begin
ModifyByFieldNo := GetModifyByFieldNo(IntegrationTableMapping."Integration Table ID");
ModifyByFieldRef := IntRecordRef.Field(ModifyByFieldNo);
if ModifyByFieldRef.Type <> FieldType::GUID then
Error(ModifiedByFieldMustBeGUIDErr, ModifyByFieldRef.Name, IntRecordRef.Name);
ModifyByFieldRef.SetFilter('<>%1', GetIntegrationUserId())
end;
end;
local procedure FindFailedNotSkippedCRMRecords(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping"; var TempCRMIntegrationRecord: Record "CRM Integration Record" temporary; var CRMIDDictionary: Dictionary of [Guid, Boolean]): Boolean
var
IntegrationRecordSynch: Codeunit "Integration Record Synch.";
CRMRecordRef: RecordRef;
CRMTableView: Text;
CRMIDFilter: Text;
Found: Boolean;
CRMIDFilterList: List of [Text];
begin
CRMTableView := IntegrationTableMapping.GetIntegrationTableFilter();
CRMRecordRef.Open(IntegrationTableMapping."Integration Table ID");
CRMRecordRef.SetView(CRMTableView);
CRMIDFilter := CRMRecordRef.Field(IntegrationTableMapping."Integration Table UID Fld. No.").GetFilter();
CRMRecordRef.Close();
if CRMIDFilter <> '' then
exit(false); // Ignore failed not synched records if going to synch records selected by CRMID
TempCRMIntegrationRecord.SetRange(Skipped, false);
TempCRMIntegrationRecord.SetRange("Table ID", IntegrationTableMapping."Table ID");
TempCRMIntegrationRecord.SetRange("Last Synch. Result", TempCRMIntegrationRecord."Last Synch. Result"::Failure);
if TempCRMIntegrationRecord.FindSet() then begin
repeat
if not CRMIDDictionary.ContainsKey(TempCRMIntegrationRecord."CRM ID") then
CRMIDDictionary.Add(TempCRMIntegrationRecord."CRM ID", true);
until TempCRMIntegrationRecord.Next() = 0;
IntegrationRecordSynch.GetIdFilterList(CRMIDDictionary, CRMIDFilterList);
Found := CacheFilteredCRMRecords(CRMIDFilterList, IntegrationTableMapping, TempCRMRecordRef);
end;
TempCRMIntegrationRecord.SetRange(Skipped);
TempCRMIntegrationRecord.SetRange("Table ID");
TempCRMIntegrationRecord.SetRange("Last Synch. Result");
exit(Found);
end;
local procedure CacheFilteredCRMRecords(var CRMIDFilterList: List of [Text]; IntegrationTableMapping: Record "Integration Table Mapping"; var TempCRMRecordRef: RecordRef): Boolean
var
CRMRecordRef: RecordRef;
CRMIDFilter: Text;
Cached: Boolean;
begin
foreach CRMIDFilter in CRMIDFilterList do
if CRMIDFilter <> '' then begin
CRMRecordRef.Open(IntegrationTableMapping."Integration Table ID");
CRMRecordRef.Field(IntegrationTableMapping."Integration Table UID Fld. No.").SetFilter(CRMIDFilter);
if CRMRecordRef.FindSet() then
repeat
CopyRecordReference(IntegrationTableMapping, CRMRecordRef, TempCRMRecordRef, false);
Cached := true;
until CRMRecordRef.Next() = 0;
CRMRecordRef.Close();
end;
exit(Cached);
end;
local procedure CacheFilteredCRMTable(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping"; var TempCRMIntegrationRecord: Record "CRM Integration Record" temporary)
var
FailedNotSkippedIdDictionary: Dictionary of [Guid, Boolean];
begin
TempCRMRecordRef.Open(IntegrationTableMapping."Integration Table ID", true);
FindFailedNotSkippedCRMRecords(TempCRMRecordRef, IntegrationTableMapping, TempCRMIntegrationRecord, FailedNotSkippedIdDictionary);
FindModifiedCRMRecords(TempCRMRecordRef, IntegrationTableMapping, FailedNotSkippedIdDictionary);
end;
local procedure HasUnidirectionalFieldMappingFromIntegrationTable(var IntegrationTableMapping: Record "Integration Table Mapping"): Boolean
var
IntegrationFieldMapping: Record "Integration Field Mapping";
begin
IntegrationFieldMapping.SetRange("Integration Table Mapping Name", IntegrationTableMapping.Name);
IntegrationFieldMapping.SetRange(Direction, IntegrationFieldMapping.Direction::FromIntegrationTable);
IntegrationFieldMapping.SetFilter("Integration Table Field No.", '>0');
IntegrationFieldMapping.SetFilter(Status, '<>%1', IntegrationFieldMapping.Status::Disabled);
exit(not IntegrationFieldMapping.IsEmpty());
end;
procedure GetOutOfMapFilter(): Boolean
begin
exit(OutOfMapFilter);
end;
local procedure GetSourceRecordRef(IntegrationTableMapping: Record "Integration Table Mapping"; SourceID: Variant; var RecordRef: RecordRef): Boolean
var
RecordID: RecordID;
CRMID: Guid;
begin
case GetSourceType(SourceID) of
SupportedSourceType::RecordID:
begin
RecordID := SourceID;
if RecordID.TableNo = 0 then
Error(CannotDetermineSourceOriginErr, SourceID);
if not (RecordID.TableNo = IntegrationTableMapping."Table ID") then
Error(SourceRecordIsNotInMappingErr, IntegrationTableMapping.TableCaption(), IntegrationTableMapping.Name);
if not RecordRef.Get(RecordID) then
Error(RecordNotFoundErr, RecordRef.Caption, Format(RecordID, 0, 1));
exit(IntegrationTableMapping.FindFilteredRec(RecordRef, OutOfMapFilter));
end;
SupportedSourceType::GUID:
begin
CRMID := SourceID;
if IsNullGuid(CRMID) then
Error(CannotDetermineSourceOriginErr, SourceID);
if not IntegrationTableMapping.GetRecordRef(CRMID, RecordRef) then
Error(RecordNotFoundErr, IntegrationTableMapping.GetExtendedIntegrationTableCaption(), CRMID);
exit(IntegrationTableMapping.FindFilteredRec(RecordRef, OutOfMapFilter));
end;
else
Error(CannotDetermineSourceOriginErr, SourceID);
end;
end;
local procedure GetSourceType(Source: Variant): Integer
begin
if Source.IsRecordId then
exit(SupportedSourceType::RecordID);
if Source.IsGuid then
exit(SupportedSourceType::GUID);
exit(0);
end;
local procedure FindFailedNotSkippedLocalRecords(var SystemIdDictionary: Dictionary of [Guid, Boolean]; IntegrationTableMapping: Record "Integration Table Mapping"; var TempCRMIntegrationRecord: Record "CRM Integration Record" temporary): Boolean
var
LocalRecordRef: RecordRef;
PrimaryKeyRef: KeyRef;
LocalTableView: Text;
I: Integer;
NoFilterOnPK: Boolean;
begin
LocalTableView := IntegrationTableMapping.GetTableFilter();
LocalRecordRef.Open(IntegrationTableMapping."Table ID");
LocalRecordRef.SetView(LocalTableView);
if LocalRecordRef.Field(LocalRecordRef.SystemIdNo()).GetFilter() <> '' then
exit(false); // Ignore failed not synched records if going to synch records selected by systemId
PrimaryKeyRef := LocalRecordRef.KeyIndex(1);
for I := 1 to PrimaryKeyRef.FieldCount() do
if LocalRecordRef.Field(PrimaryKeyRef.FieldIndex(I).Number()).GetFilter() = '' then begin
NoFilterOnPK := true;
break;
end;
if not NoFilterOnPK then
exit(false); // Ignore failed not synched records if going to synch records selected by primary key
TempCRMIntegrationRecord.SetRange(Skipped, false);
TempCRMIntegrationRecord.SetRange("Table ID", IntegrationTableMapping."Table ID");
TempCRMIntegrationRecord.SetRange("Last Synch. CRM Result", TempCRMIntegrationRecord."Last Synch. CRM Result"::Failure);
if TempCRMIntegrationRecord.FindSet() then
repeat
if not SystemIdDictionary.ContainsKey(TempCRMIntegrationRecord."Integration ID") then
SystemIdDictionary.Add(TempCRMIntegrationRecord."Integration ID", true);
until TempCRMIntegrationRecord.Next() = 0;
TempCRMIntegrationRecord.SetRange(Skipped);
TempCRMIntegrationRecord.SetRange("Table ID");
TempCRMIntegrationRecord.SetRange("Last Synch. CRM Result");
exit(SystemIdDictionary.Count() > 0);
end;
local procedure FindFailedNotSkippedLocalRecords(var SystemIdDictionary: Dictionary of [Guid, Boolean]; IntegrationTableMapping: Record "Integration Table Mapping"; var TempCRMOptionMapping: Record "CRM Option Mapping" temporary): Boolean
var
LocalRecordRef: RecordRef;
PrimaryKeyRef: KeyRef;
CurrSystemId: Guid;
LocalTableView: Text;
I: Integer;
NoFilterOnPK: Boolean;
begin
LocalTableView := IntegrationTableMapping.GetTableFilter();
LocalRecordRef.Open(IntegrationTableMapping."Table ID");
LocalRecordRef.SetView(LocalTableView);
if LocalRecordRef.Field(LocalRecordRef.SystemIdNo()).GetFilter() <> '' then
exit(false); // Ignore failed not synched records if going to synch records selected by systemId
PrimaryKeyRef := LocalRecordRef.KeyIndex(1);
for I := 1 to PrimaryKeyRef.FieldCount() do
if LocalRecordRef.Field(PrimaryKeyRef.FieldIndex(I).Number()).GetFilter() = '' then begin
NoFilterOnPK := true;
break;
end;
if not NoFilterOnPK then
exit(false); // Ignore failed not synched records if going to synch records selected by primary key
TempCRMOptionMapping.SetRange(Skipped, false);
TempCRMOptionMapping.SetRange("Table ID", IntegrationTableMapping."Table ID");
TempCRMOptionMapping.SetRange("Last Synch. CRM Result", TempCRMOptionMapping."Last Synch. CRM Result"::Failure);
if TempCRMOptionMapping.FindSet() then
repeat
if LocalRecordRef.Get(TempCRMOptionMapping."Record ID") then begin
CurrSystemId := LocalRecordRef.Field(LocalRecordRef.SystemIdNo()).Value();
if not SystemIdDictionary.ContainsKey(CurrSystemId) then
SystemIdDictionary.Add(CurrSystemId, true);
end;
until TempCRMOptionMapping.Next() = 0;
TempCRMOptionMapping.SetRange(Skipped);
TempCRMOptionMapping.SetRange("Table ID");
TempCRMOptionMapping.SetRange("Last Synch. CRM Result");
exit(SystemIdDictionary.Count() > 0);
end;
procedure SynchOption(var IntegrationTableMapping: Record "Integration Table Mapping")
var
"Field": Record "Field";
begin
if TypeHelper.GetField(
IntegrationTableMapping."Integration Table ID", IntegrationTableMapping."Integration Table UID Fld. No.", Field)
then
if Field.Type = Field.Type::Option then
SynchOptions(IntegrationTableMapping);
end;
procedure SynchRecord(IntegrationTableMapping: Record "Integration Table Mapping"; SourceID: Variant; ForceModify: Boolean; IgnoreSynchOnlyCoupledRecords: Boolean) JobID: Guid
var
IntegrationTableSynch: Codeunit "Integration Table Synch.";
FromRecordRef: RecordRef;
ToRecordRef: RecordRef;
begin
if GetSourceRecordRef(IntegrationTableMapping, SourceID, FromRecordRef) then begin // sets the global OutOfMapFilter
JobID := IntegrationTableSynch.BeginIntegrationSynchJob(TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, FromRecordRef.Number);
if not IsNullGuid(JobID) then begin
IntegrationTableSynch.Synchronize(FromRecordRef, ToRecordRef, ForceModify, IgnoreSynchOnlyCoupledRecords);
IntegrationTableSynch.EndIntegrationSynchJob();
end;
end;
end;
procedure SynchRecordsToIntegrationTable(var RecordsToSynchRecordRef: RecordRef; IgnoreChanges: Boolean; IgnoreSynchOnlyCoupledRecords: Boolean) JobID: Guid
var
IntegrationTableMapping: Record "Integration Table Mapping";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
IntegrationRecordRef: RecordRef;
IsHandled: Boolean;
begin
OnBeforeSynchRecordsToIntegrationTable(RecordsToSynchRecordRef, IgnoreChanges, IgnoreSynchOnlyCoupledRecords, IsHandled);
if IsHandled then
exit;
if not IntegrationTableMapping.FindMappingForTable(RecordsToSynchRecordRef.Number) then
Error(NoMappingErr, RecordsToSynchRecordRef.Name);
RecordsToSynchRecordRef.Ascending(false);
if not RecordsToSynchRecordRef.FindSet() then
Error(SynchronizeEmptySetErr);
JobID :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, RecordsToSynchRecordRef.Number);
if not IsNullGuid(JobID) then begin
repeat
IntegrationTableSynch.Synchronize(RecordsToSynchRecordRef, IntegrationRecordRef, IgnoreChanges, IgnoreSynchOnlyCoupledRecords)
until RecordsToSynchRecordRef.Next() = 0;
IntegrationTableSynch.EndIntegrationSynchJob();
end;
end;
procedure SynchRecordsFromIntegrationTable(var RecordsToSynchRecordRef: RecordRef; SourceTableNo: Integer; IgnoreChanges: Boolean; IgnoreSynchOnlyCoupledRecords: Boolean) JobID: Guid
var
IntegrationTableMapping: Record "Integration Table Mapping";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
IntegrationRecordRef: RecordRef;
IsHandled: Boolean;
begin
OnBeforeSynchRecordsFromIntegrationTable(RecordsToSynchRecordRef, SourceTableNo, IgnoreChanges, IgnoreSynchOnlyCoupledRecords, IsHandled);
if IsHandled then
exit;
if not IntegrationTableMapping.FindMapping(SourceTableNo, RecordsToSynchRecordRef.Number) then
Error(NoMappingErr, RecordsToSynchRecordRef.Name);
RecordsToSynchRecordRef.Ascending(false);
if not RecordsToSynchRecordRef.FindSet() then
Error(SynchronizeEmptySetErr);
JobID :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, RecordsToSynchRecordRef.Number);
if not IsNullGuid(JobID) then begin
repeat
IntegrationTableSynch.Synchronize(RecordsToSynchRecordRef, IntegrationRecordRef, IgnoreChanges, IgnoreSynchOnlyCoupledRecords)
until RecordsToSynchRecordRef.Next() = 0;
IntegrationTableSynch.EndIntegrationSynchJob();
end;
end;
local procedure SynchNAVTableToCRM(IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch.") LatestLocalModifiedOn: DateTime
var
IntTableManualSubscribers: Codeunit "Int. Table Manual Subscribers";
IntegrationRecordSynch: Codeunit "Integration Record Synch.";
TempCRMIntegrationRecord: Record "CRM Integration Record" temporary;
SourceRecordRef: RecordRef;
RecordSystemId: Guid;
FailedNotSkippedIdDictionary: Dictionary of [Guid, Boolean];
FilterList: List of [Text];
TableFilter: Text;
begin
BindSubscription(IntTableManualSubscribers);
LatestLocalModifiedOn := 0DT;
IntegrationRecordSynch.SplitLocalTableFilter(IntegrationTableMapping, FilterList);
CreateCRMIntegrationRecordClone(IntegrationTableMapping."Table ID", TempCRMIntegrationRecord);
SourceRecordRef.Open(IntegrationTableMapping."Table ID");
if FindFailedNotSkippedLocalRecords(FailedNotSkippedIdDictionary, IntegrationTableMapping, TempCRMIntegrationRecord) then
foreach RecordSystemId in FailedNotSkippedIdDictionary.Keys() do
if SourceRecordRef.GetBySystemId(RecordSystemId) then
SyncNAVRecordToCRM(SourceRecordRef, IntegrationTableMapping, IntegrationTableSynch, TempCRMIntegrationRecord, LatestLocalModifiedOn);
foreach TableFilter in FilterList do
if IntegrationRecordSynch.FindModifiedLocalRecords(SourceRecordRef, TableFilter, IntegrationTableMapping) then
repeat
RecordSystemId := SourceRecordRef.Field(SourceRecordRef.SystemIdNo()).Value();
if not FailedNotSkippedIdDictionary.ContainsKey(RecordSystemId) then
SyncNAVRecordToCRM(SourceRecordRef, IntegrationTableMapping, IntegrationTableSynch, TempCRMIntegrationRecord, LatestLocalModifiedOn);
until SourceRecordRef.Next() = 0;
OnSynchNAVTableToCRMOnBeforeCheckLatestModifiedOn(SourceRecordRef, IntegrationTableMapping);
SourceRecordRef.Close();
UnbindSubscription(IntTableManualSubscribers);
end;
procedure SyncNAVRecordToCRM(var SourceRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch."; var TempCRMIntegrationRecord: Record "CRM Integration Record" temporary; var LatestLocalModifiedOn: DateTime)
var
DestinationRecordRef: RecordRef;
SystemIdFieldRef: FieldRef;
IgnoreRecord: Boolean;
ForceModify: Boolean;
LocalModifiedOn: DateTime;
begin
ForceModify := IntegrationTableMapping."Delete After Synchronization";
IgnoreRecord := false;
OnQueryPostFilterIgnoreRecord(SourceRecordRef, IgnoreRecord);
if not IgnoreRecord then begin
SystemIdFieldRef := SourceRecordRef.Field(SourceRecordRef.SystemIdNo);
if not TempCRMIntegrationRecord.IsIntegrationIdCoupled(SystemIdFieldRef.Value(), SourceRecordRef.Number) then
IgnoreRecord := IntegrationTableMapping."Synch. Only Coupled Records";
if not IgnoreRecord then
IntegrationTableSynch.Synchronize(SourceRecordRef, DestinationRecordRef, ForceModify, false);
end;
// collect latest modified time across all local records including not synched
LocalModifiedOn := IntegrationTableSynch.GetRowLastModifiedOn(IntegrationTableMapping, SourceRecordRef);
if LocalModifiedOn > LatestLocalModifiedOn then
LatestLocalModifiedOn := LocalModifiedOn;
end;
local procedure SynchCRMTableToNAV(IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch."; var SourceRecordRef: RecordRef) LatestIntegrationModifiedOn: DateTime
var
IntTableManualSubscribers: Codeunit "Int. Table Manual Subscribers";
TempCRMIntegrationRecord: Record "CRM Integration Record" temporary;
DestinationRecordRef: RecordRef;
CloneSourceRecordRef: RecordRef;
IntegrationModifiedOn: DateTime;
IgnoreRecord: Boolean;
ForceModify: Boolean;
begin
BindSubscription(IntTableManualSubscribers);
LatestIntegrationModifiedOn := 0DT;
CreateCRMIntegrationRecordClone(IntegrationTableMapping."Table ID", TempCRMIntegrationRecord);
CacheFilteredCRMTable(SourceRecordRef, IntegrationTableMapping, TempCRMIntegrationRecord);
ForceModify := IntegrationTableMapping."Delete After Synchronization";
if SourceRecordRef.FindSet() then
repeat
CloneSourceRecordRef.Open(IntegrationTableMapping."Integration Table ID", true);
CopyRecordReference(IntegrationTableMapping, SourceRecordRef, CloneSourceRecordRef, false);
IgnoreRecord := false;
OnQueryPostFilterIgnoreRecord(CloneSourceRecordRef, IgnoreRecord);
if not IgnoreRecord then begin
if TempCRMIntegrationRecord.IsCRMRecordRefCoupled(CloneSourceRecordRef) then
TempCRMIntegrationRecord.Delete()
else
IgnoreRecord := IntegrationTableMapping."Synch. Only Coupled Records";
if not IgnoreRecord then
IntegrationTableSynch.Synchronize(CloneSourceRecordRef, DestinationRecordRef, ForceModify, false);
end;
// collect latest modified time across all integration records including not synched
IntegrationModifiedOn := IntegrationTableSynch.GetRowLastModifiedOn(IntegrationTableMapping, CloneSourceRecordRef);
if IntegrationModifiedOn > LatestIntegrationModifiedOn then
LatestIntegrationModifiedOn := IntegrationModifiedOn;
CloneSourceRecordRef.Close();
until SourceRecordRef.Next() = 0;
UnbindSubscription(IntTableManualSubscribers);
end;
local procedure GetModifyByFieldNo(CRMTableID: Integer): Integer
var
"Field": Record "Field";
begin
Field.SetRange(TableNo, CRMTableID);
Field.SetRange(FieldName, 'ModifiedBy'); // All CRM tables should have "ModifiedBy" field
Field.FindFirst();
exit(Field."No.");
end;
local procedure PerformScheduledSynchToIntegrationTable(var IntegrationTableMapping: Record "Integration Table Mapping") LatestLocalModifiedOn: DateTime
var
CRMFullSynchReviewLine: Record "CRM Full Synch. Review Line";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
JobId: Guid;
JobCreationTime: DateTime;
begin
JobCreationTime := CurrentDateTime();
JobId :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, IntegrationTableMapping."Table ID");
if not IsNullGuid(JobId) then begin
CRMFullSynchReviewLine.FullSynchStarted(IntegrationTableMapping, JobId, IntegrationTableMapping.Direction::ToIntegrationTable);
LatestLocalModifiedOn := SynchNAVTableToCRM(IntegrationTableMapping, IntegrationTableSynch);
IntegrationTableSynch.EndIntegrationSynchJob();
CRMFullSynchReviewLine.FullSynchFinished(IntegrationTableMapping, IntegrationTableMapping.Direction::ToIntegrationTable);
end;
if LatestLocalModifiedOn > JobCreationTime then
LatestLocalModifiedOn := JobCreationTime;
end;
local procedure PerformScheduledSynchFromIntegrationTable(var IntegrationTableMapping: Record "Integration Table Mapping") LatestIntegrationModifiedOn: DateTime
var
CRMFullSynchReviewLine: Record "CRM Full Synch. Review Line";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
SourceRecordRef: RecordRef;
JobId: Guid;
JobCreationTime: DateTime;
begin
JobCreationTime := CurrentDateTime();
JobId :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, IntegrationTableMapping."Integration Table ID");
if not IsNullGuid(JobId) then begin
CRMFullSynchReviewLine.FullSynchStarted(IntegrationTableMapping, JobId, IntegrationTableMapping.Direction::FromIntegrationTable);
LatestIntegrationModifiedOn := SynchCRMTableToNAV(IntegrationTableMapping, IntegrationTableSynch, SourceRecordRef);
IntegrationTableSynch.EndIntegrationSynchJob();
CRMFullSynchReviewLine.FullSynchFinished(IntegrationTableMapping, IntegrationTableMapping.Direction::FromIntegrationTable);
end;
if LatestIntegrationModifiedOn > JobCreationTime then
LatestIntegrationModifiedOn := JobCreationTime;
end;
local procedure LocalUpdateTableMappingModifiedOn(var IntegrationTableMapping: Record "Integration Table Mapping"; LatestModifiedOn: array[2] of DateTime)
var
IsHandled: Boolean;
begin
IsHandled := false;
OnBeforeUpdateTableMappingModifiedOn(IntegrationTableMapping, LatestModifiedOn, IsHandled);
if IsHandled then
exit;
IntegrationTableMapping.UpdateTableMappingModifiedOn(LatestModifiedOn);
end;
internal procedure CreateCRMIntegrationRecordClone(ForTable: Integer; var TempCRMIntegrationRecord: Record "CRM Integration Record" temporary)
var
CRMIntegrationRecord: Record "CRM Integration Record";
CRMIntegrationManagement: Codeunit "CRM Integration Management";
begin
TempCRMIntegrationRecord.Reset();
TempCRMIntegrationRecord.DeleteAll();
CRMIntegrationManagement.RepairBrokenCouplings();
CRMIntegrationRecord.SetRange("Table ID", ForTable);
if not CRMIntegrationRecord.FindSet() then
exit;
repeat
TempCRMIntegrationRecord.Copy(CRMIntegrationRecord, false);
TempCRMIntegrationRecord.Insert();
until CRMIntegrationRecord.Next() = 0;
end;
local procedure SynchOptions(var IntegrationTableMapping: Record "Integration Table Mapping")
var
OriginalJobQueueEntry: Record "Job Queue Entry";
LatestModifiedOn: array[2] of DateTime;
PrevStatus: Option;
begin
IntegrationTableMapping.SetOriginalJobQueueEntryOnHold(OriginalJobQueueEntry, PrevStatus);
if IntegrationTableMapping.Direction = IntegrationTableMapping.Direction::Bidirectional then
Error(OptionMappingCannotBeBidirectionalErr);
if IntegrationTableMapping.Direction = IntegrationTableMapping.Direction::ToIntegrationTable then
LatestModifiedOn[DateType::Local] := PerformScheduledOptionSynchToIntegrationTable(IntegrationTableMapping);
if IntegrationTableMapping.Direction = IntegrationTableMapping.Direction::FromIntegrationTable then
LatestModifiedOn[DateType::Integration] := PerformScheduledOptionSynchFromIntegrationTable(IntegrationTableMapping);
LocalUpdateTableMappingModifiedOn(IntegrationTableMapping, LatestModifiedOn);
IntegrationTableMapping.SetOriginalJobQueueEntryStatus(OriginalJobQueueEntry, PrevStatus);
end;
local procedure PerformScheduledOptionSynchFromIntegrationTable(var IntegrationTableMapping: Record "Integration Table Mapping") LatestIntegrationModifiedOn: DateTime
var
CRMFullSynchReviewLine: Record "CRM Full Synch. Review Line";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
JobId: Guid;
JobCreationTime: DateTime;
begin
JobCreationTime := CurrentDateTime();
JobId :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, IntegrationTableMapping."Integration Table ID");
if not IsNullGuid(JobId) then begin
CRMFullSynchReviewLine.FullSynchStarted(IntegrationTableMapping, JobId, IntegrationTableMapping.Direction::FromIntegrationTable);
LatestIntegrationModifiedOn := SynchCRMOptionToNAV(IntegrationTableMapping, IntegrationTableSynch);
IntegrationTableSynch.EndIntegrationSynchJob();
CRMFullSynchReviewLine.FullSynchFinished(IntegrationTableMapping, IntegrationTableMapping.Direction::FromIntegrationTable);
end;
if LatestIntegrationModifiedOn > JobCreationTime then
LatestIntegrationModifiedOn := JobCreationTime;
end;
local procedure PerformScheduledOptionSynchToIntegrationTable(var IntegrationTableMapping: Record "Integration Table Mapping") LatestIntegrationModifiedOn: DateTime
var
CRMFullSynchReviewLine: Record "CRM Full Synch. Review Line";
IntegrationTableSynch: Codeunit "Integration Table Synch.";
JobId: Guid;
JobCreationTime: DateTime;
begin
JobCreationTime := CurrentDateTime();
JobId :=
IntegrationTableSynch.BeginIntegrationSynchJob(
TABLECONNECTIONTYPE::CRM, IntegrationTableMapping, IntegrationTableMapping."Table ID");
if not IsNullGuid(JobId) then begin
CRMFullSynchReviewLine.FullSynchStarted(IntegrationTableMapping, JobId, IntegrationTableMapping.Direction::ToIntegrationTable);
LatestIntegrationModifiedOn := SynchCRMOptionFromNAV(IntegrationTableMapping, IntegrationTableSynch);
IntegrationTableSynch.EndIntegrationSynchJob();
CRMFullSynchReviewLine.FullSynchFinished(IntegrationTableMapping, IntegrationTableMapping.Direction::ToIntegrationTable);
end;
if LatestIntegrationModifiedOn > JobCreationTime then
LatestIntegrationModifiedOn := JobCreationTime;
end;
local procedure CacheFilteredCRMOptions(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping")
var
CRMRecordRef: RecordRef;
CRMOptionFieldRef: FieldRef;
CRMTableView: Text;
CRMOptionIdFilter: Text;
CRMOption: Text;
CRMOptions: List of [Text];
OptionDictionary: Dictionary of [Text, Integer];
i: Integer;
begin
LoadCRMOption(TempCRMRecordRef, IntegrationTableMapping);
CRMTableView := IntegrationTableMapping.GetIntegrationTableFilter();
CRMRecordRef.Open(IntegrationTableMapping."Integration Table ID");
CRMRecordRef.SetView(CRMTableView);
CRMOptionFieldRef := CRMRecordRef.Field(IntegrationTableMapping."Integration Table UID Fld. No.");
CRMOptionIdFilter := CRMOptionFieldRef.GetFilter();
CRMRecordRef.Close();
if CRMOptionIdFilter <> '' then begin
for i := 1 to CRMOptionFieldRef.EnumValueCount() do
OptionDictionary.Add(CRMOptionFieldRef.GetEnumValueCaption(i), CRMOptionFieldRef.GetEnumValueOrdinal(i));
CRMOptions := CRMOptionIdFilter.Replace('(', '').Replace(')', '').Split('|');
foreach CRMOption in CRMOptions do
if OptionDictionary.ContainsKey(CRMOption) then
CRMOptionIdFilter := CRMOptionIdFilter.Replace(CRMOption, Format(OptionDictionary.Get(CRMOption)));
TempCRMRecordRef.Field(TempCRMRecordRef.KeyIndex(1).FieldIndex(1).Number).SetFilter(CRMOptionIdFilter);
end;
end;
[Scope('OnPrem')]
procedure LoadCRMOption(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping")
var
CRMPaymentTerms: Record "CRM Payment Terms";
CRMFreightTerms: Record "CRM Freight Terms";
CRMShippingMethod: Record "CRM Shipping Method";
begin
case IntegrationTableMapping."Table ID" of
Database::"Payment Terms":
begin
CRMPaymentTerms.Load();
TempCRMRecordRef.GetTable(CRMPaymentTerms);
end;
Database::"Shipment Method":
begin
CRMFreightTerms.Load();
TempCRMRecordRef.GetTable(CRMFreightTerms);
end;
Database::"Shipping Agent":
begin
CRMShippingMethod.Load();
TempCRMRecordRef.GetTable(CRMShippingMethod);
end;
end;
OnLoadCRMOption(TempCRMRecordRef, IntegrationTableMapping);
end;
local procedure SynchCRMOptionToNAV(IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch.") LatestIntegrationModifiedOn: DateTime
var
CRMOptionMapping: Record "CRM Option Mapping";
DestinationRecordRef: RecordRef;
SourceRecordRef: RecordRef;
ForceModify: Boolean;
IgnoreRecord: Boolean;
begin
LatestIntegrationModifiedOn := 0DT;
CacheFilteredCRMOptions(SourceRecordRef, IntegrationTableMapping);
ForceModify := IntegrationTableMapping."Delete After Synchronization";
if SourceRecordRef.FindSet() then
repeat
IgnoreRecord := false;
if not CRMOptionMapping.IsCRMRecordRefMapped(SourceRecordRef, CRMOptionMapping) then
if IntegrationTableMapping."Synch. Only Coupled Records" then
IgnoreRecord := true;
if not IgnoreRecord then begin
IntegrationTableSynch.SynchronizeOption(SourceRecordRef, DestinationRecordRef, ForceModify, false);
LatestIntegrationModifiedOn := CurrentDateTime();
end;
until SourceRecordRef.Next() = 0;
end;
local procedure SynchCRMOptionFromNAV(IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch.") LatestLocalModifiedOn: DateTime
var
TempCRMOptionMapping: Record "CRM Option Mapping" temporary;
IntegrationRecordSynch: Codeunit "Integration Record Synch.";
SourceRecordRef: RecordRef;
FailedNotSkippedIdDictionary: Dictionary of [Guid, Boolean];
FilterList: List of [Text];
RecordSystemId: Guid;
TableFilter: Text;
begin
LatestLocalModifiedOn := 0DT;
IntegrationRecordSynch.SplitLocalTableFilter(IntegrationTableMapping, FilterList);
SourceRecordRef.Open(IntegrationTableMapping."Table ID");
if FindFailedNotSkippedLocalRecords(FailedNotSkippedIdDictionary, IntegrationTableMapping, TempCRMOptionMapping) then
foreach RecordSystemId in FailedNotSkippedIdDictionary.Keys() do
if SourceRecordRef.GetBySystemId(RecordSystemId) then
SyncNAVOptionToCRM(SourceRecordRef, IntegrationTableMapping, IntegrationTableSynch, LatestLocalModifiedOn);
foreach TableFilter in FilterList do
if IntegrationRecordSynch.FindModifiedLocalRecords(SourceRecordRef, TableFilter, IntegrationTableMapping) then
repeat
RecordSystemId := SourceRecordRef.Field(SourceRecordRef.SystemIdNo()).Value();
if not FailedNotSkippedIdDictionary.ContainsKey(RecordSystemId) then
SyncNAVOptionToCRM(SourceRecordRef, IntegrationTableMapping, IntegrationTableSynch, LatestLocalModifiedOn);
until SourceRecordRef.Next() = 0;
SourceRecordRef.Close();
end;
local procedure SyncNAVOptionToCRM(var SourceRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping"; var IntegrationTableSynch: Codeunit "Integration Table Synch."; var LatestLocalModifiedOn: DateTime)
var
CRMOptionMapping: Record "CRM Option Mapping";
DestinationRecordRef: RecordRef;
IgnoreRecord: Boolean;
ForceModify: Boolean;
LocalModifiedOn: DateTime;
begin
ForceModify := IntegrationTableMapping."Delete After Synchronization";
IgnoreRecord := false;
if not IgnoreRecord then begin
CRMOptionMapping.SetRange("Record ID", SourceRecordRef.RecordId);
if CRMOptionMapping.IsEmpty() then
IgnoreRecord := IntegrationTableMapping."Synch. Only Coupled Records";
if not IgnoreRecord then
IntegrationTableSynch.SynchronizeOption(SourceRecordRef, DestinationRecordRef, ForceModify, false);
end;
// collect latest modified time across all local records including not synched
LocalModifiedOn := IntegrationTableSynch.GetRowLastModifiedOn(IntegrationTableMapping, SourceRecordRef);
if LocalModifiedOn > LatestLocalModifiedOn then
LatestLocalModifiedOn := LocalModifiedOn;
end;
[Scope('OnPrem')]
procedure GetMaxNumberOfConditions(): Integer
var
IntegrationRecordSynch: Codeunit "Integration Record Synch.";
Handled: Boolean;
MaxNumberOfConditions: Integer;
begin
OnGetMaxNumberOfConditions(Handled, MaxNumberOfConditions);
if Handled then
exit(IntegrationRecordSynch.GetMaxNumberOfConditions());
end;
[IntegrationEvent(false, false)]
local procedure OnQueryPostFilterIgnoreRecord(SourceRecordRef: RecordRef; var IgnoreRecord: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeRun(IntegrationTableMapping: Record "Integration Table Mapping"; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeUpdateTableMappingModifiedOn(var IntegrationTableMapping: Record "Integration Table Mapping"; ModifiedOn: array[2] of DateTime; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnSynchNAVTableToCRMOnBeforeCheckLatestModifiedOn(var SourceRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnGetMaxNumberOfConditions(var Handled: Boolean; var Value: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnLoadCRMOption(var TempCRMRecordRef: RecordRef; IntegrationTableMapping: Record "Integration Table Mapping")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnIsModifiedByFilterNeeded(var IntegrationTableMapping: Record "Integration Table Mapping"; var IsHandled: Boolean; var IsNeeded: Boolean);
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeSynchRecordsFromIntegrationTable(var RecordsToSynchRecordRef: RecordRef; SourceTableNo: Integer; IgnoreChanges: Boolean; IgnoreSynchOnlyCoupledRecords: Boolean; var IsHandled: Boolean);
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeSynchRecordsToIntegrationTable(var RecordsToSynchRecordRef: RecordRef; IgnoreChanges: Boolean; IgnoreSynchOnlyCoupledRecords: Boolean; var IsHandled: Boolean);
begin
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"CRM Integration Table Synch.", 'OnQueryPostFilterIgnoreRecord', '', false, false)]
local procedure IgnoreCompanyContactOnQueryPostFilterIgnoreRecord(SourceRecordRef: RecordRef; var IgnoreRecord: Boolean)
var
Contact: Record Contact;
CRMSynchHelper: Codeunit "CRM Synch. Helper";
begin
if IgnoreRecord then
exit;
if SourceRecordRef.Number = DATABASE::Contact then begin
if CRMSynchHelper.IsContactTypeCheckIgnored() then
exit;
SourceRecordRef.SetTable(Contact);
if Contact.Type = Contact.Type::Company then
IgnoreRecord := true;
end;
end;
[EventSubscriber(ObjectType::Table, Database::"Job Queue Entry", 'OnBeforeModifyEvent', '', false, false)]
local procedure OnBeforeModifyJobQueueEntry(var Rec: Record "Job Queue Entry"; var xRec: Record "Job Queue Entry"; RunTrigger: Boolean)
var
CRMFullSynchReviewLine: Record "CRM Full Synch. Review Line";
begin
if Rec.IsTemporary() then
exit;
CRMFullSynchReviewLine.OnBeforeModifyJobQueueEntry(Rec);
end;
[EventSubscriber(ObjectType::Table, Database::"Integration Synch. Job", 'OnCanBeRemoved', '', false, false)]
local procedure OnSynchJobEntryCanBeRemoved(IntegrationSynchJob: Record "Integration Synch. Job"; var AllowRemoval: Boolean)
var
CRMIntegrationRecord: Record "CRM Integration Record";
begin
if AllowRemoval then
exit;
CRMIntegrationRecord.SetRange(Skipped, true);
CRMIntegrationRecord.SetRange("Last Synch. Job ID", IntegrationSynchJob.ID);
if CRMIntegrationRecord.IsEmpty() then begin
CRMIntegrationRecord.SetRange("Last Synch. Job ID");
CRMIntegrationRecord.SetRange("Last Synch. CRM Job ID", IntegrationSynchJob.ID);
if CRMIntegrationRecord.IsEmpty() then
AllowRemoval := true;
end;
end;
}