+ Start a Discussion
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student 

Automatically set picklist

Hi there,

 

I am very new to Apex triggers....so new that this is the first one I have attempted that isn't from a workbook. Everyone has to start somewhere though I guess.

 

I assume that this will be quite simple and so I will describe my situation as best I can and hopefully, someone will have an idea on how I can solve my problem.

 

I have a custom object (Destiny products and services), this custom object has a lookup field to another custom object (product). When creating a new 'destiny product and service" I have made formula fields which autopopulate fields from the Product object, relating to which product the record creator chose from the Product lookup field.

 

This has all worked fine as I just needed a simple formula field, however the products and services have differing stages, types, etc depending on the product or service chosen. 

 

I attempted to accomplish this by creating a picklist field within each custom object which states the names of all the products or services, I then created dependant picklists in relation to what choice was made in the name picklist and then made this hidden. 

 

I want to make this as simple as possible for the user and so I intend to make it so that when the lookup field is selected, it will autopopulate the name picklist so it is the same as the lookup field and then the Destiny product or services record will have stages and type which will be related to the product chosen. 

 

I need to write an apex trigger which will set the picklist value in the products and services object, to be the same as the picklist option from the record chosen in the lookup field (product object).

 

Any help would be much appreciate, even advice on alternative and perhaps easier ways that this can be accomplished. I am sorry if this is difficult to understand.

 

Mikie

Best Answer chosen by Admin (Salesforce Developers) 
JayNicJayNic

I forgot about the silliy limitations on the picklists for field updates... You would have to make one rule for each picklist value... This is how I do it for clients, because then they have the opportunity to update it themselves when they change values, without having to call me (as my rates are crazy! :p)

 

The most versatile way to do this in a trigger would be as follows:

1) Create a new formula field on the Destiny_Products_and_Services__c object called ProductName__c.

  The formula should pull over "Product_or_Service_Name__r.Product_Name__c"

2) Create a trigger with the following code

 

trigger destiny_bIbU on Destiny_Products_and_Services__c (before insert, before update){
/*PRocesses:
- Set the product hidden pickist to be to the ProductName so we can use dependant picklists
*/
	for(Destiny_Products_and_Services__c d : trigger.new) {
		d.Product_or_Service_hidden_picklist__c = d.ProductName__c;
	}
}
 

 

And you're done

All Answers

JayNicJayNic

I'm having trouble following your description. Can you use field names, and descriptions?

 

So you have DestinyProductsAndServices__c (Destiny object) which has a lookup to Product__c (and to Service__c ?)

 

 

You have created a ProductStage__c picklist, and a ServiceStage__c picklist on DestinyProductsAndServices__c. Each of these contains the same values as Product__c.Stage__c, or Service__c.Stage__c respectively.

 

The values of the Stage picklists on Destiny object are populated (presumably by workflow field update) to sync with their respective parent. 

 

After this I get lost.

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Certainly, I am sorry this is also my first post.

 

Destiny_Products_and_Services__c = custom object API name

 

Product_or_Service_hidden_picklist__c = Picklist field API name

 

Products__c = custom object API name

 

Product_Name__c = custom picklist field API name

 

These are the only ones that really matter. There is no service object as of yet. Destiny products and services has a lookup field for the products object which be chosen when creating the record. This serves the purpose of allowing multiple formula fields to auto-populate with information from the products object.

 

E.G. If soeone were to choose ádvantage program' in the lookup field, the record created will have description, price, etc all autopopulated from information stored in the products oject.

 

Description of objects: Destiny products and services is designed to keep track of the products in relation to the client, the products object is for information to do with the product and services (which is currently only around 6 products and services).

 

The two picklists fields lsited above have the exact same values, these values are the names of the products. I have set the picklist on each of the records in the products object so that they are the same as the name of the product. E.G. On the advantage program record, the picklist value will be set to advantage program.

 

When someone is creating a record the goal is that: After selecting what product the client wants to purchase from the  lookup, the hidden product name picklist will be chosen to be the same as the product chosen from the lookup. Except as a picklist, it will have other visable picklists which are dependant upon it and therefore the stages and type, etc will change accordingly.

 

I hope this was explained better. :)

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

P.S. the lookup field is called: Product_or_Service_Name__c

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

P.S. the lookup field is called: Product_or_Service_Name__c

JayNicJayNic

I see.. Then if I am understanding you correctly: you won't need a trigger at all.

 

You can create a workflow rule on Destiny object that runs on change of Product:

 

