Set Up Apex Triggers for Visit and Inventory Access

You can use these Apex triggers with profile-based permissions and organization-wide sharing settings to make sure that users access just their visits and inventories.

Available in: Lightning Experience

Available in: Enterprise and Unlimited Editions with Health Cloud or Health Cloud for Life Sciences


User Permissions Needed
To define, edit, delete, set security, and set version settings for Apex classes: Author Apex
To run Apex tests: View Setup and Configuration

To set up Apex triggers, you must create a few utility classes first.

  1. Click Setup and select Developer Console.
  2. Create the VisitAccess utility class.
    1. Select File | New | Apex Class.
    2. For the Apex class name, enter VisitAccess.
    3. Delete the auto-generated content and paste the following code.
      1public class VisitAccess {
      2    
      3    static Set<String> accessLevels = new Set<String>{'read', 'edit'};
      4    
      5    public static void insertVisitAccess(Visit visit, User user, String accessType) {
      6        insertVisitAccess(visit, new List<User>{user}, accessType);
      7    }
      8    
      9    public static void insertVisitAccess(Visit visit, Id objectId, String accessType) {
      10        if(isUser(objectId)) {
      11            insertVisitAccess(visit, new User(id=objectId), accessType);
      12        }
      13    }
      14    
      15    public static void insertVisitAccess(Visit visit, List<User> users, String accessType) {
      16        if(visit==null || visit.Id==null || users==null || users.isEmpty()) {
      17            return;
      18        }
      19        List<VisitShare> shareVisitList = new List<VisitShare>();
      20        for(User user: users) {
      21            if(user==null)
      22                continue;
      23            VisitShare shareVisit = New VisitShare();
      24            shareVisit.ParentId=visit.Id;
      25            shareVisit.UserOrGroupID = user.Id;
      26            shareVisit.AccessLevel = accessType;
      27            shareVisitList.add(shareVisit);
      28        }
      29        try{
      30            Database.insert(shareVisitList);
      31        } catch(Exception e) {
      32            System.debug('Unexpected exception : ' + e);
      33        }
      34    }
      35    
      36    public static void updateVisitAccess(Visit visit, List<Id> oldUsers, List<Id> newUsers, String access) {
      37        if(visit==null) {
      38            return;
      39        }
      40        if(oldUsers!=null) {
      41            for(Id userId : oldUsers) {
      42                if(isUser(userId)) {
      43                    deleteVisitAccess(visit, new User(id=userId));
      44                }
      45            }
      46        }
      47        if(newUsers!=null) {
      48            for(Id userId : newUsers) {
      49                if(isUser(userId)) {
      50                    insertVisitAccess(visit, userId, access);
      51                }
      52            }
      53        }
      54    }
      55    
      56    public static void updateVisitAccess(Visit oldVisit, Visit updatedVisit, String access) {
      57        //Comment this condition if for any update visit need to be shared
      58        if(oldVisit.VisitorId==updatedVisit.VisitorId) {
      59            return;
      60        }
      61        updateVisitAccess(oldVisit, oldVisit!=null?new List<Id>{oldVisit.VisitorId}:new List<Id>(), updatedVisit!=null?new List<Id>{updatedVisit.VisitorId}:new List<Id>(), access);
      62    }
      63    
      64    //remove user from oldVisit if user does not have any more access to oldVisit, and link user to newVisit
      65    public static void updateVisitAccess(Visit oldVisit, Visit newVisit, List<User> users, String access) {
      66        if(users==null || users.isEmpty()) {
      67            return;
      68        }
      69        if(access==null||!accessLevels.contains(access)) {
      70            access='edit';
      71        }
      72        for(User user : users) {
      73            if(oldVisit!=null)
      74                deleteVisitAccess(oldVisit, user);
      75             if(newVisit!=null)
      76                 insertVisitAccess(newVisit, user, access);
      77        }
      78    }
      79    
      80        
      81    //remove user from oldVisit if user does not have any more access to oldVisit, and link user to newVisit
      82    public static void updateVisitAccess(Visit oldVisit, Visit newVisit, Id objectId, String access) {
      83          if(isUser(objectId)) {
      84              updateVisitAccess(oldVisit, newVisit, new List<User>{new User(id=objectId)}, access);
      85          }
      86    }
      87    
      88    public static void deleteVisitAccess(Visit visit, List<User> users) {
      89        List<VisitShare> visitShareList = new List<VisitShare>();
      90        for(User user : users) {
      91            if(isUserLinkedToVisit(user, visit))
      92                continue;
      93            List<VisitShare> visitShare = [select Id from VisitShare where ParentId = :visit.Id and UserOrGroupID = :user.Id and RowCause='Manual'];
      94            if(!visitShare.isEmpty()) {
      95                visitShareList.add(visitShare.get(0));
      96            }
      97        }
      98        delete visitShareList;
      99    }
      100    
      101    public static void deleteVisitAccess(Visit visit, User user) {
      102        deleteVisitAccess(visit, new List<User>{user});
      103    }
      104    
      105    public static void deleteVisitAccess(Visit visit, Id objectId) {
      106        if(isUser(objectId)) {
      107            deleteVisitAccess(visit, new User(id=objectId));
      108        }
      109    }
      110    
      111    public static Boolean isUser(Id objId) {
      112        if(objId==null)
      113            return false;
      114        List<User> users = [select Id from User where Id = :objId];
      115        return !users.isEmpty();
      116    }
      117    
      118    public static Boolean isUser(Object obj) {
      119        if(obj==null)
      120            return false;
      121        try {
      122            User user = (User)obj;
      123        } catch(TypeException e) {
      124            return false;
      125        }
      126        return true;
      127    }
      128
      129    public static Boolean isUserLinkedToVisit(User user, Visit visit) {
      130        List<Visit> visits = [select Id from Visit where VisitorId = :user.Id and Id=:visit.Id];
      131        List<Visitor> visitors = [select Id from Visitor where AssigneeId = :user.Id and VisitId=:visit.Id];
      132        return !visits.isEmpty() || !visitors.isEmpty();
      133    }
      134}
    4. Select File | Save.
  3. Create the visit trigger.
    1. Select File | New | Apex Trigger.
    2. For the trigger name, enter VisitTrigger, and select Visit from the sObject picklist.
    3. Delete the auto-generated content and paste the following sample.
      1trigger VisitTrigger on Visit (after insert, after update) {
      2    List<VisitShare> visitShares = new List<VisitShare>();
      3    for(Visit visit : trigger.new) {
      4        if(trigger.isUpdate) {
      5             Visit oldVisit = trigger.oldMap.get(visit.Id);
      6             VisitAccess.updateVisitAccess(oldVisit, visit, 'edit');
      7        }
      8        if(trigger.isInsert) {
      9            VisitAccess.insertVisitAccess(visit, visit.VisitorId, 'edit');
      10        }
      11    }
      12}
    4. Select File | Save.
  4. Create the visitor trigger.
    1. Select File | New | Apex Trigger.
    2. For the trigger name, enter VisitorTrigger, and select Visitor from the sObject picklist.
    3. Delete the auto-generated content and paste the following sample.
      1trigger VisitorTrigger on Visitor (after insert, after update, after delete) {
      2    
      3    if(trigger.isInsert) {
      4        for(Visitor visitor : trigger.new) {
      5            VisitAccess.insertVisitAccess(new Visit(id=visitor.VisitId), visitor.AssigneeId, 'edit');
      6        }
      7    }
      8    
      9    if(trigger.isUpdate) {
      10         for(Visitor visitor : trigger.new) {
      11            Visitor oldVisitor = trigger.oldMap.get(visitor.Id);
      12            VisitAccess.updateVisitAccess(new Visit(id=oldVisitor.VisitId), new Visit(id=visitor.VisitId), visitor.AssigneeId, 'edit');
      13        }
      14    }
      15    
      16    if(trigger.isDelete) {
      17        for(Visitor visitor : trigger.old) {
      18            VisitAccess.deleteVisitAccess(new Visit(id=visitor.VisitId), visitor.AssigneeId);
      19        }
      20    }
      21}
    4. Select File | Save.
    Now, sales reps can see only the visits they’re assigned to.
  5. Create the ProductItemAccess utility class.
    1. Select File | New | Apex Class.
    2. For the Apex class name, enter ProductItemAccess.
    3. Delete the auto-generated content and paste the following sample.
      1public class ProductItemAccess {
      2    
      3    public static void insertAccess(List<ProductItem> productItems, List<Id> users, String access) {
      4
      5        if(productItems==null || productItems.isEmpty() || users==null || users.isEmpty()) {
      6            return;
      7        }
      8        productItems = getProductItemsWithId(productItems);
      9        List<ProductItemShare> shareList = new List<ProductItemShare>();
      10        for(Id user: users) {
      11            if(user==null)
      12                continue;
      13            for(ProductItem item : productItems) {
      14                ProductItemShare share = New ProductItemShare();
      15                share.ParentId=item.Id;
      16                share.UserOrGroupID = user;
      17                share.AccessLevel = access;
      18                shareList.add(share);
      19            }
      20        }
      21        try{
      22            Database.insert(shareList);
      23        } catch (Exception e) {
      24            System.debug(e);
      25        }
      26    }
      27    
      28    public static void deleteAccess(List<ProductItem> productItems, List<Id> users) {
      29        List<ProductItemShare> shareList = new List<ProductItemShare>();
      30        for(Id userId : users) {
      31            for(ProductItem productItem : productItems) {
      32                List<ProductItemShare> shares = [select Id from ProductItemShare where ParentId = :productItem.Id and UserOrGroupID = :userId and RowCause='Manual'];
      33                if(!shares.isEmpty()) {
      34                    shareList.addAll(shares);
      35                }
      36            } 
      37        }
      38        delete shareList;
      39    }
      40    
      41    public static List<ProductItem> getProductItems(ProductFulfillmentLocation fulfillLocation) {
      42        return [select Id from ProductItem where Product2Id=:fulfillLocation.ProductId and LocationId=:fulfillLocation.FulfillmentLocationId];
      43    }
      44    
      45    private static List<ProductItem> getProductItemsWithId(List<ProductItem> productItems) {
      46        List<ProductItem> productItemsWithId = new List<ProductItem>();
      47        for(ProductItem item : productItems) {
      48            if(item==null)
      49                continue;
      50            if(item.id==null) {
      51                List<ProductItem> itemList = [select Id from ProductItem where Product2Id=:item.Product2Id and LocationId=:item.locationId];
      52                productItemsWithId.addAll(itemList);
      53            }
      54        }
      55        return productItemsWithId;
      56    }
      57}
    4. Select File | Save.
  6. Create the ProductFulfillmentLocationAccess utility class.
    1. Select File | New | Apex Class.
    2. For the Apex class name, enter ProductFulfillmentLocationAccess.
    3. Delete the auto-generated content and paste the following sample.
      1public class ProductFulfillmentLocationAccess {
      2    
      3    static Set<String> accessLevels = new Set<String>{'read', 'edit'};
      4    
      5     /**
      6     * Gives access to users on all product fulfillment locations
      7     */
      8    public static void insertAccessToAllLocations(ProductFulfillmentLocation locationToInsert, String accessType) {
      9        List<ProductFulfillmentLocation> fulfillLocations = [select Id, ProductId, FulfillmentLocationId from ProductFulfillmentLocation];
      10        insertAccess(fulfillLocations, getAllResponsibleUsers(), accessType);
      11        insertProductItemAccess(new List<ProductFulfillmentLocation>{locationToInsert}, new List<Id>{locationToInsert.UserId}, accessType);
      12    }
      13    
      14    public static void insertAccess(List<ProductFulfillmentLocation> fulfillLocations, List<Id> users, String accessType) {
      15        if(fulfillLocations==null || fulfillLocations.isEmpty() || users==null || users.isEmpty()) {
      16            return;
      17        }
      18        List<ProductFulfillmentLocationShare> shareList = new List<ProductFulfillmentLocationShare>();
      19        for(Id user: users) {
      20            if(user==null)
      21                continue;
      22            for(ProductFulfillmentLocation location : fulfillLocations) {
      23                if(location==null || location.Id==null)
      24                    continue;
      25                ProductFulfillmentLocationShare share = New ProductFulfillmentLocationShare();
      26                share.ParentId=location.Id;
      27                share.UserOrGroupID = user;
      28                share.AccessLevel = accessType;
      29                shareList.add(share);
      30            }
      31        }
      32        try{
      33            Database.insert(shareList);
      34        } catch (Exception e) {
      35            System.debug(e);
      36        }
      37    }
      38
      39    public static void updateAccess(ProductFulfillmentLocation oldLocation, ProductFulfillmentLocation updatedLocation, String access) {
      40         deleteAccess(oldLocation);
      41         insertAccessToAllLocations(updatedLocation, access);
      42    }
      43    
      44    public static void deleteAccess(List<Id> users) {
      45        List<ProductFulfillmentLocationShare> shareList = new List<ProductFulfillmentLocationShare>();
      46        for(Id userId : users) {
      47            if(isUserLinkedTofulfillLocation(userId)) 
      48                continue;
      49            List<ProductFulfillmentLocationShare> shares = [select Id from ProductFulfillmentLocationShare where UserOrGroupID = :userId and RowCause='Manual'];
      50            if(!shares.isEmpty()) {
      51                shareList.addAll(shares);
      52            }
      53        }
      54        delete shareList;
      55    }
      56    
      57    public static void deleteAccess(ProductFulfillmentLocation fulfillLocation) {
      58        deleteAccess(new List<Id>{fulfillLocation.UserId});
      59        deleteProductItemAccess(fulfillLocation);
      60    }
      61    
      62    public static Boolean isUserLinkedTofulfillLocation(Id userId) {
      63        List<ProductFulfillmentLocation> fulfillLocations = [select Id from ProductFulfillmentLocation where UserId = :userId];
      64        return !fulfillLocations.isEmpty();
      65    }
      66    
      67    public static void deleteProductItemAccess(ProductFulfillmentLocation fulfillLocation) {
      68       ProductItemAccess.deleteAccess(ProductItemAccess.getProductItems(fulfillLocation), new List<id>{fulfillLocation.UserId});
      69    }
      70
      71    public static void insertProductItemAccess(List<ProductFulfillmentLocation> fulfillLocations, List<Id> users, String access) {
      72        List<ProductItem> productItems = new List<ProductItem>();
      73        for(ProductFulfillmentLocation location : fulfillLocations) {
      74            productItems.add(new ProductItem(Product2Id=location.ProductId, LocationId=location.FulfillmentLocationId));
      75        }
      76        ProductItemAccess.insertAccess(productItems, users, access);
      77    }
      78    
      79    public static List<Id> getAllResponsibleUsers() {
      80        List<Id> users = new List<Id>();
      81        List<ProductFulfillmentLocation> fulfillLocations = [select UserId from ProductFulfillmentLocation];
      82        for(ProductFulfillmentLocation location : fulfillLocations) {
      83            users.add(location.UserId);
      84        }
      85        return users;
      86    }
      87}
    4. Select File | Save.
    Now sales reps can see only the inventories they’re responsible for.