InCountry logo
mobile-nav
Search
  • Products
    • Products
      • InCountry for Salesforce
      • Data Residency-as-a-Service
      • Alibaba Cloud InCountry Service
      • Compliance and security
    • Gateways
      • Email
      • Payment Vault
      • Web Forms
      • HTML
    • Developers
      • REST API
      • SDK
  • Solutions
    • Automotive
    • Energy
    • Financial services
    • Healthcare
    • Retail
    • Technology
    • Latest success story
      • IBM Consulting
  • Integrations
    • Cegid
    • Intertrust
    • MuleSoft
    • PayPal
    • Salesforce
    • ServiceNow
    • Stripe
    • Veeva Systems
    • Yandex
  • Resources
    • Country compliance
    • Documentation
    • Library
    • Partners
    • Pricing
  • About
    • News and Blog
    • Careers
    • Contact Us
    • FAQ
    • Leadership
  • Login
  • Schedule a Demo

›Developer’s guide

Home
  • InCountry Platform
Portal
  • Getting started
  • Documentation
    • Dashboard
    • Managing environments
    • Managing SDK credentials and services
    • Managing Border configuration
    • Managing payment vaults
    • Managing email gateways
    • Managing resident functions
    • Managing file imports
    • Managing profile and organization
    • Managing users
    • Managing encryption keys
  • Release notes
Border
  • Documentation
  • Release notes
REST API
  • Documentation
  • How to test CRUD requests through REST API
  • Release notes
POP API
  • Documentation
  • Release notes
Resident Functions
  • Documentation
Salesforce
  • About
  • Overview
  • Quick start guide for three-model package
  • Quick start guide for legacy package
  • Administrator's guide
    • Managing the package
    • Managing permissions
    • Managing OAuth2 authentication and authorization
    • Managing certificates
    • Registering CSP Trusted Sites
    • Managing InCountry Endpoints
    • Managing REST endpoints
    • Managing InCountry flags
    • Loading the application
    • Managing data regulation policies
    • Managing protected fields
    • Hashing the UserName field
    • Managing custom objects
    • Replacing standard elements
    • Configuring record search
    • Managing components
    • Managing web components
    • Setting up Salesforce Experience Cloud
    • Managing resident functions
    • Managing InCountry cache
    • Managing Apex triggers
    • Managing record synchronization
    • Managing web forms
    • Tracking changes to data regulation policies and regulated fields
    • Using formula fields
    • Using frontend validations
    • Using Email-to-Case feature
    • Debugging
    • Migrating data from one Salesforce organization to another
  • Developer’s guide
    • Apex SDK
    • JavaScript API
    • Retrieving record statistics
    • Tracking field history
  • User's guide
    • Working with protected fields
    • Sending compliant email messages
    • Importing data into Salesforce
    • Migrating records
    • Managing audit reports
    • Converting leads
    • Managing reports
    • FAQ
    • Release notes
Payment Vault
  • Documentation
BYOK
  • Documentation
FAQ
  • Get started with the platform
  • Integration options
  • Data regulation models
  • Limits and quotas
  • Video tutorials
Service Status
  • Status

Field history tracking in replication models

The following table describes how the field history tracking works in different replication models:

note

Please make your solution decisions based on generally available functionality only. The new replication model is not implemented yet.

Functionality Legacy replicated model (pre-commit/post-commit) UI-based replicated model (custom Lightning Web Components)
Create/update a record from UI The Field History saves a regulated data value before the pre-commit process:
  1. User saves a record.
  2. The Field History tracks a regulated data value.
  3. Pre-commit is executed.
  4. The Field History saves a hashed value.
  5. The record is synchronized with the InCountry platform.
  6. Post-commit is executed.
  7. The Field History tracks a regulated data value.

In this scenario, the Field History tracks a regulated data value BEFORE its synchronization with the InCountry platform.

The Field History saves a regulated data value after its synchronization with the InCountry platform:
  1. User saves a record.
  2. The record is synchronized with the InCountry platform.
  3. The record is saved to the Salesforce database.
  4. The Filed History tracks a regulated data value.