Name: "Destiny - New, with product"
Description: "When a record is created, and it has a product" Criteria: "Only when a record is created" formula: NOT(ISBLANK(Product__c))

 Then create a field update within the WF rule:

Name: "Destiny - Product Name = Name of Product"
Description: "Changes the Product Name picklist field to the Product lookups product name field"
field to update: "Product_or_Service_hidden_picklist__c"
formula:
Product_or_Service_Name__r.Product_Name__c

 You can probably just call it Product_or_Service_Name__c as that is more accurate to what it is. It's name will confuse people when making reports, and developers when building functionality.

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Hey,

 

I followed the instructions exactly, but when it came to the field update there is no place in which to put it in. As its a picklist, the only options are to chooose a pre-defined value or to move the picklist choice up or down.

 

Maybe I am missing something?

 

Thankyou so much for your time and help by the way

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

It also needs to be a picklist so I can set the other picklist fields to be dependant off it and therefore each product will have unique stages.

 

Mikie

JayNicJayNic

I forgot about the silliy limitations on the picklists for field updates... You would have to make one rule for each picklist value... This is how I do it for clients, because then they have the opportunity to update it themselves when they change values, without having to call me (as my rates are crazy! :p)

 

The most versatile way to do this in a trigger would be as follows:

1) Create a new formula field on the Destiny_Products_and_Services__c object called ProductName__c.

  The formula should pull over "Product_or_Service_Name__r.Product_Name__c"

2) Create a trigger with the following code

 

trigger destiny_bIbU on Destiny_Products_and_Services__c (before insert, before update){
/*PRocesses:
- Set the product hidden pickist to be to the ProductName so we can use dependant picklists
*/
	for(Destiny_Products_and_Services__c d : trigger.new) {
		d.Product_or_Service_hidden_picklist__c = d.ProductName__c;
	}
}
 

 

And you're done

This was selected as the best answer
Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

I am now getting the error:

 

Error: Field Product_Name__c is a picklist field. Picklist fields are only supported in certain functions

 

Does this mean that it cannot be done?

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

I tried putting down the lookup field to the product, except when saved, the record just shows the URL ID number thing.

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I found a way around this. I wrote another formula field which uses an if statement to read the name of the product

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Just going to attempt the trigger name. I have checked it and the ProductName field and productName2 field both work. When a product is chosen from thelookup...the name is replicated in the ProductName2 field...I feel like I am close now...

JayNicJayNic

You don't need any other formula fields other than the ProductName__c one I mentioned before.

 

Change the formula to 'TEXT(Product_or_service_name__r.Product_Name__c)'

 

Then remove the unecescary fields. 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Will do.

 

I will delete the redundant field now. Once again thankyou soo much. I have entered the trigger into sandbox environment and then deployed it to my production version. One final question before i accept the inbound change set into my production version. Firstly I jsut want to say that I am very new to apex triggers and do not full understand them as of yet. Do i need to test the code in anyway before deploying it....If so how would I do this? Short of writing an apex test code?

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Once again, I am very, very grateful for all your help. 

JayNicJayNic

You're welcome!

 

Yes, you will need to write a test class in order to deploy it. Triggers require at least 75% code coverage for the trigger itself, as well as at least 75% global coverage in the organisation to deploy to...

 

Thankfully, this is a very simple trigger to test for. 

 

Apex testing is a whole other kettle of fish that many amatuer coders tend to underestimate the value of.

 

Poorly architected tests can cost huge amounts of man hours as your organization grows. As a consultant, I commonly have to augment client applications that are made up from other consultants, or the clients themselves. Sadly, most of the time, it is not done correctly, and the man hours I burn fixing improper code, and ensuring test coverage must be passed on to the client to pay for.

 

Added to that: the vast majority of free sample code out there has test classes that are poorly architected... Here is how I would cover this code:

1) Create a class that will store your test object records. From what I know of your system, you will need at least two records. The class would look as follows:

public without sharing class TESThelper {

/*
Use this class to help create static objects in Test classes...
DO NOT create records inline in test classes

NOTE all properties are alphabetical by object
*/

	//A simple Destiny_Products_and_Services__c
    public static Destiny_Products_and_Services__c  testDestiny00 {
        get {
            if(testDestiny00 == null) {
                testDestiny00  = new Destiny_Products_and_Services__c(
                    name = 'testDestiny00',
					Product_or_Service__c = testProduct00.id
                );
                insert testDestiny00 ;
            }
            return testDestiny00 ;
        }
    } 
	
	//A simple product
	public static Product__c testProduct00{
        get {
            if(testProduct00 == null) {
                testProduct00  = new Product__c(
                    name = 'testProduct00',
					Product_Name__c = 'EXAMPLE PRODUCT NAME'
                );
                insert testProduct00;
            }
            return testProduct00;
        }
	}

}

 Note my naming convention. I keep all test and test related classes prefixed with the name 'TEST' so that they're easier to view and sort etc...

