function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
michelemcgeoy.ax1914michelemcgeoy.ax1914 

error cloning campaign member status

I am trying to Clone a campaign with all it's members. I am successful in cloning the campaign and all of it's members, but when I try and close the MemberStatus object i get:
error: Insert failed. First exception on row 0; first error: DUPLICATE_VALUE, duplicate value found: <unknown> duplicates value on record with id: <unknown>: [] Error is in expression '{!insertRecord}' in component <apex:page> in page cloneclassattendance

I'm very new to Apex, so sorry if this seems like a basic question. Is there a way that I can write the contents of the list to an external table so I can see the data and try and trace the error?

Here is the code below:

public class CloneClassAttendance {
    private final Campaign camp;
   
    public CloneClassAttendance(ApexPages.StandardController stdController){
        this.camp = (Campaign)stdController.getRecord();
    }

   public void insertRecord(){
        // TODO - make sure Start date and DOW are same
        //If (DAY_IN_WEEK(Camp.startdate) = 2) {
        // do something
        //} else {
        // do nothing
        //}

        Date NewDate = Camp.Startdate;
        while (NewDate <= camp.enddate) { 
            // Clone the campaign record
            Campaign NewCamp = new Campaign(Name = 'Test Campaign record');
            insert NewCamp;
            // If you add new fields that you want to clone, you also have to add to the page. Not sure why...
            Campaign NewCamp1 = [SELECT Id, Type, Status FROM Campaign WHERE Name = 'Test Campaign record'];
            NewCamp1.Type = 'Class Attendance';
            NewCamp1.IsActive = true;     
            NewCamp1.Name = camp.name + +'-'+NewDate.format();
            NewCamp1.Parentid = camp.id;
            NewCamp1.Startdate = NewDate;
            NewCamp1.enddate = NewDate;
            NewCamp1.teacher__c = camp.teacher__c;
            NewCamp1.Day_of_Week__c = camp.Day_of_Week__c;
            NewCamp1.Hours_Per_Class__c = camp.Hours_Per_Class__c;
            NewCamp1.Location__c = camp.Location__c;
            NewCamp1.Start_Time__c = camp.Start_Time__c;
            NewCamp1.End_Time__c = camp.End_Time__c;
            NewCamp1.Description = camp.Description;
            NewCamp1.Waitlist__c = camp.Waitlist__c;
            NewCamp1.Max_Capacity__c = camp.Max_Capacity__c;
            update NewCamp1;
           
            // Clone all records in CampaignMemberStatus
            // Select records in CampaignMemberStatus from new campaign
            List<CampaignMemberStatus> MembersStatus = [SELECT id, campaignid, label, SortOrder, HASRESPONDED,isDefault
            from CampaignMemberStatus where campaignid = :camp.id ];
            List<CampaignMemberStatus> lstCampMemberStatus = new List<CampaignMemberStatus>();
            // Change campaign ids to the newly cloned campaign id
            for(CampaignMemberStatus ms : MembersStatus){
                CampaignMemberStatus campMembStatus = ms.clone(false, true);
                campMembStatus.Campaignid = Newcamp1.id;
                lstCampMemberStatus.add(campMembStatus);
            }

            if(lstCampMemberStatus.size() > 0)
            // This is where error occurs. Newcamp1.id should be set to the campaign that was just added. It seems to work fine below. But the error
            // says "duplicates value on record with id: <unknown>"
            insert lstCampMemberStatus;
                

            // Clone all records in CampaignMember
            // Select records in CampaignMember from new campaign
            //Create list of members for new campaign above.
            //Code below tested and works properly

            List<CampaignMember> Members = [SELECT id, contactid,status from CampaignMember where campaignid = :camp.id ];
            List<CampaignMember> lstCampMember = new List<CampaignMember>();
            // Change campaign ids to the newly cloned campaign id
            for(CampaignMember m : Members){
            CampaignMember campMemb = m.clone(false, true);
            campMemb.Campaignid = Newcamp1.id;
            lstCampMember.add(campMemb);
            }

            if(lstCampMember.size() > 0)
            insert lstCampMember;

            Newdate = NewDate + 7;  

        }
    }  
}

Thanks, Michele
GlynAGlynA
Michelle,

I don't have a good explanation for why your code is getting the error it's getting.  However, I offer you the following code, which eliminates the use of "clone()", and which reduces your total number of DML statements to 3, instead of 4 per cloned campaign.  I also included a "dayOfWeek()" method to use for the check you commented out in your routine.

I can tell you why you need to add fields to the page, however.  The standard controller for an object will only query fields referenced in the page - the system scans the VF to figure out which fields to query.  You don't have to display the fields.  They can be in a section that's hidden (although it must be rendered).  The alternative is to re-query the record in your controller extension using the Id from the record that the standard controller has; but then you have to manage two copies of the record (the one displayed on the page and the one with the extra fields) and it's easy to introduce errors.

<pre>
public class CloneClassAttendance
{
    private final Campaign theCampaign;

    public CloneClassAttendance( ApexPages.StandardController stdController )
    {
        theCampaign = (Campaign)stdController.getRecord();
    }

    //  returns the day-of-week for the given date in 2001 - 2099
    //  this works because 2001 started on a Monday
    //  0 = Sunday, ... 6 = Saturday
    private static Integer dayOfWeek( Date theDate )
    {
        Integer theYear = theDate.year() - 2001;
        return Math.mod( theYear + theYear/4 + theDate.dayOfYear(), 7 );
    }

