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);
}
} |
Categories: Apex, Code Sample, Salesforce













Will the better solution be to use batch apex once it goes GA?
Definitely!
Hi Jeff,
Is line #64 correct? Was wondering if Master-Detail relationship allowed you to do an update?
@Dakang, this code is not using a M:D relationship, just a simple lookup relationship so you can reparent the record.
What happens if Inventory item is deleted or undeleted
As of right now nothing. We do not delete inventory items in the system.