This class will grow as your org grows with tests. Anytime you need to create records to cover apex code, those records definition should be put in here in the same fashion as the examples i've placed.

NOTE: that if you have some required fields that I don't know about, you would need to add those to these two records as well. This is just a framework.

 

By creating all objects in once place, and havint your test classes reference all the same objects, you avoid having to recode every single test class when you make a change to a business rule.

 

Next, create your test class to cover the trigger:

@isTest
private class TESTdestiny_bIbU {
    static testMethod void myUnitTest() {
        Destiny_Products_and_Services__c d = TESThelper.testDestiny00;
    }
}

 That's easy. We know it's covered because all the trigger does is set a value from the record in one field to another field. So there is really nothing else to do. this should get you to 100% coverage.

 

Again, notice the naming convention: TEST then I put the name of the trigger I'me testing against. This way, there are no questions as to what this test is supposed to accomplish, because it is defined by the very trigger process it covers.

 

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I feel like I have made progress. I have been using FORCE.com IDE as my sandbox inbound change sets are not working....Either way, I have two clases and one trigger in my project. Not one of them can be saved to the server because of these three reasons:

 

Save Error: Invalid type: product__c      , I assumed this was because the actual API name for the object was products__c. So I changed it and the text became blue, I assume this is a good sign...But the error remains.

 

Save Error: variable does not exist: TESThelper.testDestiny00

 

Test coverage of selected apex trigger is 0%, atleast 1% test coverage.......

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Randomly after clicking save on TESThelper for the 11th time, the error finally went away...but it was replaced by another two errors that say:

 

Save error: Unable to perform save on all files: com.salesforce.ide.api.metadata.types.Metadata$JaxbAccessorF_fullName cannot be cast to com.sun.xml.internal.bind.v2.runtime.reflect.Accessor TESThelper.cls-meta.xml /MikiesDellComputerProjects/src/classes line 1 Force.com save problem

 

I know I am being a pain npw, but it feels so close I can almost taste success.

 

Thankyou again

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Randomly after clicking save on TESThelper for the 11th time, the error finally went away...but it was replaced by another two errors that say:

 

Save error: Unable to perform save on all files: com.salesforce.ide.api.metadata.types.Metadata$JaxbAccessorF_fullName cannot be cast to com.sun.xml.internal.bind.v2.runtime.reflect.Accessor TESThelper.cls-meta.xml /MikiesDellComputerProjects/src/classes line 1 Force.com save proble

 

I know I am being a pain npw, but it feels so close I can almost taste success.

 

Thankyou again

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

I am so sorry I do not understand this at all....now there is three errors again....

 

The third is still as a result of the trigger not being tested, but the other two are:

 

Description Resource Path Location Type
Save error: Field is not writeable: Destiny_Products_and_Services__c.Name TESThelper.cls /MikiesDellComputerProjects/src/classes line 15 Force.com save problem

 

Description Resource Path Location Type
Save error: Variable does not exist: TESThelper.testDestiny00 TESTdestiny_bIbU.cls /MikiesDellComputerProjects/src/classes line 25 Force.com save problem

 

 

My only idea is that.....it is saying that Destiny_products_and_services__.Name is an autonumber and therefore it cannot be "testDestiny00"

 

The second error is then because TESThelper cannot create the class?

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

One hurdle over....I Changed the autonumber to just text and now the Test class has saved.

 

I still have three errors:

 

Description Resource Path Location Type
Average test coverage across all Apex Classes and Triggers is 70%, at least 75% test coverage is required. MikiesDellComputerProjects line 1 Force.com code coverage warning

 

Description Resource Path Location Type
Save error: Variable does not exist: TESThelper.testDestiny00 TESTdestiny_bIbU.cls /MikiesDellComputerProjects/src/classes line 25 Force.com save problem

 

Description Resource Path Location Type
Test coverage of selected Apex Trigger is 0%, at least 1% test coverage is required destiny_bIbU.trigger /MikiesDellComputerProjects/src/triggers line 1 Force.com code coverage warning

 

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Maybe...it is because Destiny products and services is a child to accounts object?

JayNicJayNic