In this scenario, the Filed History tracks a regulated data value AFTER its synchronization with the InCountry platform.

Create/update a record via custom Salesforce API Depends on the architecture of a custom Apex webservice.

The following option is available (with Apex SDK):

  1. A record is saved via an Apex webservice.
  2. The record is synchronized with the InCountry platform.
  3. The record is saved to the Salesforce database.
  4. The Filed History tracks a regulated data value.

In this scenario, the Field History is recorded after its synchronization with the InCountry platform.

Create/update a record via the native Salesforce API In this scenario the Field History always tracks a regulated data value BEFORE its synchronization with the InCountry platform.

As a workaround, you can use InbCountry Border. In this case, Border will capture regulated values and will store them in InCountry. The Field History will capture the regulated data value AFTER its synchronization with the InCountry platform.

Update a regulated field in BEFORE triggers The Field History tracks a regulated data value before its synchronization with the InCountry platform.

Two potential workarounds are available:

  1. Regulated data fields should not be changed synchronously BEFORE execution of triggers.
  2. Regulated data fields can be updated asynchronously in a separate transaction:
    • Asynchronous transaction.
    • Change of the regulated field value.
    • Synchronization with the InCountry platform (Apex SDK).
    • The record is saved to the Salesforce database.
    • The Field History tracks a regulated data value AFTER its synchronization with the InCountry platform.
Change a regulated field in custom Apex controllers Depends on the architecture of Apex controllers.

The following option is available (with Apex SDK):

  1. A record is saved via an Apex controller.
  2. The record is synchronized with the InCountry platform.
  3. The record is saved to the Salesforce database.
  4. The Filed History tracks a regulated data value.

In this scenario, the Field History tracks a regulated data value after its synchronization with the InCountry platform.

Code examples

Cases are created or updated via a custom Apex RESTful web-service

@RestResource(urlMapping='/Cases/*')
global with sharing class CaseWebService {
    @HttpPut
    global static Case upsertCase(String Subject, String Status, String Origin, String Priority, String Id) {
        // Prepare Case record in memory
        Case thisCase = new Case(
                Subject=subject,
                Status=status,
                Origin=origin,
                Priority=priority);
        if (String.isNotBlank(Id)) {
            thisCase.Id = Id;
        }
        // First save the case to InCountry
        InCountryRecordSynchronizer synchronizer = new InCountryRecordSynchronizer('Case');
        String temporaryId = synchronizer.addSObject(thisCase);
        System.debug('temporary id = ' + temporaryId);
        Map<String, InCountryRecordSynchronizer.InCountrySaveStatus> statuses = synchronizer.sync();
        if (statuses.get(temporaryId).isSuccess) {
            InCountryReplicationTriggerHandler.disableTrigger = true;
            // Then upsert the case in Salesforce
            upsert thisCase;

            // If necessary change the case Id in InCountry
            String recordId = String.valueOf(thisCase.get('Id'));
            if (recordId != temporaryId) {
                InCountryRecordSynchronizer.migrate(JSON.serialize(synchronizer.mapSensitiveData), new Map<String, String> {
                    temporaryId => recordId
                 });
            }
        }
        // Return the case.
        return thisCase;
    }
}

REST Explorer

A request is performed at this endpoint /services/apexrest/Cases/

{
  "Status" : "Working",
  "Subject" : "Bigfoot Sighting!",
  "Priority" : "Medium"
}

The following result is returned:

{
  "attributes" : {
    "type" : "Case",
    "url" : "/services/data/v53.0/sobjects/Case/5005w00001r5XvzAAE"
  },
  "Subject" : "Bigfoot Sighting!",
  "Status" : "Working",
  "Origin" : null,
  "Priority" : "Medium",
  "Id" : "5005w00001r5XvzAAE"
}

Find in InCountry

Regulated fields are updated in Case Trigger

Case - regulated object. Regulated fields are Type and Priority. A Customer wants to update Priority to high if Type is set to Structural.

Apex trigger:

