Salesforce Apex error: SELF_REFERENCE_FROM_TRIGGER - salesforce

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger triggerOpportunityCloseInstallDateChange caused an unexpected exception, contact your administrator: triggerOpportunityCloseInstallDateChange: execution of BeforeUpdate caused by: System.DmlException: Delete failed. First exception on row 0 with id 00o30000003ySNhAAM; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 0063000000i23T9) is currently in trigger triggerOpportunityCloseInstallDateChange, therefore it cannot recursively update itself: []: Class.OpportunitySchedule.BuildScheduleAndUpdateDates: line 17, column 5
I'm getting the above error when I try to excute the code below.
I have a trigger on the "before" of an opportunity. It then calls the below class with trigger.new.
public with sharing class OpportunitySchedule {
public static void BuildScheduleAndUpdateDates(List<Opportunity> OpportunityList) {
for (Integer i = 0; i < OpportunityList.size(); i++)
{
Opportunity opp_new = OpportunityList[i];
List<OpportunityLineItem> lineItems = [Select o.Id, (Select OpportunityLineItemId From OpportunityLineItemSchedules), o.System_Add_on__c, o.ServiceDate, o.Schedule_Length__c , o.Monthly_Quantity__c, o.Monthly_Amount__c
From OpportunityLineItem o
where o.Opportunity.Id = :opp_new.Id];
for (OpportunityLineItem item : lineItems)
{
item.ServiceDate = opp_new.CloseDate;
update item;
delete item.OpportunityLineItemSchedules;
}
}
}
}
I'm trying to delete all of the Opportunity Line Item Schedules when someone edits an opportunity. The weird thing is, I can remove the delete item.OpportunityLineItemSchedules line and the code runs, it will update the item. I don't understand why deleting a childs children (Opportunity -> OpportunityLineItem -> OpportunityLineItemSchedule) would cause a recursive loop.
I've tried implimenting the below code in this link with no luck:
http://boards.developerforce.com/t5/Apex-Code-Development/Trigger-is-fired-twice-due-to-the-workflow...
I've also commented out all other triggers to make sure one of them aren't causing it.
Does anyone know what I'm doing wrong?

A few things I noticed. First, never put DML inside a loop and especially when inside of a trigger. Reading up on Bulkified triggers here would help: http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_triggers.htm
In your code you're almost there. Instead of doing your update in the loop, simply update your entire list after the loop:
for (OpportunityLineItem item : lineItems)
{
item.ServiceDate = opp_new.CloseDate;
//update item;
//delete item.OpportunityLineItemSchedules;
}
update lineItems;
Then you would make a new list of just OpportunityLineItemSchedules that had ParentId == OpportunityLineItem.Id. Then you would delete that entire list in one call:
delete saidList;
As for the recursion, in a master-detail relationship Force.com will handle the deletion of children automatically. Not so in a lookup, where you need to delete these by hand. Though I'm uncertain about OpportunityLineItemSchedules specifically, I'd try either starting things with using an AFTER trigger, or use a helper class that your trigger thread keeps in memory to ensure that once inside of your trigger handler class, that it doesn't enter it again.
Unfortunately the above is all I had a moment to share! Good luck and welcome to Force.com programming. Hope it grows on you.

I don't understand why deleting a childs children (Opportunity -> OpportunityLineItem -> OpportunityLineItemSchedule) would cause a recursive loop.
When using revenue schedules the TotalPrice on the parent OpportunityLineItem is updated based on the associated OpportunityLineItemSchedules. So when you delete the OpportunityLineItemSchedule records you are effectively updating the OpportunityLineItem, which causes the SELF_REFERENCE_FROM_TRIGGER DML Exception.
See the Effects on Opportunities and Opportunity Line Items section of the OpportunityLineItemSchedule documentation.
Deleting an OpportunityLineItemSchedule has a similar effect on the related OpportunityLineItem and Opportunity. Deleting an OpportunityLineItemSchedule decrements the OpportunityLineItem TotalPrice by the deleted OpportunityLineItemSchedule Quantity or Revenue amount. The Opportunity Amount is also decremented by the OpportunityLineItemSchedule Quantity or Revenue amount, and the Opportunity ExpectedRevenue is reduced by OpportunityLineItemSchedule Quantity or Revenue amount multiplied by the Opportunity Probability.

