• bdardin
  • NEWBIE
  • 60 Points
  • Member since 2012

  • Chatter
    Feed
  • 2
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 12
    Questions
  • 14
    Replies
I am working on an integration with an external application that sends us urlencoded data (content type "application/x-www-form-urlencoded"). I created a POST method without any parameters. According to the documentation, the request should then be loaded into the requestBody property of the RestRequest object. However when I'm testing it, the debug log shows the value of the blob to be "Blob[0]", and converting the blob to a string shows nothing (as in, it's blank, it doesn't say "null"), nor does trying to decode that string. The request isn't being passed into the paramters as all the parameter values are null (as in, they show as "null", they're not blank). The entire class is below, all I'm doing at the moment is logging everything in the Request. What am I doing wrong? How do I make the request show in the blob?

@RestResource(urlMapping='/servicechannel/*')
global without sharing class ServiceChannelWS {
    @HttpPost
    global static void doPost() {
        RestRequest req = RestContext.request;
       
        Map<String, String> headers = req.headers;
        system.debug('Headers');
        for(String s : headers.keySet()) {
            system.debug('Key: ' + s + ' Value: ' + headers.get(s));
        }
       
        system.debug('Method: ' + req.httpMethod);
       
        Map <String, String> params = req.params;
        if(params != null) {
            system.debug('Params');
            for(String s : headers.keySet()) {
                system.debug('Key: ' + s + ' Value: ' + params.get(s));
            }
        }
       
        system.debug('IP Address: ' + req.remoteAddress);
       
        Blob b = req.requestBody;
        if(b != null) {
            system.debug('Body Blob: ' + b);
            String body = b.toString();
            system.debug('Body String: ' + body);
            String msg = EncodingUtil.urlDecode(body, 'UTF-16');
            system.debug('Body Decoded: ' + msg);
        }
       
        system.debug('Request URL: ' + req.requestURI);
    }
}
I'm very new to working with webservices in Salesforce, though I'm experienced with using Apex for triggers and Visualforce. We are trying to create a webservice that receives XML data as a string from an external application - and that's all it does. I keep trying to research how to set this up, but all the documentation I've found seems to be based on a user actually logging into an external application with OAuth. No users are going to be logging into an external application, and we really don't want to give the external application a username and password to use. What other options can be used to authenticate?

We will be using the REST API. This is the webservice class that I wrote:

@RestResource(urlMapping='/servicechannel/*')
global class ServiceChannelWS {
    @HttpPost
    global static void doPost(String xml) {
        //insert logic to parse the XML and do things with it
    }
}

If anyone could please just point me in the right direction, I would greatly appreciate it!

I am trying to override the tab of a custom object to a Visualforce. I am using a Standard Set Controller in the page. All the documentation I've found said it is possible to override a tab with a Standard Set Controller, but the page doesn't show up in the Visualforce page drop down. Is there something I'm missing as to why it's not showing up?

 

Visualforce page code:

 

<apex:page standardController="SVMXC__Installed_Product__c" recordSetVar="installedproducts" extensions="PortalTabRedirectController" action="{!redirect}">
    <apex:enhancedList type="SVMXC__Installed_Product__c" height="700" rowsPerPage="50" customizable="false"/>
</apex:page>

Extension code, in case that's relevant - I purposely wrote it to be not specific to any object, as we are trying to override the tab for multiple objects in the same way. Basically we want the standard tab to show for all users except for our Customer Portal users, who instead will only see the enhancedList just showing all the records, instead of the Recently Viewed list.

 

public with sharing class PortalTabRedirectController {
    public String keyPrefix;
    
    public PortalTabRedirectController(ApexPages.StandardSetController stsc) {
        SObject obj = stsc.getRecord();
        Schema.SObjectType objType = obj.getSObjectType();
        Schema.DescribeSobjectResult result = objType.getDescribe();
        keyPrefix = result.getKeyPrefix();
        system.debug('Variable keyPrefix: ' + keyPrefix);
    }
    
    public String getKeyPrefix() {
        return keyPrefix;
    }
    
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }
    
    public PageReference redirect() {
        Profile p = [SELECT Name FROM Profile WHERE Id = :UserInfo.getProfileId()];
        system.debug('Profile Name: ' + p.Name);
        
        String tabURL = '/' + keyPrefix + '/o';
        system.debug('Variable tabURL: ' + tabURL);
        
        if('Customer Portal'.equals(p.Name)) {
            return null;
        } else {
            PageReference pageRef = new PageReference(tabURL);
            pageRef.setRedirect(true);
            return pageRef;
        }
    }
}

 Any thoughts would be greatly appreciated!

I am attempting to build a Visualforce page with pagination using this guide. However when I put it in place, it keeps putting the same lines into each array - even though debug statements show that it's creating the arrays and adding them to a list of arrays successfully. Somehow the content of each array is getting overwritten, but I have no idea what's causing it.

 

I am not including the entire controller - just the code that I have been testing through the developer console.

 

static Integer FIRST_BREAK = 10;
static Integer SUBSEQ_BREAKS = 30;

ID woID = 'a0wE0000000NTi0';

