+ Start a Discussion
cribiscribis 

Changing Records

I am working on a trigger to redirect a lookup based on a picklist value (field: Measurement system with values 'english' and 'metric'. The intended, finished workflow will be when the user has chosen one of the two values and clicks save, the system will repopulate the lookup with the appropriate record based on the original value.

 

Logially, the system will look up the value based on the product, metric family, and the new value converted from the original value multiplied by the conversion rate.

 

The specific field names are:

 

The obje4ctive is to change the Bulk Density according to the Customer Product and the Measurement System. The converted rate will be applied the original Bulk Density to determine the new bulk density. The bulk density field is a lookup.

 

Below is my code:

 

trigger BulkDensityChange on Opportunity(after insert, after update) {
    Set<id> cid = new Set<id>();
    for(Opportunity opp : Trigger.new) {
    System.debug('**** 0 cid id : '+opp.Customer_Product_1__c);
    
        cid.add(opp.Customer_Product_1__c);
    System.debug('**** 1 opp id : '+opp.id);
    }
    List<Customer_Bulk_Density__c> density = [select id, name, Customer_Product__c from Customer_Bulk_Density__c 
                                   Where Customer_Product__c in (
                                      select id from Customer_Product__c)];
                                      
    
 
}

 

Currently I have set up the list relationship between the customer product and  the customer bulk density. I am not sure I have done this correctly as I am trying to link the opportunity, customer product, and bulk density objects together.

 

I believe my next step is to calculate the converted amount from measurement 1 to measurement 2.

 

I would like to write the following:

 

If the new bulk density equals the old bulk density times the conversion rate and is related to the customer product, then populate.

 

The bulk densities will be prpopulated for both the metric and english measurements.

 

My questions are:

 

1. Have I started the trigger correctly, as I have described above?

2. How do I write the next part of the trigger to convert the bulk density values?

cribiscribis

I have been able to complete the trigger, yet it has fallen short as it will not do anything. What have I missed?

 

trigger BulkDensityChange on Opportunity(Before update) {
    Set<id> cid = new Set<id>();
    List<Customer_Bulk_Density__c> c = [select Id from Customer_Bulk_Density__c where id in :cid];
    for(Opportunity opp : Trigger.new) {
    System.debug('**** 0 cid id : '+opp.Customer_Product_1__c);
    
        cid.add(opp.Customer_Product_1__c);
    System.debug('**** 1 opp id : '+opp.id);
    }

    Opportunity opp;
    Customer_Bulk_Density__c cb;
    
    for(Integer i=0; i < Trigger.new.size(); i++)  {

//If the measurement system picklist equals English and the limits the bulk density records only to 
//those associated with the customer product
    List<Customer_Bulk_Density__c> cbd = [select Id, Customer_Product__c, Metric_Label__c,
        Density_Number_Value__c from Customer_Bulk_Density__c where id in :cid];
          If (Trigger.new[i].Measurement_System__c<>null) {  
            if (Trigger.new[i].Customer_Product_1__c == Trigger.new[i].Customer_Product_1_Bulk_Density__r.Customer_Product__c) {
                
              If (opp.Measurement_System__c == 'English') {
                If (opp.Customer_Product_1_Bulk_Density__r.Metric_Label__c == 'Kg/m3'){
                    If (Opp.Customer_Product_1_ID__c == cb.Customer_Product__c){

// calculates the value of the bulk density in kg/m3 from lbs./ft3. The conversion rater is 1 lb  to 16.018 kg

                        If (Opp.Customer_Product_1_Bulk_Density__r.Density_Number_Value__c * 16.018 == cb.Density_Number_Value__c){
                        opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__r.id;
                        opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__c;
                        }
            }}
     }
}
}
}
insert c;
}

 

Thank you for your help

cribiscribis

Am Continuing to work on the same trigger and have come to a stand still with a dereference the error.

 

Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger BulkDensityChangeTesting caused an unexpected exception, contact your administrator: BulkDensityChangeTesting: execution of BeforeUpdate caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.BulkDensityChangeTesting: line 26, column 68

 

 

Here is line 26:

 

If (Trigger.new[i].Customer_Product_1_ID__c == cb.Customer_Product_1_ID__c){

 

 

Below is the entire code:

 

trigger BulkDensityChangeTesting on Opportunity(Before Update) {
    Set<id> cid = new Set<id>();
    List<Customer_Bulk_Density__c> c = [select Id from Customer_Bulk_Density__c where id in :cid];
    for(Opportunity opp : Trigger.new) {
    System.debug('**** 0 cid id : '+opp.Customer_Product_1_ID__c);
    
        cid.add(opp.Customer_Product_1_ID__c);
    System.debug('**** 1 opp id : '+opp.id);
    }

    Opportunity opp;
    Customer_Bulk_Density__c cb;
    
    for(Integer i=0; i < Trigger.new.size(); i++)  {

//If the measurement system picklist equals English and the limits the bulk density records only to 
//those associated with the customer product
   // Map<ID, Opportunity> cd = new Map<Id, Opportunity>[select Customer_Product_1_ID__c from Opportunity where id in :cid
    List<Customer_Bulk_Density__c> cbd = [select Id, Customer_Product_1_ID__c, Metric_Label__c,
        Density_Number_Value__c from Customer_Bulk_Density__c where id in :cid];
          If (Trigger.new[i].Measurement_System__c<>null) {          
            if (Trigger.new[i].Customer_Product_1__r.id == Trigger.new[i].Customer_Product_1_Bulk_Density__r.Customer_Product_1_ID__c) {
                
              If (Trigger.new[i].Measurement_System__c == 'English') {
                //If (opp.Customer_Product_1_Bulk_Density__r.Metric_Label__c == 'Kg/m3'){
                    If (Trigger.new[i].Customer_Product_1_ID__c == cb.Customer_Product_1_ID__c){

// calculates the value of the bulk density in kg/m3 from lbs./ft3. The conversion rater is 1 lb  to 16.018 kg

                        If (Opp.Customer_Product_1_Bulk_Density__r.Density_Number_Value__c * 16.018 == cb.Density_Number_Value__c){
                        opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__r.id;
                       // opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__c;
                      //  }
            }}
     }
}insert cbd;
}
}

}

 

I believe int his case I need to use a Map command, but am not sure. Any help is appreciated.

cribiscribis

Solved the dereference problem, but the trigger is not changing the value in lookup field Customer Product 1 Bulk Density. Below is my code:

 

trigger BulkDensityChangeTesting on Opportunity(Before Update) {
    Set<id> cid = new Set<id>();
    List<Customer_Bulk_Density__c> c = [select Id from Customer_Bulk_Density__c where id in :cid];
    for(Opportunity opp : Trigger.new) {
    System.debug('**** 0 cid id : '+opp.Customer_Product_1_ID__c);
    
        cid.add(opp.Customer_Product_1_ID__c);
    System.debug('**** 1 opp id : '+opp.id);
    }

    Opportunity opp;
    Customer_Bulk_Density__c cb;
    
    for(Integer i=0; i < Trigger.new.size(); i++)  {

//If the measurement system picklist equals English and the limits the bulk density records only to 
//those associated with the customer product
   Map<ID, Opportunity> cd = new Map<Id, Opportunity> ([select Customer_Product_1_ID__c from Opportunity where id in :cid]);
    List<Customer_Bulk_Density__c> cbd = [select Id, Customer_Product_1_ID__c, Metric_Label__c,
        Density_Number_Value__c from Customer_Bulk_Density__c where id in :cid];
          If (Trigger.new[i].Measurement_System__c<>null) {          
            if (Trigger.new[i].Customer_Product_1__r.id == Trigger.new[i].Customer_Product_1_Bulk_Density__r.Customer_Product_1_ID__c) {
                
              If (Trigger.new[i].Measurement_System__c == 'English') {
                If (Trigger.new[i].Customer_Product_1_Bulk_Density__r.Metric_Label__c == 'Kg/m3'){
                    If (Trigger.new[i].Customer_Product_1_ID__c == cb.Customer_Product_1_ID__c){

// calculates the value of the bulk density in kg/m3 from lbs./ft3. The conversion rater is 1 lb  to 16.018 kg

                        If (Opp.Customer_Product_1_Bulk_Density__r.Density_Number_Value__c * 16.018 == cb.Density_Number_Value__c){
                        opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__r.id;
                       // opp.Customer_Product_1_Bulk_Density__c = opp.Customer_Product_1_Bulk_Density__c;
                      //  }
            }}
     }
}insert cbd;
}
}
}
}

 

Can anyone help?

crop1645crop1645

cribis:

 

I think your issue is that you are trying to update the variable opp in the Trigger. This won't work. You have to update the triggered record as such ...

 

 

for (Opportunity o: Trigger.new) {
   // set triggered Opportunity record to some value
   o.foo = 'fubar';
}

 Upon completion of the trigger, any changes to the triggered records are automatically saved (unless you have an error)

 

In addition, the insert of cbd (a list) won't work since you have fetched it earlier. You'll get duplicate id errors.  If this list is meant to be updated, then you'll need an update statement.  

 

 

Here are some tips:

 

 

1. At the beginning of your trigger you have:

    List<Customer_Bulk_Density__c> c = [select Id from Customer_Bulk_Density__c where id in :cid];

Since cid is empty, the first SOQL statment returns nothing and wastes a governor limit SOQL call. If you want to initialize the list 'c', then just do:

 

    List<Customer_Bulk_Density__c> c = new List<Customer_Bulk_Density__c> ();

 

2. You have a Trigger / Governor idiom issue here if I've got this right.  

 

Within your for (Integer i=0 ....) block you do two SOQL calls:

 

   Map<ID, Opportunity> cd = new Map<Id, Opportunity> ([select Customer_Product_1_ID__c from Opportunity where id in :cid]);
    List<Customer_Bulk_Density__c> cbd = [select Id, Customer_Product_1_ID__c, Metric_Label__c,
        Density_Number_Value__c from Customer_Bulk_Density__c where id in :cid];

 

You'll exhaust your governor limit on SOQL statements if you have more than a few Opportunities in the trigger set (limit is 20 SOQL calls)  These Maps and Lists need to be built outside of any 'for' loop.

 

Trigger writing is an art -- one tends to do several passes through data using relationship queries or child-parent queries to fetch as much as possible with the fewest statements.  You need to build up maps/lists/sets and then reference them in subsequent steps. This minimizes the SOQL calls and makes your code amenable to data loader, excel connector, wsapi, and other batch-oriented update clients.

 

3.  I use a naming convention to keep everything straight in my head between Lists, Sets, and Maps: This helps tremendously in recalling the differences in methods needed for each collection type

 

e.g.:

 

Set<id> cidSet = new Set<id>();
List<Customer_Bulk_Density__c> cbdList = ...
Map<ID,Opportunity> cdMap = ...

4. I also rarely use Trigger[n] notation when processing through Triggers. Instead, use for (Opportunity o : Trigger.new) and rely on the id fields to look up in the maps any related records that you want to check.

 

5. try-catch blocks around your DML statements are also good ideas as they let you catch validation errors potentially tossed in the target of the DML statement

foodrunnerfoodrunner

Thank you for the programming tips. This is extremely helpful