    public void insertRecord()
    {
        Map<Date,Campaign> map_newCampaigns = new Map<Date,Campaign>();

        Date newDate = theCampaign.StartDate;
        while ( newDate <= theCampaign.EndDate )
        {
            map_newCampaigns.put
            (   newDate,
                new Campaign
                (   Type                = 'Class Attendance',
                    IsActive            = true,
                    StartDate           = newDate,
                    EndDate             = newDate,
                    ParentId            = theCampaign.Id,
                    Name                = theCampaign.Name + '-' + newDate.format(),
                    Description         = theCampaign.Description,
                    Teacher__c          = theCampaign.Teacher__c,
                    Day_of_Week__c      = theCampaign.Day_of_Week__c,
                    Hours_Per_Class__c  = theCampaign.Hours_Per_Class__c,
                    Location__c         = theCampaign.Location__c,
                    Start_Time__c       = theCampaign.Start_Time__c,
                    End_Time__c         = theCampaign.End_Time__c,
                    Waitlist__c         = theCampaign.Waitlist__c,
                    Max_Capacity__c     = theCampaign.Max_Capacity__c
                )
            );
            newDate = newDate.addDays( 7 );
        }
        insert map_newCampaigns.values();

        List<CampaignMemberStatus>  list_newCampaignMemberStatus    = new List<CampaignMemberStatus>();
        List<CampaignMember>        list_newCampaignMembers         = new List<CampaignMember>();

        Date newDate = theCampaign.StartDate;
        while ( newDate <= theCampaign.EndDate )
        {
            Campaign newCampaign = map_newCampaigns.get( newDate );

            for ( CampaignMemberStatus status :
                [   SELECT  Id, HasResponded, IsDefault, Label, SortOrder
                    FROM    CampaignMemberStatus
                    WHERE   (   CampaignId = :theCampaign.Id
                            AND IsDeleted = false
                            )
                ]
                )
            {
                list_newCampaignMemberStatus.add
                (   new CampaignMemberStatus
                    (   CampaignId      = newCampaign.Id,
                        HasResponded    = status.HasResponded,
                        IsDefault       = status.IsDefault,
                        Label           = status.Label,
                        SortOrder       = status.SortOrder
                    )
                );
            }

            for ( CampaignMember member :
                [   SELECT  Id, ContactId, Status
                    FROM    CampaignMember
                    WHERE   CampaignId = :theCampaign.Id
                ]
                )
            {
                list_newCampaignMembers.add
                (   new CampaignMember
                    (   CampaignId      = newCampaign.Id,
                        ContactId       = member.ContactId,
                        Status          = member.Status
                    )
                );
            }

            newDate = newDate.addDays( 7 );
        }

        insert list_newCampaignMemberStatus;
        insert list_newCampaignMembers;
    }
}
</pre>

I apologize for any typos that you might have to fix.  I couldn't compile this, as I don't have your custom fields.  Let me know if you have any questions.

Glyn Anderson
Sr Developer | System Analyst | ClosedWon | closedwon.com
Certified Developer | Certified Advanced Administrator
GlynAGlynA
Michelle,

I found one typo already - Line 53 should not re-declare the "newDate" variable, it should just assign to it.  Change line 53 to read:

newDate = theCampaign.StartDate;

-Glyn
michelemcgeoy.ax1914michelemcgeoy.ax1914
Hi Glyn:
Thanks for your help. I tried your code with this fix and get the error: Insert failed. First exception on row 0; first error: DUPLICATE_VALUE, duplicate value found: <unknown> duplicates value on record with id: <unknown>: []

Is there a way to write the contents of  list_newCampaignMemberStatus and list_newCampaignMembers to an external file so I can see where this duplicate may be occurring?

Thanks, Michele
GlynAGlynA
Michele,

I'm wondering if there are duplicate CampaignMemberStatus values on one of your Campaigns.  You can add a Debug statement in the CampaignMemberStatus loop (@ line 67 in my code above):

System.debug( 'CampaignMemberStatus:  Label = ' + status.Label + ', SortOrder = ' + status.SortOrder );

Look at your debug log and find these lines (in Developer Console you can check the "Debug Only" box).  You can determine if there are any duplicate labels or sort order values.  Either of these might be causing the error.

Let me know how it goes.  I'll keep checking back here to see if I can help further.

-Glyn
michelemcgeoy.ax1914michelemcgeoy.ax1914
Hi Glyn:
I'm pretty new to the developer console, so I may not be using it properly.
I added your line 67 and then pasted your code into the "Execute Anonymous" and clicked "Execute" and I get the following error: "Only top-level class methods can be declared static"

If I highlight the insertRecord method and click "Execute Highlighted" I get the error: "Variable does not exist: theCampaign.StartDate"

What am I doing wrong?

Thanks, Michele
GlynAGlynA
You probably can't execute this in the Execute Anonymous window.  Rather, go to Setup | Monitoring | Debug Logs and enable debug logs for yourself.  Then access the VF page for which this is the controller.  Go back to the debug logs list and View the debug log that was generated.  Then you can search for lines that include "CampaignMemberStatus: ".

Or, instead of opening the debug log from the debug log list, you can open developer console.  Your debug log should appear in the "Logs" tab and you can open the log there inside developer console.  There is a checkbox labeled "Debug Only".  Checking this will filter the log so you only see lines with debug output in them.  This is handy because then all the debug lines appear grouped together - easier than searching for them one at a time the other way. 
michelemcgeoy.ax1914michelemcgeoy.ax1914
It turns out that the error is occuring because when I clone the campaign it is adding 2 records in CampaignMemberStatus for Sent and Received with Sort Order 1 and 2, even though these record do not exist for the campaign being cloned.
As a work around I changed line 74 to "SortOrder       = status.SortOrder +2"
The code now works properly ... until I try and run it to clone a course for a year, then I run into the Governor limit and get the error:
"Too many SOQL queries: 101"
The help files suggest not putting any SOQL statements inside a FOR loop.
Any ideas how I would restructure the code so that this is the case?

Thanks, Michele