RecordType rt = [SELECT Id FROM RecordType WHERE Name = 'Products Serviced' AND SobjectType = 'SVMXC__Service_Order_Line__c'];
        
        List<SVMXC__Service_Order_Line__c> lines = [SELECT Id, Building__c, Position__c, Product_of_SN__c, SN__c, SVMXC__Serial_Number__r.Condition__c FROM SVMXC__Service_Order_Line__c WHERE SVMXC__Service_Order__c = :woID AND RecordTypeId = :rt.Id ORDER BY Position__c ASC NULLS LAST];
        
        List<SVMXC__Service_Order_Line__c[]> pageLines = new List<SVMXC__Service_Order_Line__c[]>();
        SVMXC__Service_Order_Line__c[] pageOfLines = new SVMXC__Service_Order_Line__c[]{};
        
        Integer counter = 0;
        Boolean firstBreakFound = false;
        Boolean setSubSeqBreak = false;  
        Integer breakPoint = FIRST_BREAK;
        
        for(SVMXC__Service_Order_Line__c line : lines) {
            system.debug('Counter: ' + counter + ' IP: ' + line.SN__c);
            if(counter < breakPoint) {
                pageOfLines.add(line);
                counter++;
            }
            
            if(counter == breakPoint) {
                if(!firstBreakFound) {
                    firstBreakFound = true;
                    setSubSeqBreak = true;
                }
                
                counter = 0;
                pageLines.add(pageOfLines);
                system.debug('pageOfLines Size: ' + pageOfLines.size());
                system.debug('pageLines Size: ' + pageLines.size());
                pageOfLines.clear();
            }
            
            if(setSubSeqBreak) {
                breakPoint = SUBSEQ_BREAKS;
                setSubSeqBreak = false;
            }
        }
        
	if(!pageOfLines.isEmpty()) {
            pageLines.add(pageOfLines);
        }

system.debug('Size After Adding Leftover: ' + pageLines.size());

Integer counter2 = 0;
for(SVMXC__Service_Order_Line__c[] lineArray : pageLines) {
    system.debug('Size of Array #' + counter2 + ' : ' + lineArray.size());
    
    for(SVMXC__Service_Order_Line__c li : lineArray) {
    	system.debug('Array #: ' + counter2 + ' IP: ' + li.SN__c);
    }
    
    counter2++;
}

 And here is the debug log, showing that after the for loop is completed, the size and contents of the arrays have suddenly become the same:

 

