Roll-Up Summary Fields With Lookup Relationships – Part 2
July 31st, 2009
In the first part of this post I outlined the issues involved with creating a trigger to do roll-up summaries with a lookup relationship. To solve the problem I did what you should always try to do first: let the Force.com platform do the heavy lifting for you. Don’t try to code around the platform when the declarative interface will do the trick 99% of the time. You’ll be amazed with the things you can do with the platform when you let it. The rule of thumb is to use the point-n-click Builder first and write code only as a last resort.
So to solve my problem I added another custom object called Shipment Item and created a Master-Detail(Shipment) field that would automatically provide the roll-up summaries. However, since users only interact with the Inventory Items on the shipment they never actually see or use this Shipment Items so there’s really no interaction with them; it’s simply for the roll-up summaries. I needed a way to allow users to manage the Inventory Items on the shipment but in the background add/move/remove Shipment Items to keep the number of records in synch with the Inventory Items. Here is the trigger that I wrote for the Inventory Items to implement this functionality:
Trigger ShipmentItemProcess.cls
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | /************************************************************************************** * Keeps inventory items and shipment items in sync. If an inventory item is on a * shipment, then it ensures that there is a corresponding shipment item record. When * an inventory item is removed from a shipment, it deletes the shipment item record. **************************************************************************************/ trigger ShipmentItemProcess on Inventory_Item__c (after update, after insert) { if (Trigger.isUpdate) { List<inventory_Item__c> invItemsToInsert = new List<inventory_Item__c>(); Map<id,ID> mapInvItemShipment = new Map<id,ID>(); Set<id> invItemIdsToUpdate = new Set<id>(); Set<id> invItemIdsToDelete = new Set<id>(); // figure out which ones need to be inserted, updated or deleted for (Integer i=0;i<trigger.new.size();i++) { // if the old is null and the new is NOT null, they are assigning to a shipment - INSERT RECORD if (Trigger.old[i].Shipment__c == null & Trigger.new[i].Shipment__c != null) { invItemsToInsert.add(Trigger.new[i]); // if old is NOT null and new is null, then they are removing from the shipment - DELETE RECORD } else if (Trigger.old[i].Shipment__c != null & Trigger.new[i].Shipment__c == null) { invItemIdsToDelete.add(Trigger.new[i].id); // if they are both NOT null the we need to switch the new one to another shipment } else if (Trigger.old[i].Shipment__c != null & Trigger.new[i].Shipment__c != null) { if (Trigger.old[i].Shipment__c != Trigger.new[i].Shipment__c) { mapInvItemShipment.put(Trigger.new[i].id,Trigger.new[i].Shipment__c); invItemIdsToUpdate.add(Trigger.new[i].id); } } } // insert the new shipment records if (invItemsToInsert.size() > 0) { List<shipment_Item__c> shipmentItemsToInsert = new List<shipment_Item__c>(); for (Inventory_Item__c item : invItemsToInsert) { Shipment_Item__c shipItem = new Shipment_Item__c(); shipItem.Name = item.Name; shipItem.Inventory_Item__c = item.Id; shipItem.Shipment__c = item.Shipment__c; shipmentItemsToInsert.add(shipItem); } insert shipmentItemsToInsert; } // delete the corresponding shipment items that were removed if (invItemIdsToDelete.size() > 0) delete [select id from Shipment_Item__c where Inventory_Item__c IN :invItemIdsToDelete]; // update ones that have been moved to another shipment if (invItemIdsToUpdate.size() > 0) { List<shipment_Item__c> itemsToUpdate = [select Id, Inventory_Item__c, Shipment__c from Shipment_Item__c where Inventory_Item__c IN :invItemIdsToUpdate]; for (Shipment_Item__c item : itemsToUpdate) item.Shipment__c = mapInvItemShipment.get(item.Inventory_Item__c); update itemsToUpdate; } } else { List<shipment_Item__c> shipmentItemsToInsert = new List<shipment_Item__c>(); for (Inventory_Item__c item : Trigger.new) { if (item.Shipment__c != null) { Shipment_Item__c shipItem = new Shipment_Item__c(); shipItem.Name = item.Name; shipItem.Inventory_Item__c = item.Id; shipItem.Shipment__c = item.Shipment__c; shipmentItemsToInsert.add(shipItem); } } if (shipmentItemsToInsert.size() > 0) insert shipmentItemsToInsert; } } |
My test class looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | @isTest private class Test_ShipmentItemProcess { static ID createShipment(String shipmentName) { Shipment__c shipment = new Shipment__c( name = shipmentName ); insert shipment; return shipment.id; } static Inventory_Item__c createInventoryItem(String itemName) { Inventory_Item__c item = new Inventory_Item__c( name = itemName ); insert item; return item; } static testMethod void testInsertAndDelete() { ID ship1Id = createShipment('Shipment 1'); Inventory_Item__c item1 = createInventoryItem('LP100'); // update the item with the new shipment number item1.Shipment__c = ship1Id; update item1; // fetch the newly created shipment item and make sure it was created correctly Shipment_Item__c shipItem1 = [select Id,Shipment__c,Inventory_Item__c from Shipment_Item__c where Inventory_Item__c = :item1.id]; System.assertEquals(item1.id,shipItem1.Inventory_Item__c); System.assertEquals(item1.Shipment__c,shipItem1.Shipment__c); // now delete the shipment item1.Shipment__c = null; update item1; // make sure there are no records System.assertEquals(0,[select count() from Shipment_Item__c where Inventory_Item__c = :item1.id]); } static testMethod void testChangeShipment() { ID ship1Id = createShipment('Shipment 1'); Inventory_Item__c item = new Inventory_Item__c( name = 'My LP', Shipment__c = ship1Id ); insert item; Shipment_Item__c si = new Shipment_Item__c(); si.Inventory_Item__c = item.id; si.Name = 'My LP'; si.Shipment__c = ship1Id; insert si; ID ship2Id = createShipment('Shipment 2'); item.Shipment__c = ship2Id; update item; // fetch the newly created shipment item and make sure it was created correctly Shipment_Item__c shipItem2 = [select Id,Shipment__c,Inventory_Item__c from Shipment_Item__c where Inventory_Item__c = :item.id]; System.assertEquals(ship2Id,shipItem2.Shipment__c); } } |
Related posts:
- Roll-Up Summary Fields with Lookup Relationships – Part 1
- Salesforce.com Critical Update – Workflow Rule and Roll-Up Summary Field Evaluations Up
- Enhancing the Lead Conversion Process in Salesforce.com
- Programmatically Creating Sharing Rules with Apex
- Relationship Lookup Objects in Triggers are NULL?
Categories: Apex, Salesforce




Will the better solution be to use batch apex once it goes GA?
Definitely!