6

I'm getting an error 'Argument must be an inline query' on the return line. Does that mean I need to make a query of my sObject in the parameter of the getQueryLocator() as opposed to passing in a variable for a List of opportunities?

public Database.QueryLocator start(Database.BatchableContext BC) {
    return Database.getQueryLocator(woList);
}

where List<Opportunity> woList = new List<Opportunity>(trigger.new);

I would use my query there, but I'm writing a batchable class for a before Insert trigger. I can't think of anyway to write the query to identify the opportunities in trigger.new as they don't have Ids assigned yet.

Within context, I am trying to lookup a value passed in on an Opportunity (fs_wmCompanyId__c). I will then need to look to the account table to see if any account has that same value stored in a field (fs_wmCompanyId__c) on that table; if there is I will need the account ID. I will then loop back to the inserted Opportunity and set the account Id in the rs_client__c field.

I have it on a before insert trigger to ensure the rs_client__c field is set prior to saving the record; this was to make sure no validation rules prevent the update after save and that no integrations with other systems sync the data without the rs_client__c field. I figured the trigger should call a batchable class in case several hundred records were inserted at once. This is my first piece of code, so feel free to rip it apart and critique.

  • 2
    Please don't be offended but your question is quite... misguided. I don't know where to start. Yes, you will need a query (or iterable but let's not touch it). So if anything - it should probably be in "after insert". It's quite dangerous to fire batches off a trigger, you might hit the limit of max 5 jobs running at same time. And batch won't be tied to the trigger (it won't be "before" anymore when it finishes the calculation). Can you tell us what's the end result you're trying to achieve? Maybe you'd be better off with @future or scheduled batch running for example every hour? – eyescream Jan 18 '14 at 0:42
  • Not offended at all - it's my first piece of code I'm writing without any template or guide, so I expect there to be errors. – zainogj Jan 19 '14 at 20:27
  • What I'm trying to do is take a value from a field (fs_wmCompanyID__c) on an inserted opportunity, look up if that value is on any account field fs_wmCompanyID__c (unique field), and if so pass the account ID into the opportunity rs_client__c. I figure because of that, I want the action to take place before it's inserted. – zainogj Jan 19 '14 at 20:32
10

You actually need to provide a query string to the QueryLocator. If you want to use a collection instead, try using Iterable.

global Iterable start(Database.BatchableContext info){ 
    return woList; 
}  

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_batch_interface.htm

| improve this answer | |
1

This does not answer the question's title - but it does answer the business requirement described in the comment & edited v2

No, you don't need a batch job for that. Even if you have a huge job that inserts thousands of Opportunities (Data Loader? Some integration?) the whole "task" (I don't want to use the "batch (of records)" word) will be split into chunks of at most 200 records. Every chunk will count as separate transaction - so governor limits will be reset etc.

So the trigger will be separately called to deal with only that 1 chunk. (and if you'll ever run into problems in future - it's easy for the external system to split the load into smaller chunks).

I'd say you need something like this:

trigger RsClientLookup on Opportunity (before insert){

// 1. Fetch all values on Opportunities we got into 1 helper variable
Set<String> companies = new Set<String>();
for(Opportunity o : trigger.new){
    if(o.rs_client__c == null && o.fs_wmCompanyId__c != null){ // think if you need the 1st part of that if? I'd say it won't hurt to skip stuff you don't have to fetch.
        companies.add(o.fs_wmCompanyId__c);
    }
}

// 2. Look the Accounts up but store the results in a Map. Keys will be these unique company Ids, values can be anything (I'll store whole Account record, you could store only Ids for performance)

Map<String, Account> accountMap = new Map<String, Account>();
for(Account a : [SELECT Id, Name, fs_wmCompanyId__c FROM Account WHERE fs_wmCompanyId__c IN :companies]){
    accountMap.put(a.fs_wmCompanyId__c, a);
}

// 3. Finally we need to loop through trigger.new again. 
// But now we have the "dictionary" of companies we've just built
for(Opportunity o : trigger.new){
    if(accountMap.containsKey(o.fs_wmCompanyId__c)){
        Account a = accountMap.get(o.fs_wmCompanyId__c);
        System.debug('Matching ' + a.Name + ' to ' + o.Name + ' on ' + o.fs_wmCompanyId__c);
        o.rs_client__c = a.Id;
    }
}

}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.