11:20:59.235 (235006000)|USER_DEBUG|[45]|DEBUG|Counter: 0 IP: MCK3170
11:20:59.235 (235163000)|USER_DEBUG|[45]|DEBUG|Counter: 1 IP: 329120
11:20:59.235 (235277000)|USER_DEBUG|[45]|DEBUG|Counter: 2 IP: MCK3179
11:20:59.235 (235387000)|USER_DEBUG|[45]|DEBUG|Counter: 3 IP: 329263
11:20:59.235 (235495000)|USER_DEBUG|[45]|DEBUG|Counter: 4 IP: MCK3180
11:20:59.235 (235602000)|USER_DEBUG|[45]|DEBUG|Counter: 5 IP: 329239
11:20:59.235 (235709000)|USER_DEBUG|[45]|DEBUG|Counter: 6 IP: MCK3181
11:20:59.235 (235815000)|USER_DEBUG|[45]|DEBUG|Counter: 7 IP: 329132
11:20:59.235 (235921000)|USER_DEBUG|[45]|DEBUG|Counter: 8 IP: MCK3182
11:20:59.236 (236033000)|USER_DEBUG|[45]|DEBUG|Counter: 9 IP: 329109
11:20:59.236 (236173000)|USER_DEBUG|[59]|DEBUG|pageOfLines Size: 10
11:20:59.236 (236239000)|USER_DEBUG|[60]|DEBUG|pageLines Size: 1
11:20:59.236 (236361000)|USER_DEBUG|[45]|DEBUG|Counter: 0 IP: MCK3183
11:20:59.236 (236471000)|USER_DEBUG|[45]|DEBUG|Counter: 1 IP: MCK3184
11:20:59.236 (236579000)|USER_DEBUG|[45]|DEBUG|Counter: 2 IP: 329183
11:20:59.236 (236685000)|USER_DEBUG|[45]|DEBUG|Counter: 3 IP: MCK3185
11:20:59.236 (236790000)|USER_DEBUG|[45]|DEBUG|Counter: 4 IP: 329203
11:20:59.236 (236897000)|USER_DEBUG|[45]|DEBUG|Counter: 5 IP: MCK3186
11:20:59.237 (237003000)|USER_DEBUG|[45]|DEBUG|Counter: 6 IP: 329150
11:20:59.237 (237117000)|USER_DEBUG|[45]|DEBUG|Counter: 7 IP: MCK3187
11:20:59.237 (237228000)|USER_DEBUG|[45]|DEBUG|Counter: 8 IP: 329060
11:20:59.237 (237339000)|USER_DEBUG|[45]|DEBUG|Counter: 9 IP: MCK3188
11:20:59.237 (237446000)|USER_DEBUG|[45]|DEBUG|Counter: 10 IP: 329195
11:20:59.237 (237553000)|USER_DEBUG|[45]|DEBUG|Counter: 11 IP: MCK3171
11:20:59.237 (237659000)|USER_DEBUG|[45]|DEBUG|Counter: 12 IP: 329168
11:20:59.237 (237765000)|USER_DEBUG|[45]|DEBUG|Counter: 13 IP: MCK3189
11:20:59.237 (237871000)|USER_DEBUG|[45]|DEBUG|Counter: 14 IP: 329098
11:20:59.237 (237977000)|USER_DEBUG|[45]|DEBUG|Counter: 15 IP: MCK3190
11:20:59.238 (238090000)|USER_DEBUG|[45]|DEBUG|Counter: 16 IP: 329173
11:20:59.238 (238199000)|USER_DEBUG|[45]|DEBUG|Counter: 17 IP: MCK3191
11:20:59.238 (238308000)|USER_DEBUG|[45]|DEBUG|Counter: 18 IP: 329253
11:20:59.238 (238416000)|USER_DEBUG|[45]|DEBUG|Counter: 19 IP: MCK3192
11:20:59.238 (238522000)|USER_DEBUG|[45]|DEBUG|Counter: 20 IP: 329208
11:20:59.238 (238628000)|USER_DEBUG|[45]|DEBUG|Counter: 21 IP: MCK3193
11:20:59.238 (238733000)|USER_DEBUG|[45]|DEBUG|Counter: 22 IP: 329192
11:20:59.238 (238839000)|USER_DEBUG|[45]|DEBUG|Counter: 23 IP: MCK3194
11:20:59.238 (238945000)|USER_DEBUG|[45]|DEBUG|Counter: 24 IP: 329159
11:20:59.239 (239057000)|USER_DEBUG|[45]|DEBUG|Counter: 25 IP: MCK3195
11:20:59.239 (239165000)|USER_DEBUG|[45]|DEBUG|Counter: 26 IP: 329259
11:20:59.239 (239274000)|USER_DEBUG|[45]|DEBUG|Counter: 27 IP: MCK3172
11:20:59.239 (239384000)|USER_DEBUG|[45]|DEBUG|Counter: 28 IP: 329232
11:20:59.239 (239490000)|USER_DEBUG|[45]|DEBUG|Counter: 29 IP: MCK3173
11:20:59.239 (239584000)|USER_DEBUG|[59]|DEBUG|pageOfLines Size: 30
11:20:59.239 (239623000)|USER_DEBUG|[60]|DEBUG|pageLines Size: 2
11:20:59.239 (239716000)|USER_DEBUG|[45]|DEBUG|Counter: 0 IP: 329245
11:20:59.239 (239823000)|USER_DEBUG|[45]|DEBUG|Counter: 1 IP: MCK3174
11:20:59.239 (239930000)|USER_DEBUG|[45]|DEBUG|Counter: 2 IP: 329234
11:20:59.240 (240042000)|USER_DEBUG|[45]|DEBUG|Counter: 3 IP: MCK3175
11:20:59.240 (240150000)|USER_DEBUG|[45]|DEBUG|Counter: 4 IP: 329223
11:20:59.240 (240259000)|USER_DEBUG|[45]|DEBUG|Counter: 5 IP: MCK3176
11:20:59.240 (240369000)|USER_DEBUG|[45]|DEBUG|Counter: 6 IP: 329213
11:20:59.240 (240475000)|USER_DEBUG|[45]|DEBUG|Counter: 7 IP: MCK3177
11:20:59.240 (240581000)|USER_DEBUG|[45]|DEBUG|Counter: 8 IP: 329219
11:20:59.240 (240687000)|USER_DEBUG|[45]|DEBUG|Counter: 9 IP: MCK3178
11:20:59.240 (240793000)|USER_DEBUG|[45]|DEBUG|Counter: 10 IP: 329137
11:20:59.240 (240979000)|USER_DEBUG|[74]|DEBUG|Size After Adding Leftover: 3
11:20:59.241 (241247000)|USER_DEBUG|[78]|DEBUG|Size of Array #0 : 11
11:20:59.241 (241354000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329245
11:20:59.241 (241411000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: MCK3174
11:20:59.241 (241464000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329234
11:20:59.241 (241516000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: MCK3175
11:20:59.241 (241568000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329223
11:20:59.241 (241620000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: MCK3176
11:20:59.241 (241673000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329213
11:20:59.241 (241725000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: MCK3177
11:20:59.241 (241777000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329219
11:20:59.241 (241829000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: MCK3178
11:20:59.241 (241881000)|USER_DEBUG|[81]|DEBUG|Array #: 0 IP: 329137
11:20:59.241 (241973000)|USER_DEBUG|[78]|DEBUG|Size of Array #1 : 11
11:20:59.242 (242048000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329245
11:20:59.242 (242103000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: MCK3174
11:20:59.242 (242155000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329234
11:20:59.242 (242207000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: MCK3175
11:20:59.242 (242264000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329223
11:20:59.242 (242319000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: MCK3176
11:20:59.242 (242372000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329213
11:20:59.242 (242424000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: MCK3177
11:20:59.242 (242476000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329219
11:20:59.242 (242528000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: MCK3178
11:20:59.242 (242580000)|USER_DEBUG|[81]|DEBUG|Array #: 1 IP: 329137
11:20:59.242 (242671000)|USER_DEBUG|[78]|DEBUG|Size of Array #2 : 11
11:20:59.242 (242738000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329245
11:20:59.242 (242790000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: MCK3174
11:20:59.242 (242842000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329234
11:20:59.242 (242894000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: MCK3175
11:20:59.242 (242946000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329223
11:20:59.242 (242997000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: MCK3176
11:20:59.243 (243062000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329213
11:20:59.243 (243115000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: MCK3177
11:20:59.243 (243168000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329219
11:20:59.243 (243220000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: MCK3178
11:20:59.243 (243275000)|USER_DEBUG|[81]|DEBUG|Array #: 2 IP: 329137

 Anyone have any idea what I'm doing wrong here?? I would greatly appreciate any input!

I'm about to tear my hair out over this... We have a basic trigger that updates the tax rate on the opportunity line items when the tax rate changes on the opportunity. We couldn't use a formula field because the calculated tax is rolled up into a roll-up summary field. However, I keep getting this error:

 

Message: Update failed. First exception on row 0 with id 00kE0000008QHZVIA4; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: [] Stack Trace: Class.optyTriggerClass.afterUpdateTrigger: line 61, column 1 Trigger.optyTrigger: line 7, column 1

 

In the trigger itself, trigger.new and trigger.oldMap are passed into the method below:

 

public static void afterUpdateTrigger (List<Opportunity> optys, Map<ID, Opportunity> oldOptys) {
        Set<ID> optyIDs = new Set<ID>();
        for(Opportunity opty : optys) {
            Opportunity oldOpty = oldOptys.get(opty.Id);
            system.debug('Old Tax: ' + oldOpty.Tax_Rate__c);
            system.debug('New Tax: ' + opty.Tax_Rate__c);
            
            if(opty.HasOpportunityLineItem == true && (oldOpty.Tax_Rate__c != opty.Tax_Rate__c)) {
                optyIDs.add(opty.Id);
            }
        }
        
        if(!optyIDs.isEmpty()) {
            List<OpportunityLineItem> lineItems = [SELECT Id, OpportunityId, Opportunity.Tax_Rate__c, Tax_Rate_del__c, Taxable__c
                                                   FROM OpportunityLineItem 
                                                   WHERE OpportunityId IN :optyIDs];
            
            List<OpportunityLineItem> linesToUpdate = new List<OpportunityLineItem>();
           
            for(OpportunityLineItem oli : lineItems) {
                if(oli.Taxable__c == true) {
                    oli.Tax_Rate_del__c = oli.Opportunity.Tax_Rate__c;
                    linesToUpdate.add(oli);
                }
            }
            
            try {
                if(linesToUpdate.size() > 0) {
                    update linesToUpdate;
                }
            } catch (Exception e) {
                system.debug('Message: ' + e.getMessage() + ' Stack Trace: ' + e.getStackTraceString());
            }
        }
    }

The stack trace refers to the "update linesToUpdate" line, which is very unhelpful. I have no idea whatsoever why I am getting this error. If anyone could please help point out where this error is coming from I'd greatly appreciate it!

I'm writing a trigger on a custom object that when the Estimate Approved checkbox is true, it creates an opportunity. I decided to compare the trigger.old and trigger.new values in order to detect the change. The trigger works great when testing through the UI, but the unit test is consistently failing and I can't figure out why.

 

Both trigger.new and trigger.old are passed into a method in a class. Since the method handles other things irrelevant to this issue, I'll only be posting the relevant code:

 

public static void afterUpdateTrigger (List<SVMXC__Service_Order__c> wos, Map<ID, SVMXC__Service_Order__c> oldWOs){
        Set<ID> woIDs = new Set<ID>();
        for(SVMXC__Service_Order__c wo : wos) {
            woIDs.add(wo.Id);
        }

//queried separately in order to get lookup field values
List<SVMXC__Service_Order__c> workOrders = [SELECT Id, SVMXC__Order_Type__c, RecordType.Name, SVMXC__Service_Contract__r.Territory__c, Dash_1__c, Requirement_Numbers__c, Lines_Created__c, SVMXC__Component__c, Milestone__r.Project__c, SVMXC__Order_Status__c, Estimate_Approved__c, Technician_completed_WO_Date_Time__c, Department__c, SVMXC__Company__c, Location__c, Location__r.Sales_Tax_Schedule__c, Location__r.Name, SVMXC__Problem_Description__c
                                                    FROM SVMXC__Service_Order__c
                                                    WHERE Id IN :woIDs];

List<Opportunity> optysToCreate = new List<Opportunity>();

for(SVMXC__Service_Order__c wo : workOrders) {
            SVMXC__Service_Order__c oldWO = oldWOs.get(wo.Id);
            system.debug('Old Estimate Approved: ' + oldWO.Estimate_Approved__c);
            system.debug('New Estimate Approved: ' + wo.Estimate_Approved__c);

//code

} else if(oldWO.Estimate_Approved__c == false && wo.Estimate_Approved__c == true) {
                 Map<String, Sales_Tax__c> taxMap = Sales_Tax__c.getAll();
                 String schedule = String.valueOf(wo.Location__r.Sales_Tax_Schedule__c);
                 
                 Datetime nowDate = system.now();
                 Date closeDate = date.valueOf(nowDate).addDays(7);
                 String today = nowDate.format('MM-dd-yyyy', 'PST');
                 
                 Opportunity opty = new Opportunity();
                     opty.Name = wo.Location__r.Name + ' Estimate ' + today;
                     opty.CloseDate = closeDate;
                     opty.AccountId = wo.SVMXC__Company__c;
                     opty.Location__c = wo.Location__c;
                     opty.Product_Family__c = 'Service';
                     opty.Company__c = wo.Department__c;
                     opty.Work_Order__c = wo.Id;
                     opty.StageName = 'Budget Quote';
                     opty.Probability = 25;
                     opty.Drop_Ship__c = 'No';
                     opty.LeadSource = 'Technician';
                     opty.Project_Scope__c = wo.SVMXC__Problem_Description__c;
                     if(schedule != null) {
                         opty.Tax_Rate__c = taxMap.get(schedule).Tax_Rate__c;
                     } else {
                         opty.Tax_Rate__c = 0;
                     }
                 optysToCreate.add(opty);
             }
}

try {
              system.debug('Size of Opty List: ' + optysToCreate.size());
              if(!optysToCreate.isEmpty()) {
                  insert optysToCreate;
              }
          } catch (Exception e) {
              System.debug(e.getMessage());
          }

And here is the unit test. I originally wrote this to be bulkified, but that made the debug log generated from the test run to hit the maximum size allowed and thus cut off the parts I needed to see so I have it only testing one record. Once I figure out what is causing the error it will be bulkified. SeeAllData is also set to true because inserting a SVMXC__Service_Order__c record always fails due to a trigger in a managed package, so instead I query an existing work order and then clone it for testing.

 

@isTest(SeeAllData=true)
private class workOrderTrigger_UT {
    static testMethod void OptyTest() {
        Account a = new Account (Name ='Account',
                                        Phone='949-271-2476',
                                        BillingStreet='ABC',
                                        BillingCity='PQR ',
                                        BillingState = 'XYZ',
                                        BillingPostalCode='111',
                                        BillingCountry='XYZ'
                                        );
        insert a;
        
        Contact c = new Contact(FirstName='Brianna',
                                LastName='Dardin',
                                AccountId = a.Id);
        insert c;
        
        SVMXC__Site__c loc = new SVMXC__Site__c (Name='Location',
                                               SVMXC__Account__c = a.Id,
                                               SVMXC__Street__c='ABC',
                                               SVMXC__City__c='PQR ',
                                               SVMXC__State__c = 'XYZ',
                                               SVMXC__Zip__c='111',
                                               SVMXC__Country__c='XYZ',
                                               Sales_Tax_Schedule__c = 'CALC OC'
                                              );
        insert loc;
        
        SVMXC__Service_Order__c wo = [SELECT Id, SVMXC__Contact__c, SVMXC__Order_Type__c, SVMXC__Order_Status__c, Estimate_Approved__c
                                      FROM SVMXC__Service_Order__c
                                      WHERE RecordType.Name = 'Service' AND SVMXC__Order_Status__c = 'Estimate' 
                                      LIMIT 1];
        system.assert(wo != null);
        
        SVMXC__Service_Order__c woClone = wo.clone(false, true);
                woClone.SVMXC__Company__c = a.Id;
                woClone.Location__c = loc.Id;
                woClone.Department__c = 'MEC';
                woClone.SVMXC__Contact__c = c.Id;
        insert woClone;

        test.startTest();
system.debug('Old Estimate Approved - Unit Test' + woClone.Estimate_Approved__c); woClone.Estimate_Approved__c = true; update woClone; system.debug('New Estimate Approved - Unit Test' + woClone.Estimate_Approved__c); test.stopTest();
Opportunity opty = [SELECT Id, Work_Order__c FROM Opportunity WHERE Work_Order__c = :woClone.Id]; system.assert(opty != null); } }

The most baffling part is in the debug logs - the debug statements in the trigger say false for both the old record and the new record - as if the update never happened. But the debug statement in the unit test returns true for the new value. When testing the trigger with test records through the UI, those debug logs do show the old value as false and the new value as true.

 

Here are snippets from the debug logs as well:

 

Unit Test Debug Log:

13:13:49.314 (30314626000)|SYSTEM_METHOD_ENTRY|[44]|System.debug(ANY)
13:13:49.314 (30314636000)|USER_DEBUG|[44]|DEBUG|Old Estimate Approved - Unit Testfalse
13:13:49.314 (30314642000)|SYSTEM_METHOD_EXIT|[44]|System.debug(ANY)
13:13:49.314 (30314726000)|DML_BEGIN|[46]|Op:Update|Type:SVMXC__Service_Order__c|Rows:1
13:13:49.306 (30306278000)|SYSTEM_METHOD_ENTRY|[119]|System.debug(ANY)
13:13:49.306 (30306291000)|USER_DEBUG|[119]|DEBUG|Old Estimate Approved: false
13:13:49.306 (30306298000)|SYSTEM_METHOD_EXIT|[119]|System.debug(ANY)
13:13:49.306 (30306310000)|SYSTEM_METHOD_ENTRY|[120]|String.valueOf(Object)
13:13:49.306 (30306324000)|SYSTEM_METHOD_EXIT|[120]|String.valueOf(Object)
13:13:49.306 (30306334000)|SYSTEM_METHOD_ENTRY|[120]|System.debug(ANY)
13:13:49.306 (30306341000)|USER_DEBUG|[120]|DEBUG|New Estimate Approved: false
13:13:49.306 (30306346000)|SYSTEM_METHOD_EXIT|[120]|System.debug(ANY)
13:13:49.306 (30306782000)|SYSTEM_METHOD_ENTRY|[333]|System.debug(ANY)
13:13:49.306 (30306791000)|USER_DEBUG|[333]|DEBUG|Size of Opty List: 0
13:13:49.306 (30306797000)|SYSTEM_METHOD_EXIT|[333]|System.debug(ANY)
13:13:49.983 (30983973000)|SYSTEM_METHOD_ENTRY|[47]|System.debug(ANY)
13:13:49.983 (30983985000)|USER_DEBUG|[47]|DEBUG|New Estimate Approved - Unit Testtrue
13:13:49.983 (30983992000)|SYSTEM_METHOD_EXIT|[47]|System.debug(ANY)

 

UI Debug Log

13:27:08.291 (5291747000)|USER_DEBUG|[119]|DEBUG|Old Estimate Approved: false
13:27:08.291 (5291785000)|USER_DEBUG|[120]|DEBUG|New Estimate Approved: true
13:27:08.297 (5297238000)|SYSTEM_CONSTRUCTOR_ENTRY|[255]|<init>()
13:27:08.297 (5297264000)|SYSTEM_CONSTRUCTOR_EXIT|[255]|<init>()
13:27:08.297 (5297401000)|USER_DEBUG|[333]|DEBUG|Size of Opty List: 1
13:27:08.297 (5297504000)|DML_BEGIN|[335]|Op:Insert|Type:Opportunity|Rows:1

I have no idea what I'm doing wrong here. If there's something I'm missing please point it out. I've been going crazy trying to figure it out on my own!

We want to clean up our product data, and we want to start by focusing on the products that are NOT related to anything. While we have multiple objects related to products, there are really only 3 main objects that use products, so for simplicity I will be focusing on those. However the problem I'm having is generating this list. With a report I know I am able to filter out products without 2 of our custom objects, but that feature doesn't support filtering out opportunity line items (or even pricebook entries), which is one of the 3 objects we need. I then considered using GROUP BY queries on the 3 objects to find all the ones related to products, and then add all those IDs to a set and then query for all the products that aren't in that set, but this hits the governor limit for query rows very quickly, so that's not an option either. Is there any way at all I could build this list?

I have been searching and googling like crazy trying to resolve this. I wrote an email template with a component and custom controller, but I can't get any of the values set in the controller to show up when I try to test it via the Send Test and Verify Merge Fields button. I've read many posts here in this forum about this issue and have implemented everything I read but I'm STILL not getting anything, so there must be something else I'm missing.

 

Email Template

<messaging:emailTemplate subject="How Did We Do?" recipientType="Contact" relatedToType="SVMXC__Service_Order__c">
<messaging:htmlEmailBody >
<html>
    <body>
    <style type="text/css">
        body {color:black; font-family:arial; font-size:12px}
    </style>
        Hello {!Recipient.Name},<br/><br/>
        <c:SurveyEmailComponent_MEC emailWOId="{!relatedTo.Id}"/>
    </body>
</html>
</messaging:htmlEmailBody>
</messaging:emailTemplate>

 

Component

<apex:component controller="SurveyEmailController" access="global">
    <apex:attribute name="emailWOId" description="Passes the ID of the WO to the custom controller" type="Id" assignTo="{!woID}"/>
    <apex:outputText >We want to continue to improve by learning how we did on our recent service completed for you on {!completedDate}.<br/><br/>
    We would be grateful if you took 2 minutes to complete a very short survey by clicking the link below:
    </apex:outputText><br/><br/>
    <apex:outputLink value="http://mckinley.force.com/timbasurveys__survey?id=a2LE0000000Td4qMAC&cId={!contactID}&wf=yes">Service Follow Up Survey</apex:outputLink><br/><br/>
    <apex:outputText >Thank you,<br/>
    {!techName}
    </apex:outputText><br/>
    <apex:image value="{!photoURL}"/>
</apex:component>

 

Controller

public class SurveyEmailController {
    public ID woID {get; set;}
    public SVMXC__Service_Order__c wo {get; set;}
    public ID contactID {get; set;}
    public String techName {get; set;}
    public String photoURL {get; set;}
    public String completedDate {get; set;}
    
    public SurveyEmailController() {
    }
    
    public SVMXC__Service_Order__c getWO() {
        wo = [SELECT Id, SVMXC__Contact__c, Technician_completed_WO_Date_Time__c, SVMXC__Group_Member__r.Name, SVMXC__Group_Member__r.SVMXC__Salesforce_User__r.SmallPhotoUrl
              FROM SVMXC__Service_Order__c
              WHERE Id = :woID];
        
        return wo;
    }
    
    public String getPhotoURL() {
        photoURL = wo.SVMXC__Group_Member__r.SVMXC__Salesforce_User__r.SmallPhotoUrl;
        
        return photoURL;
    }
    
    public String getCompletedDate() {
        datetime techCompDate = datetime.valueOf(wo.Technician_completed_WO_Date_Time__c);

        if(techCompDate != null) {
            completedDate = techCompDate.format('MM-dd-yyyy', 'PST');
        } else {
            completedDate = System.Now().format('MM-dd-yyyy', 'PST');
        }
        
        return completedDate;
    }
    
    public ID getContactID() {
        contactID = wo.SVMXC__Contact__c;
        
        return contactID;
    }
    
    public String getTechName() {
        techName = wo.SVMXC__Group_Member__r.Name;
        
        return techName;
    }
    
}

 

We've recently installed Timba Surveys. We want to send out a survey to the customer every time a work order, a custom object, is completed. More importantly, we want to relate their response back to the work order it came from. I already contacted Timba Surveys support and they said they don't support it so I was hoping to find a workaround.

 

The idea I had was pretty simple - pass the work order ID into the URL of the survey, then grab that ID and insert it into a lookup field on the survey response object. I was hoping I would be able to copy the visualforce page code and just add a new extension that does this, since I don't want to touch anything else. But when I try to copy the code and save it, I get this error: "Error: Constructor is not visible: [TIMBASURVEYS.SurveyController]<init>(ApexPages.StandardController)". I understand that it's not visible because it's managed, but it was the only idea I had. Is there any way around this or any other way of making this work? It's critically important for us to be able to relate it back to this custom object.

On our Project object, we have a Salesman picklist field, which just lists the last name of the salesman who sold the project. We have a Project Contact object on the Project. We want to use this Salesman field to automatically create a Project Contact looking up to the Contact record that corresponds to that salesman. Right now we're currently doing this by hardcoding the IDs of the contacts - which I understand is bad programming, but still works in production. I want to be able to do this without hardcoding the IDs, however I'm floundering over how to query just the salesmen contacts without having a huge OR statement in the where clause for all their last names, and how to then correctly grab the right salesman contact for each project in a for loop. I know I can't use a query inside the for loop. This sounds like something I'd use Maps for - is it possible to construct a Map where the last name as a String is the key and the Contact object (or ID) is the value? Or is there some other way I should do it? I'm an Apex (and programming in general) newbie. Thanks in advance!

I wanted to build a simple trigger on the Event that would make a checkbox true if the What field looked up to one of our custom objects, and false otherwise. But with my attempt at this, it keeps making the checkbox true regardless of what is related to the event and I don't understand why. Here's the code:

 

trigger WorkOrderCheckbox on Event (before insert, before update) {
 for(Event newEvent : trigger.new) {
  if(newEvent.What instanceof SVMXC__Service_Order__c) {
   newEvent.Work_Order__c = true;
  } else {
   newEvent.Work_Order__c = false;
  }
 }
}

What am I doing wrong here?

I want to be able to display a datatable for one object based on some criteria, and if the criteria is false, it will display a different datatable showing data from a different object. I have tried doing this with two separate datatables with complementary rendered attributes, but they just always came up blank. What would be the way to do this?

I am working on an integration with an external application that sends us urlencoded data (content type "application/x-www-form-urlencoded"). I created a POST method without any parameters. According to the documentation, the request should then be loaded into the requestBody property of the RestRequest object. However when I'm testing it, the debug log shows the value of the blob to be "Blob[0]", and converting the blob to a string shows nothing (as in, it's blank, it doesn't say "null"), nor does trying to decode that string. The request isn't being passed into the paramters as all the parameter values are null (as in, they show as "null", they're not blank). The entire class is below, all I'm doing at the moment is logging everything in the Request. What am I doing wrong? How do I make the request show in the blob?

@RestResource(urlMapping='/servicechannel/*')
global without sharing class ServiceChannelWS {
    @HttpPost
    global static void doPost() {
        RestRequest req = RestContext.request;
       
        Map<String, String> headers = req.headers;
        system.debug('Headers');
        for(String s : headers.keySet()) {
            system.debug('Key: ' + s + ' Value: ' + headers.get(s));
        }
       
        system.debug('Method: ' + req.httpMethod);
       
        Map <String, String> params = req.params;
        if(params != null) {
            system.debug('Params');
            for(String s : headers.keySet()) {
                system.debug('Key: ' + s + ' Value: ' + params.get(s));
            }
        }
       
        system.debug('IP Address: ' + req.remoteAddress);
       
        Blob b = req.requestBody;
        if(b != null) {
            system.debug('Body Blob: ' + b);
            String body = b.toString();
            system.debug('Body String: ' + body);
            String msg = EncodingUtil.urlDecode(body, 'UTF-16');
            system.debug('Body Decoded: ' + msg);
        }
       
        system.debug('Request URL: ' + req.requestURI);
    }
}
I'm very new to working with webservices in Salesforce, though I'm experienced with using Apex for triggers and Visualforce. We are trying to create a webservice that receives XML data as a string from an external application - and that's all it does. I keep trying to research how to set this up, but all the documentation I've found seems to be based on a user actually logging into an external application with OAuth. No users are going to be logging into an external application, and we really don't want to give the external application a username and password to use. What other options can be used to authenticate?

We will be using the REST API. This is the webservice class that I wrote:

@RestResource(urlMapping='/servicechannel/*')
global class ServiceChannelWS {
    @HttpPost
    global static void doPost(String xml) {
        //insert logic to parse the XML and do things with it
    }
}

If anyone could please just point me in the right direction, I would greatly appreciate it!

I am trying to override the tab of a custom object to a Visualforce. I am using a Standard Set Controller in the page. All the documentation I've found said it is possible to override a tab with a Standard Set Controller, but the page doesn't show up in the Visualforce page drop down. Is there something I'm missing as to why it's not showing up?

 

Visualforce page code:

 

<apex:page standardController="SVMXC__Installed_Product__c" recordSetVar="installedproducts" extensions="PortalTabRedirectController" action="{!redirect}">
    <apex:enhancedList type="SVMXC__Installed_Product__c" height="700" rowsPerPage="50" customizable="false"/>
</apex:page>

Extension code, in case that's relevant - I purposely wrote it to be not specific to any object, as we are trying to override the tab for multiple objects in the same way. Basically we want the standard tab to show for all users except for our Customer Portal users, who instead will only see the enhancedList just showing all the records, instead of the Recently Viewed list.

 

public with sharing class PortalTabRedirectController {
    public String keyPrefix;
    
    public PortalTabRedirectController(ApexPages.StandardSetController stsc) {
        SObject obj = stsc.getRecord();
        Schema.SObjectType objType = obj.getSObjectType();
        Schema.DescribeSobjectResult result = objType.getDescribe();
        keyPrefix = result.getKeyPrefix();
        system.debug('Variable keyPrefix: ' + keyPrefix);
    }
    
    public String getKeyPrefix() {
        return keyPrefix;
    }
    
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }
    
    public PageReference redirect() {
        Profile p = [SELECT Name FROM Profile WHERE Id = :UserInfo.getProfileId()];
        system.debug('Profile Name: ' + p.Name);
        
        String tabURL = '/' + keyPrefix + '/o';
        system.debug('Variable tabURL: ' + tabURL);
        
        if('Customer Portal'.equals(p.Name)) {
            return null;
        } else {
            PageReference pageRef = new PageReference(tabURL);
            pageRef.setRedirect(true);
            return pageRef;
        }
    }
}

 Any thoughts would be greatly appreciated!

I'm about to tear my hair out over this... We have a basic trigger that updates the tax rate on the opportunity line items when the tax rate changes on the opportunity. We couldn't use a formula field because the calculated tax is rolled up into a roll-up summary field. However, I keep getting this error:

 

Message: Update failed. First exception on row 0 with id 00kE0000008QHZVIA4; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: [] Stack Trace: Class.optyTriggerClass.afterUpdateTrigger: line 61, column 1 Trigger.optyTrigger: line 7, column 1

 

In the trigger itself, trigger.new and trigger.oldMap are passed into the method below:

 

public static void afterUpdateTrigger (List<Opportunity> optys, Map<ID, Opportunity> oldOptys) {
        Set<ID> optyIDs = new Set<ID>();
        for(Opportunity opty : optys) {
            Opportunity oldOpty = oldOptys.get(opty.Id);
            system.debug('Old Tax: ' + oldOpty.Tax_Rate__c);
            system.debug('New Tax: ' + opty.Tax_Rate__c);
            
            if(opty.HasOpportunityLineItem == true && (oldOpty.Tax_Rate__c != opty.Tax_Rate__c)) {
                optyIDs.add(opty.Id);
            }
        }
        
        if(!optyIDs.isEmpty()) {
            List<OpportunityLineItem> lineItems = [SELECT Id, OpportunityId, Opportunity.Tax_Rate__c, Tax_Rate_del__c, Taxable__c
                                                   FROM OpportunityLineItem 
                                                   WHERE OpportunityId IN :optyIDs];
            
            List<OpportunityLineItem> linesToUpdate = new List<OpportunityLineItem>();
           
            for(OpportunityLineItem oli : lineItems) {
                if(oli.Taxable__c == true) {
                    oli.Tax_Rate_del__c = oli.Opportunity.Tax_Rate__c;
                    linesToUpdate.add(oli);
                }
            }
            
            try {
                if(linesToUpdate.size() > 0) {
                    update linesToUpdate;
                }
            } catch (Exception e) {
                system.debug('Message: ' + e.getMessage() + ' Stack Trace: ' + e.getStackTraceString());
            }
        }
    }

The stack trace refers to the "update linesToUpdate" line, which is very unhelpful. I have no idea whatsoever why I am getting this error. If anyone could please help point out where this error is coming from I'd greatly appreciate it!

We want to clean up our product data, and we want to start by focusing on the products that are NOT related to anything. While we have multiple objects related to products, there are really only 3 main objects that use products, so for simplicity I will be focusing on those. However the problem I'm having is generating this list. With a report I know I am able to filter out products without 2 of our custom objects, but that feature doesn't support filtering out opportunity line items (or even pricebook entries), which is one of the 3 objects we need. I then considered using GROUP BY queries on the 3 objects to find all the ones related to products, and then add all those IDs to a set and then query for all the products that aren't in that set, but this hits the governor limit for query rows very quickly, so that's not an option either. Is there any way at all I could build this list?

We've recently installed Timba Surveys. We want to send out a survey to the customer every time a work order, a custom object, is completed. More importantly, we want to relate their response back to the work order it came from. I already contacted Timba Surveys support and they said they don't support it so I was hoping to find a workaround.

 

The idea I had was pretty simple - pass the work order ID into the URL of the survey, then grab that ID and insert it into a lookup field on the survey response object. I was hoping I would be able to copy the visualforce page code and just add a new extension that does this, since I don't want to touch anything else. But when I try to copy the code and save it, I get this error: "Error: Constructor is not visible: [TIMBASURVEYS.SurveyController]<init>(ApexPages.StandardController)". I understand that it's not visible because it's managed, but it was the only idea I had. Is there any way around this or any other way of making this work? It's critically important for us to be able to relate it back to this custom object.

On our Project object, we have a Salesman picklist field, which just lists the last name of the salesman who sold the project. We have a Project Contact object on the Project. We want to use this Salesman field to automatically create a Project Contact looking up to the Contact record that corresponds to that salesman. Right now we're currently doing this by hardcoding the IDs of the contacts - which I understand is bad programming, but still works in production. I want to be able to do this without hardcoding the IDs, however I'm floundering over how to query just the salesmen contacts without having a huge OR statement in the where clause for all their last names, and how to then correctly grab the right salesman contact for each project in a for loop. I know I can't use a query inside the for loop. This sounds like something I'd use Maps for - is it possible to construct a Map where the last name as a String is the key and the Contact object (or ID) is the value? Or is there some other way I should do it? I'm an Apex (and programming in general) newbie. Thanks in advance!

I wanted to build a simple trigger on the Event that would make a checkbox true if the What field looked up to one of our custom objects, and false otherwise. But with my attempt at this, it keeps making the checkbox true regardless of what is related to the event and I don't understand why. Here's the code:

 

trigger WorkOrderCheckbox on Event (before insert, before update) {
 for(Event newEvent : trigger.new) {
  if(newEvent.What instanceof SVMXC__Service_Order__c) {
   newEvent.Work_Order__c = true;
  } else {
   newEvent.Work_Order__c = false;
  }
 }
}

What am I doing wrong here?

I want to be able to display a datatable for one object based on some criteria, and if the criteria is false, it will display a different datatable showing data from a different object. I have tried doing this with two separate datatables with complementary rendered attributes, but they just always came up blank. What would be the way to do this?