trigger CaseTrigger on Case (after insert) {
    if (Trigger.isAfter && Trigger.isInsert) {
        List<Case> casesToUpdate = CaseTriggerHandler.getCasesToUpdate(Trigger.New);
        if (!casesToUpdate.isEmpty()) {
            CaseTriggerHandler.updateRegulatedFields((new Map<Id, Case>(casesToUpdate)).keySet());
        }
    }
}

Trigger Handler:

public class CaseTriggerHandler {

    /*
     * Asynchronous method with business logic and synchronization to InCountry PoP
     */
    @future(callout=true)
    public static void updateRegulatedFields(Set<Id> caseIds) {
        List<Case> cases = [SELECT Id, Type, Priority FROM Case WHERE Id IN :caseIds];
        List<Case> casesToUpdate = getCasesToUpdate(cases);
        for (Case caseRecord : casesToUpdate) {
            caseRecord.Priority = 'High';
        }
        if (!casesToUpdate.isEmpty()) {
            InCountry service = InCountry.getInstance();
            service.write(casesToUpdate);
        }
        update casesToUpdate;
    }

    public static List<Case> getCasesToUpdate(List<Case> cases) {
        List<Case> casesToUpdate = new List<Case>();
        for(Case caseRecord : cases) {
            // Type and Priority are regulated fields
            if (caseRecord.Type == 'Structural') {
                casesToUpdate.add(caseRecord);
            }
        }
        return casesToUpdate;
    }
}

Details

Cases are created via Apex Lightning Controller

public with sharing class CasesConroller {
    @AuraEnabled
    public static Case upsertCase(String Subject, String Status, String Origin, String Priority, String Id) {
        // Prepare Case record in memory
        Case thisCase = new Case(
                Subject=subject,
                Status=status,
                Origin=origin,
                Priority=priority);
        if (String.isNotBlank(Id)) {
            thisCase.Id = Id;
        }
        // First save the case to InCountry
        InCountryRecordSynchronizer synchronizer = new InCountryRecordSynchronizer('Case');
        String temporaryId = synchronizer.addSObject(thisCase);
        System.debug('temporary id = ' + temporaryId);
        Map<String, InCountryRecordSynchronizer.InCountrySaveStatus> statuses = synchronizer.sync();
        if (statuses.get(temporaryId).isSuccess) {
            InCountryReplicationTriggerHandler.disableTrigger = true;
            // Then upsert the case in Salesforce
            upsert thisCase;

            // If necessary change the case Id in InCountry
            String recordId = String.valueOf(thisCase.get('Id'));
            if (recordId != temporaryId) {
                InCountryRecordSynchronizer.migrate(JSON.serialize(synchronizer.mapSensitiveData), new Map<String, String> {
                    temporaryId => recordId
                 });
            }
        }
        // Return the case.
        return thisCase;
    }
}
← Retrieving record statisticsWorking with protected fields →
InCountry logo blue
© InCountry 2022.
All rights reserved. InCountry, Inc
  • PRIVACY POLICY
  • TERMS OF SERVICE
  • Social share
    • YouTube logo
    • Facebook logo
    • Twitter logo
    • LinkedIn
  • Column 1
    • Products
      • Products
        • InCountry for Salesforce
        • Data Residency-as-a-Service
        • Alibaba Cloud InCountry Service
        • Compliance and security
      • Gateways
        • Email
        • Payment Vault
        • Web Forms
        • HTML
      • Developers
        • REST API
        • SDK
  • Column 2
    • Solutions
      • Automotive
      • Energy
      • Financial services
      • Healthcare
      • Retail
      • Technology
    • Integrations
      • Cegid
      • Intertrust
      • MuleSoft
      • PayPal
      • Salesforce
      • ServiceNow
      • Stripe
      • Veeva Systems
      • Yandex
  • Column 3
    • Resources
      • Country compliance
      • Documentation
      • Library
      • Partners
      • Pricing
    • About
      • News and Blog
      • Careers
      • Contact Us
      • FAQ
      • Leadership