I wrote the following code at the Admin To Hero App Building Workshop and it was very popular. I think we used the code on 3 or 4 projects that day so I thought I'd post it to help everyone out.
Essentially it is a Visualforce page and Apex Controller that allows you to do a "deep clone" of an object and it's line items for a master-detail relationship. So I created a "Clone with Items" custom button on a page layout that invokes the Visualforce page that clones a purchase order header and its line items.
PurchaseOrderClone - Visualforce page
<apex:page standardController="Purchase_Order__c"
extensions="PurchaseOrderCloneWithItemsController"
action="{!cloneWithItems}">
<apex:pageMessages />
</apex:page>
PurchaseOrderCloneWithItemsController - Apex controller
public class PurchaseOrderCloneWithItemsController {
//added an instance varaible for the standard controller
private ApexPages.StandardController controller {get; set;}
// add the instance for the variables being passed by id on the url
private Purchase_Order__c po {get;set;}
// set the id of the record that is created -- ONLY USED BY THE TEST CLASS
public ID newRecordId {get;set;}
// initialize the controller
public PurchaseOrderCloneWithItemsController(ApexPages.StandardController controller) {
//initialize the stanrdard controller
this.controller = controller;
// load the current record
po = (Purchase_Order__c)controller.getRecord();
}
// method called from the VF's action attribute to clone the po
public PageReference cloneWithItems() {
// setup the save point for rollback
Savepoint sp = Database.setSavepoint();
Purchase_Order__c newPO;
try {
//copy the purchase order - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
po = [select Id, Name, Ship_To__c, PO_Number__c, Supplier__c, Supplier_Contact__c, Date_Needed__c, Status__c, Type_of_Purchase__c, Terms__c, Shipping__c, Discount__c from Purchase_Order__c where id = :po.id];
newPO = po.clone(false);
insert newPO;
// set the id of the new po created for testing
newRecordId = newPO.id;
// copy over the line items - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
List<Purchased_Item__c> items = new List<Purchased_Item__c>();
for (Purchased_Item__c pi : [Select p.Id, p.Unit_Price__c, p.Quantity__c, p.Memo__c, p.Description__c From Purchased_Item__c p where Purchase_Order__c = :po.id]) {
Purchased_Item__c newPI = pi.clone(false);
newPI.Purchase_Order__c = newPO.id;
items.add(newPI);
}
insert items;
} catch (Exception e){
// roll everything back in case of error
Database.rollback(sp);
ApexPages.addMessages(e);
return null;
}
return new PageReference('/'+newPO.id+'/e?retURL=%2F'+newPO.id);
}
}
TestPurchaseOrderCloneWithController - Test class
@isTest
private class TestPurchaseOrderCloneWithController {
static testMethod void testPOCloneController() {
// setup a reference to the page the controller is expecting with the parameters
PageReference pref = Page.PurchaseOrderClone;
Test.setCurrentPage(pref);
// setup a ship to account
Account shipTo = new Account();
shipTo.Name = 'PSAV 6FOO';
shipTo.Type = 'Supplier';
insert shipTo;
// create new po record
Purchase_Order__c po = new Purchase_Order__c();
po.Date_Needed__c = Date.newInstance(2020,01,01);
po.Ship_To__c = shipTo.id;
insert po;
// create a line item for the po
Purchased_Item__c pi1 = new Purchased_Item__c();
pi1.Description__c = 'My item';
pi1.Purchase_Order__c = po.id;
pi1.Quantity__c = 1;
pi1.Unit_Price__c = 10;
insert pi1;
// Construct the standard controller
ApexPages.StandardController con = new ApexPages.StandardController(po);
// create the controller
PurchaseOrderCloneWithItemsController ext = new PurchaseOrderCloneWithItemsController(con);
// Switch to test context
Test.startTest();
// call the cloneWithItems method
PageReference ref = ext.cloneWithItems();
// create the matching page reference
PageReference redir = new PageReference('/'+ext.newRecordId+'/e?retURL=%2F'+ext.newRecordId);
// make sure the user is sent to the correct url
System.assertEquals(ref.getUrl(),redir.getUrl());
// check that the new po was created successfully
Purchase_Order__c newPO = [select id from Purchase_Order__c where id = :ext.newRecordId];
System.assertNotEquals(newPO, null);
// check that the line item was created
List<Purchased_Item__c> newItems = [Select p.Id From Purchased_Item__c p where Purchase_Order__c = :newPO.id];
System.assertEquals(newItems.size(),1);
// Switch back to runtime context
Test.stopTest();
}
}