You can change the object name back to an auto nuber if you like. If there is no reason for users to enter a specific name, then leave it as an auto number, and remove the line in the TESThelper class that assigns a value to the name.

 

Using the IDE is pretty advanced. If you can use change sets, they're easier to handle because they do a lot of the order of execution stuff for you. The ide does not.

 

Try creating a change set, and deploying that. See what error pops up if any.

 

Before you do that, go in to your sandbox and do a "run all tests" from the apex classes list, and see if there are failures anywhere that are preventing you from getting the needed coverage

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

I have taken your advice and copied all my work over from IDE to my sandbox. You were right, it is easier to use.

 

TestHelper has saved with 87% code coverge, as for the Test, I am getting this error:

 

Error Message System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Account__c]: [Account__c] Stack Trace

Class.TESThelper.__sfdc_testDestiny00: line 18, column 1
Class.TESTdestiny_bIbU.myUnitTest: line 25, column 1

 

 

I created  an account inside the sandbox environment called TestTrigger, I wrote into the TestHelper in place of the name: TestDestiny00, Account__c = TestTrigger. But I get this error:

 

Error: Compile Error: Variable does not exist: TestTrigger at line 15 column 34

 

Is there a different way in which to input if it is a masterdetail relationship or a lookup?

 

Mikie

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

It is weird though as the original trigger...now it randomly has 2/2 100% code coverage. Should I just deploy it or wait until I figure out this class and test problem?

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I went into Testtrigger and wrote down the number in the URL and inserted that into the TestHelper. Now everything has passed with 100% code coverage! 

 

I will just deploy it now and see how she goes.

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I went into Testtrigger and wrote down the number in the URL and inserted that into the TestHelper. Now everything has passed with 100% code coverage! 

 

I will just deploy it now and see how she goes.

 

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I tested it out in sandbox before accepting inbound change sets in production. The product or service hidden picklist has filled itself with the Product ID:a06N0000000tLY0, rather than the actual name of the product..

 

I think I have two options now, whichd o you think would be best:

 

1. It is a hidden field anyway and therefore what it actually says matters little...If i were to just change the picklist to be the Id's and then make the other picklists dependant on it.

 

2. Add to the trigger which will convert the id into the name?

 

What are your thoughts?

 

I also understand there is some kudos system to the forum. How do I do it and I assume that I pick the original trigger post as the solution?

 

Mikie

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Scratch that.

 

I realised that my sandbox was still set to the old formula field for ProductName. I have changed it and in the sandbox environment, the Trigger works absolutely perfectly. You my friend are a genius!!

 

Everything passes and all the tests and code coverage is 100% inside the sandbox environment. However when I try to deploy it via changesets, it fails to validate because of:

 

API Name
Type
Line
Column
Problem

TESTdestiny_bIbU.myUnitTest() Class 18   Failure Message: "System.DmlException: Insert failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []", Failure Stack Trace: "Class.TESThelper.__sfdc_testDestiny00: line 18, column 1 Class.TESTdestiny_bIbU.myUnitTest: line 25, column 1"

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Ok, I created another record in production entitled TestTrigger, I recorded the ID, put it into the account Id int he TestHelper, deployed the changesets and everything works perfectly. I have tested it all out in production and it works perfectly.

 

You are a genius, thank you so much for the time and effort you have put into helping me. I am very greatful.

 

Could you recommend places in which would assist me to better understand Apex?

 

Mikie

JayNicJayNic

I never went to school to code, I learned everything by myself through these means:

 

Reading a great deal of posts, right here on the boards.

Bookmarking the documentation:

Force.com Apex Code Developer's Guide

Force.com SOQL and SOSL Reference

Visualforce Developer's Guide

Metadata API Developer's Guide

SOAP API Developer's Guide

AJAX Toolkit Developer's Guide

Web Services API Developer's Guide

Operators and Functions

 

And, my personal favourite method:

Installing unmanaged packages in dev orgs, and reverse engineering all the cool stuff they do.

 

My personal tips:

1) Make sure EVERYTHING is batchable. You will need it one day...

2) Comment the crap out of your code so that other people could read it, understand it, and augment it

3) Clean up stuff that's no longer needed

4) Learn to apply the precise amount of pressure to get the job done, but still get it done right. Any more and it's convoluded, or over engineered. Any less and it won't do what you want.

4) If you need to start over, start over. You can't shine a turd.

 

Developer.mikie.Apex.StudentDeveloper.mikie.Apex.Student

Hahaha thankyou I will definitely take what you have said into account.