Related

Can somebody explain the error 'System.QueryException: List has no rows for assignment to SObject'?

everyone. Im new to apex, I was trying for code coverage of atleast 75% on my batch apex code through test class. But im getting the error " System.QueryException: List has no rows for assignment to SObject ". Execute method is not covering in the code. Will anyone point out whats wrong with the testclass?
Batch Apex Class:-
Global class BatchApexOnAccount implements Database.Batchable<sObject> {
Global Database.QueryLocator start (Database.BatchableContext Bc){
return Database.getQueryLocator('Select Id, Name from Account');
}
Global void Execute(Database.BatchableContext Bc, List<Account> AccList){
List<Account> AccEmpty = new List<Account>();
for(Account Acc : AccList){
Acc.Name = 'Salesforce';
Acc.Description = 'Update Account ' + system.today();
AccEmpty.add(Acc);
}
Update AccEmpty;
}
Global void Finish (Database.BatchableContext Bc){
}
}
Test class:-
Test code
In your test class you have initiated Account but didn't insert, and in Line 21 you are querying the fields based on that account id which doesn't exists. Because of this you are getting the QueryException.
Your test is weird. You select existing accounts. There are none. You have a piece of batch copy-pasted - why? Even if it'd work - how would you distinguish between what the test did and what the batch did?
You'd have to insert some accounts first. I think the error you get is from test's line 21 but the problems (including "Execute method is not covering in the code") are earlier.
Next time include the test's code too, screenshots are painful to retype.
I think you need something like this (there might be typos!)
private static void testBatch(){
List<Account> accs = new List<Account>{
new Account(Name = 'Unimportant'),
new Account(Name = 'Whatever', Description = 'blah blah...')
};
insert accs;
Test.startTest();
Database.executeBatch(new BatchApexOnAccount());
Test.stopTest();
for(Account a : [SELECT Name, Description FROM Account]){
System.assertEquals('Salesforce', a.Name);
System.assert(a.Description.startsWith('Update account'));
}
}
Edit: another user suggested following edit to my post. I don't agree with it but I'll put it here to preserve the idea and he can always post his own answer with improvements/reasoning.
https://stackoverflow.com/users/6718505/waldemar-liedle
All Assets (sic) in a Loop is a Bad Idea. When no Accounts are Inserted, the Test would still succeed. I would also Check something Outside Loop
List<Account> testAccs = [SELECT Name, Description FROM Account];
System.assertEquals(accs.size(), testAccs.size());
for(Account a : testAccs){
System.assertEquals('Salesforce', a.Name);
System.assert(a.Description.startsWith('Update account'));
}
So...
I don't think it matters because accounts are inserted earlier in the test. You'd need to completely mess the batch job up so it'd start deleting accounts for this query to fail finding any.
normally tests see only data that was inserted during test so it's not like it'll query thousands here (you'd run into other problems like "in test there can be only 1 execute()" so 200 accounts max)
in extreme cases querying all records to a List instead of looping through query (silently calling queryMore() when needed) will eat lots of memory

java.lang.NullPointerException when using getCurrentRow in adf

I am developing a mater-detail page where I can insert a new row into a details table (AstAssetReturnsVO) and set its referential key to the ID of the master table (AstAssetsVO). The code is run as part of a method call activity right before I get to the JSFF call.
For some reason I get the NullPointerException when it gets to the tagged line below and I have no idea why getCurrentRow returns a null pointer. I checked the documentation here and it is supposed to return the current row of the iterator corresponding to the current row of the data control.
Please let me know what I am possibly doing wrong.
Best Regards,
public void assetReturnInitialization(){
getAstAssetReturnsVO().clearCache();
getAstAssetReturnsVO().executeEmptyRowSet();
Row row = getAstAssetReturnsVO().createRow();
row.setAttribute("Stat", 99);
row.setAttribute("AsrtDate", "1396/12/31");
getAstAssetsVO().executeQuery();
Row assetRow = getAstAssetsVO().getCurrentRow(); <<<<<<< ERROR OCCURS HERE
row.setAttribute("AsetId", assetRow.getAttribute("Id"));
getAstAssetsVO().getCurrentRow().setAttribute("Status", "Returned".toUpperCase());
getAstAssetReturnsVO().insertRow(row);
getAstAssetReturnsVO().setCurrentRow(row);
return ;
}
getAstAssetsVO().executeQuery();
Row assetRow = getAstAssetsVO().getCurrentRow(); <<<<<<< ERROR OCCURS HERE
This answer is specific to above two lines of code. I did not review your use case and rest of the code.
After vo.executeQuery() API, row iterator would point to one above the first row. So, you have two options. Either use vo.hasNext() to check if there is next row and loop through the records or if you just need first row, then use vo.first() to get it. Below is the sample code for later case.
getAstAssetsVO().executeQuery();
Row assetRow = getAstAssetsVO().first();
if (assetRow != null) {
//your logic
}

Delphi Dbgrid delete a record

I have a multiple tabsheet. Each tabsheet in my PageControl that contain an Dbgrid with some button to manipulate this dbgrid.
Sometimes, when i click on button remove, my DBGrid not change. After delete, i see my value in my DBgrid.
If MyTable.recordcount > 0 then
begin
str := 'Value of name field/**' + MyTable.FieldByName('Name').AsString+'**/';
If Application.MessageBox(PChar(str), PChar('NAME'), MB_OKCANCEL + MB_ICONQUESTION) = IDOK then
begin
MyTable.Delete;
end;
end;
I try to add
MyTable.Refresh;
after delete but it doesn't work.
The value of myfield in the MessageBox is correct!
This problem appear when i select another Tabsheet in MyDBgrid.
It can be problem of focus in my DBGrid? It can be problem of state of my DBGrid(dsBrowser, dsEdit,...)?
=========================================================
I try to create a log for BeforeDelete event and AfterDelete event :
Log(1,'------------------------------------Before Del');
Log(1,Format('DataSet.IsEmpty:%s',[BoolToStr1(DataSet.IsEmpty)]));
Log(1,Format('DataSet.State:%d',[Ord(DataSet.State)]));
Log(1,'--------');
Log(1,Format('DataSet.RecNo:%d',[DataSet.RecNo]));
Log(1,Format('DataSet.TabSheet:%s',[DataSet.FieldByName('TabSheetName').AsString]));
Log(1,Format('DataSet.FieldName:%s',[DataSet.FieldByName('FieldName').AsString]));
Log(1,Format('DataSet.FieldId:%s',[DataSet.FieldByName('FieldId').AsString]));
on AfterDelete event :
Log(1,'------------------------------------After Del');
Log(1,Format('DataSet.IsEmpty:%s',[BoolToStr1(DataSet.IsEmpty)]));
Log(1,Format('DataSet.State:%d',[Ord(DataSet.State)]));
Log(1,'--------');
Log(1,Format('DataSet.RecNo:%d',[DataSet.RecNo]));
Log(1,Format('DataSet.TabSheet:%s',[DataSet.FieldByName('TabSheetName').AsString]));
Log(1,Format('DataSet.FieldName:%s',[DataSet.FieldByName('FieldName').AsString]));
Log(1,Format('DataSet.FieldId:%s',[DataSet.FieldByName('FieldId').AsString]));
I use the tabsheet as :
enter image description here
I obtain this log
------------------------------------Before Del
DataSet.IsEmpty:False
DataSet.State:1
--------
DataSet.RecNo:7
DataSet.TabSheet:tabsheet_0
DataSet.FieldName:
DataSet.FieldId:03
------------------------------------After Del
DataSet.IsEmpty:False
DataSet.State:1
--------
DataSet.RecNo:6
DataSet.TabSheet:tabsheet_0
DataSet.FieldName:
DataSet.FieldId:03
You can see in my log: the dataset not change (filedname and fieldId not change / the RecNo properties is change ) after delete the row that contain 03.
Since your problem is intermittent, you'll have to debug it yourself, because readers can't do that for you.
The first thing to do is to set up a logging function and call it in the dataset's BeforeDelete and AfterDelete events. You should record in it the identity of the current dataset row, the dataset's state (dsBrowse, dsEdit, etc) and anything other you think might be relevant (e.g. the identity of the active tabsheet) and see if you can spot in what circumstances the Delete fails. You could write the results of your logging calls to a TMemo on your form.
Ime, it's good practice only to allow a Delete if the dataset is in dsBrowse state.
By the way, I assume you are aware that a Delete operation is automatically aborted if the dataset is in dsInsert or dsSetKey state? From what you've described, I'd start debugging by investigating whether it's in dsInsert state when your problem arises. Of course, you can catch when the dataset is being put into dsInsert state by catching its BeforeInsert event and calling your logging function from there.

How to select a row by default in af:table in Oracle ADF

I have a requirement in which I have an af:query panel which after querying populates an af:table.
Now based on the table rows when any 1 row is selected, the graphs below it should be populated based on some columns.
Now my problem is that on search when the table is populated for the first time, no row is selected.
I need the first row to be selected automatically. I have searched multiple solutions from the net forums but till now haven't found any working solution.
Please help me what code should I use to select a row programmatically. and also where should I put this code in the backing bean.
As you've correctly mentioned, at the first render of the table no row is selected. I've encountered this problem too, and i've dealt with it by calling a getting the first row of the corresponding VO on BeforePhase (i.e: on the page first rendering or refresh).
I understand that you intend to do so for the first searching.
The af:query component has a property called QueryListener. You can link it with a method inside a backing bean. The content should be like:
private QueryEvent qEvent = null;
public void queryListener(QueryEvent queryEvent) {
setQEvent(queryEvent);
JSFUtils.invokeMethodExpression("#{bindings.YourViewObjectNameViewCriteriaQuery.processQuery}", Object.class,
QueryEvent.class, getQEvent());
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry(); //Edited
DCBindingContainer bc = (DCBindingContainer)bindings;
DCIteratorBinding iterator = bc.findIteratorBinding("YourViewObject1Iterator");
Row r = iterator.getCurrentRow(); //Here you've got the very first row, and can operate with it
.....
}
public void setQEvent(QueryEvent qEvent) {
this.qEvent = qEvent;
}
public QueryEvent getQEvent() {
return qEvent;
}
With this, you should be able to get the first row when the query is executed (i.e: when the search is done).
After getting the first row, you can programmatically the graph process execution or whatever you do.
NOTE: invokeMethodExpression can be found inside JSFUtils, which is a starndard class with static methods which source code you can download here: JSFUtils.java

How do I avoid STANDARD_PRICE_NOT_DEFINED when unit-testing an OpportunityLineItem in Apex v24.0?

Apparently a new feature of the Spring '12 / v24.0 release of Apex in Salesforce.com is that unit tests no longer have access to 'real' data -- thus (if I'm understanding the change correctly) a SOQL query will now only retrieve objects that have been inserted during the course of the unit test -- and even that is subject to some limitations.
At any rate this seems to throw OpportunityLineItem testing out the window, because:
It's impossible to insert an OpportunityLineItem without a PriceBookEntryId, BUT
You can't insert a new price-book entry for product X unless you already have a Standard Price Book entry for product X, BUT
There isn't a Standard Price Book in the test data because the Pricebook2 table, like all tables, is effectively empty at the beginning of the unit-test run, AND
There's no way to create a Standard Price Book in Apex
I'm really hoping I got at least one of those four points wrong, but so far no variation on my existing unit-tests has shown any of them to be wrong. Which of course means my unit tests no longer work. This happened literally overnight -- the tests ran fine in my sandbox on Friday, and now they fail.
Am I missing something, or is this a bug in the new SFDC release?
There is new functionality introduced in Summer 14, you can now use Test.getStandardPricebookId() to get the standard pricebook ID without having to set SeeAllData to True.
Firstly, to put your mind at ease, there are no plans ever to deprecate the seeAllData flag. We're not going to pull the rug out from under you. As to the creation of standard price book in an apex test, I'm not sure. There are, I'm sure, several areas where testing without existing data is difficult on the platform today, which is one reason why the seeAllData flag is there. We'll be trying to close those gaps in the next few releases.
I just ran into this, and although your post is old, it's the first result on Google so I thought I'd share what I did.
My basic architecture is a test class that calls a utility class to as a way of creating test data on the fly (there are other ways, this is my habit).
Short version:
set see all data to true
make sure the standard price book is active
add a pricebook entry for the standard price book - flag as active
add a pricebook entry for you test price book - flag as active
Test class:
#isTest (seeAllData=true)
public with sharing class RMA_SelectLineItemsControllerTest {
static testmethod void testBasicObjects() {
Pricebook2 standard = [Select Id, Name, IsActive From Pricebook2 where IsStandard = true LIMIT 1];
if (!standard.isActive) {
standard.isActive = true;
update standard;
}
Pricebook2 pb = RMA_TestUtilities.createPricebook();
Product2 prod = RMA_TestUtilities.createProduct();
PricebookEntry pbe = RMA_TestUtilities.createPricebookEntry(standard,pb,prod);
}
}
The utility method look like this (only showing that around the new PBE):
public static PricebookEntry createPricebookEntry (Pricebook2 standard, Pricebook2 newPricebook, Product2 prod) {
System.debug('***** starting one');
PricebookEntry one = new PricebookEntry();
one.pricebook2Id = standard.id;
one.product2id = prod.id;
one.unitprice = 1249.0;
one.isactive = true;
insert one;
System.debug('***** one complete, ret next');
PricebookEntry ret = new PricebookEntry();
ret.pricebook2Id = newPricebook.id;
ret.product2id = prod.id;
ret.unitprice = 1250.0;
ret.isactive = true;
insert ret;
return ret;
}
Another work around would be to make your trigger be aware of being run in a test using Test.isRunningTest(), but I think this solution misses the point of best practice, which I believe is the whole point of making tests isolated from pre-existing data.
Perhaps Salesforce could make the Pricebook2.isStandard field writeable if code is running in the context of a test, or the specific Standard Price Book record should be given the same status as User and Profile??
Please let me know if anyone has used Test.getStandardPricebookId() and able to insert opportunity line item in test class. I tried this method with below mentioned code but got an error ": STANDARD_PRICE_NOT_DEFINED, No standard price defined for this product: []".
Note: I have seeAllData=false
ID standardPBID = Test.getStandardPricebookId();
PriceBook2 pb = new PriceBook2();
pb.name = 'GEW Water CMS';
pb.isActive=true;
insert pb;
Product2 prod= new Product2();
prod.name='TestProd';
prod.productcode='4568';
prod.isActive=true;
insert prod;
PricebookEntry standardPrice = new PricebookEntry(Pricebook2Id = standardPBID, Product2Id = prod.Id, UnitPrice = 10000, IsActive = true, currencyISOCode='USD' );
PriceBookEntry pbe= new PricebookEntry(pricebook2id=pb.id, product2id=prod.id,unitprice=2000, isActive=true, currencyISOCode='EUR');
insert pbe;
OpportunityLineItem oli = new OpportunityLineItem(OpportunityId = OppList[0].Id, pricebookentryid=pbe.id, UnitPrice = 100, Quantity = 1);
insert oli;

Resources