As far as I know it's not possible to meet all your requirements. Specifically the one about keeping the same Id / URL. I'd love to be proved wrong :)
You'll have to insert a new Account with right record type and link/reparent all related data, then eventually delete the old PA. Maybe you could defer the delete and create a new field "click here to go to the new record"...
We can't use merge
statement on a mix of business and person accounts.
The actual process is not too complex. I'll be describing it as for one record, it's up to you to maybe make a batch job out of it, maybe create a button to convert one at a time.
Things to consider beforehand:
- ask Salesforce.com support to make the audit fields like CreatedDate, CreatedById editable on Account and Contact. They'll most likely grant it for some time and you can write to these fields only during the insert, not during updates.
- identify reports and report types that have
Person...
or ...__pc
in them (I think easiest would be to download them all to Eclipse IDE and run a full search - Ctrl+H). You might have to recreate them for business-only version, you might be up for some explanations if data suddenly "disappears" from certain dashboards.
- If you're using Leads & converting them you need to deal with the fact that
ConvertedAccountId
on the lead will point to old record (and I think converted lead becomes readonly afterwards).
- Similarly - you'll lose the field history tracked to date. And Chatter data - if you have it enabled. And approval process history if you were approving Accounts.
- If you want to convert only some of them and not all - create a checkbox field or something to identify them & fill it in. Optionally add also a lookup field that will point to new, replacement Acc.
- Try to identify validation rules & triggers that might prevent you from changing lookup to Account on objects. Add to them some kind of exception like
&& $Profile.Name != 'System Administrator'
so they won't fire and cause problems.
- If you have Master-Detail links between Account and custom objects - make sure they have "reparentable" checkbox ticked.
- Prepare some reconciliation reports - how many Opportunities/Cases/... are owned by Business vs. PA accounts. You'll need to monitor the numbers and make sure the grand total stays.
Create a new Account
Prepare a query that fetches most of the standard and __c
Account fields. We could try writing a dynamic way of fetching all the fields that match the condition but I think it's better to be done by hand (you'll want to skip the SystemModStamp, isDeleted, RecordTypeId, maybe you use Jigsaw / Data.com...). And clone it.
Account replacement = [SELECT Id, Name, ...
FROM Account
WHERE isPersonAccount = true
AND To_be_converted__c = true AND Replacement__c = null /* depends if you'll want such fields, kind of extra protection */
AND Id = :originalId
FROM Account].clone(false,true,true,true); //http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_System_SObject_clone.htm
replacement.RecordTypeId = '...';
replacement.PersonContactID = null;
insert replacement;
Copy the Contact
This should be straightforward enough as there can be only one under the PA.
Contact repContact = [SELECT FirstName, LastName, ...
FROM Contact
WHERE isPersonAccount = true AND AccountId = :originalId].clone(false,true,true,true);
repContact.AccountId = replacement.Id;
insert repContact;
Fix all references
First we need to learn how many objects have a lookup to Account before we can fix them. You could have a look at the "child relationships" in Eclipse or Real Force Explorer:

Or you could use code to fetch them all:
for(Schema.ChildRelationship rel : Account.sObjectType.getDescribe().getChildRelationships()){
System.debug('SELECT Id FROM ' + rel.getChildSObject() + ' WHERE ' + rel.getField() + ' = ...');
} // It's tempting to use Database.query() here
SELECT Id FROM Account WHERE ParentId = ...
SELECT Id FROM AccountContactRole WHERE AccountId = ...
SELECT Id FROM AccountFeed WHERE ParentId = ...
SELECT Id FROM AccountHistory WHERE AccountId = ...
SELECT Id FROM AccountPartner WHERE AccountFromId = ...
SELECT Id FROM AccountPartner WHERE AccountToId = ...
SELECT Id FROM AccountShare WHERE AccountId = ...
(...)
SELECT Id FROM Asset WHERE AccountId = ...
SELECT Id FROM Attachment WHERE ParentId = ...
SELECT Id FROM Case WHERE AccountId = ...
(...)
Some won't be applicable (Acc Contact Roles for example). Some stuff you'll have to write-off like history entries. Most will be easy to just fetch all and update the AccountId. Those that can't be reparented - could be queried, deep cloned and inserted back with new Account Id.
Cleanup
Check sharing rules? Check the manual sharing entries (if any). Remove the hacks from validation rules if you've made the exceptions for sysadmins. And once it looks good - delete the original Person Accounts.
Best Answer
When we created our org we added a Record Type called "Business Accout" and then requested Person Accounts be activated. You don't have to assign the Record Type to any of your profiles so I would just go ahead and create it. I'm pretty sure you can delete it afterward, if you wanted.