Salesforce Trigger Test Error - unit-testing
Hello!
I am working on unit tests for trigger within Salesforce and I keep encountering an error that I can't seem to solve so I am hoping someone with more experience can help me get back on track. I've hunted around on Google and messed with the structure of my code many times but I'm unable to find a solution.
Purpose:
I have been tasked with writing a trigger that will handle the logic required to maintain case rankings per developer. Each developer is assigned cases and those cases may or may not have a priority determined by the business. Each developer may only have 10 cases prioritized at any one time. Any other cases will just have a null value in the ranking field. If a case with a ranking is inserted, updated, or deleted then all the other cases assigned to that developer with a rank must automatically update accordingly. Any case with a rank higher than 10 will get nulled out.
Problem:
I have completed the trigger and trigger handler class now I am writing unit tests to cover all the unit tests. When I finished my first unit test method I got an error that referenced a workflow issue. I found and corrected the issue but after I finished my second unit test method I get the same error again. I can comment out either of the two methods and the other passes but whenever I run them together I get a pass on the first and a fail on the second with the same original error.
Error:
System.DmlException: Upsert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, A workflow or approval field update caused an error when saving this record. Contact your administrator to resolve it.
Developer Assigned Email: invalid email address: false: []
Code:
Trigger Code -
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/09/15
* #brief: This is a trigger for the Case object that will modify the rank of the Cases
* assigned to the developer based on a priority set by the Administrator.
***************************************************************************************/
trigger CaseRankTrigger on Case (before insert, before update, before delete) {
// trigger level variables
private static Boolean firstRun = true;
private RecordType ticketRecordType = [SELECT Id FROM RecordType WHERE SobjectType = 'Case' AND Name = 'Salesforce Service Ticket' LIMIT 1];
private List<Case> newTrigger = trigger.new;
private List<Case> currentTrigger = trigger.old;
private List<Case> qualifiedNewCases = new List<Case>();
private List<Case> qualifiedCurrentCases = new List<Case>();
// makes sure that the trigger only runs once
if (firstRun) {
firstRun = false;
// populate trigger Case lists
qualifyCases();
if (qualifiedNewCases.size() == 1 || qualifiedCurrentCases.size() == 1) {
// the CaseRankTriggerHandler constructor method takes (List<Case>, List<Case>, String)
if (trigger.isInsert) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(qualifiedNewCases, qualifiedCurrentCases, 'Insert'); // if a new Case is being inserted into the database
if (trigger.isUpdate) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(qualifiedNewCases, qualifiedCurrentCases, 'Update'); // if an existing Case is being updated
if (trigger.isDelete) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(qualifiedNewCases, qualifiedCurrentCases, 'Delete'); // if an existing Case is deleted from the database
}
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/24/15
* #brief: The qualifyCases method populates a list of Cases for each trigger
* that are of the Salesforce Service Ticket record type only.
* #return: Void
***************************************************************************************/
public void qualifyCases() {
if (newTrigger != null ) {
for (Case c : newTrigger) {
if (c.recordTypeId == ticketRecordType.Id) {
qualifiedNewCases.add(c);
}
}
}
if (currentTrigger != null) {
for (Case c : currentTrigger) {
if (c.recordTypeId == ticketRecordType.Id) {
qualifiedCurrentCases.add(c);
}
}
}
}
}
Trigger Handler Code -
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/09/15
* #brief: This is a Case object trigger handler class that provides logic to the CaseRankTrigger for manipulating
* the ranks of all Cases assigned to a developer based on a priority that is set by an Administrator.
***************************************************************************************/
public with sharing class CaseRankTriggerHandler {
// class level variables
private static Boolean firstRun = true;
private static Boolean modify = false;
private static Integer MAX = 10;
private static Integer MIN = 1;
private List<Case> newTrigger {get; set;}
private List<Case> currentTrigger {get; set;}
private List<Case> cases {get; set;}
private List<Case> newList {get; set;}
private List<Case> currentList {get; set;}
private String developer {get; set;}
private Decimal newRank {get; set;}
private Decimal currentRank {get; set;}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: Class constructor method.
* #return: Void
***************************************************************************************/
public CaseRankTriggerHandler(List<Case> newT, List<Case> oldT, String type) {
if (firstRun) { // makes sure that the trigger only runs once
firstRun = false;
InitializeTrigger(newT, oldT, type); // initializes the trigger
if (developer != null) { // skips trigger if DML is performed on a Case with no developer assigned
ModificationCheck(type); // determines if Cases need to be modified
if (modify) ModificationLogic(type); // modifies Cases if needed
}
}
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: The InitializeTrigger method initializes the handler class based on the type of trigger fired.
* #return: Void
***************************************************************************************/
private void InitializeTrigger(List<Case> newT, List<Case> oldT, String type) {
if (type == 'Insert') {
this.newTrigger = newT;
this.developer = newTrigger[0].Resource_Assigned__c;
this.newRank = newTrigger[0].Case_Rank__c;
this.newList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :newRank ORDER BY Case_Rank__c];
} else if (type == 'Update') {
this.newTrigger = newT;
this.currentTrigger = oldT;
this.developer = newTrigger[0].Resource_Assigned__c;
this.newRank = newTrigger[0].Case_Rank__c;
this.currentRank = currentTrigger[0].Case_Rank__c;
this.newList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :newRank ORDER BY Case_Rank__c];
this.currentList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :currentRank ORDER BY Case_Rank__c];
} else if (type == 'Delete') {
this.currentTrigger = oldT;
this.developer = currentTrigger[0].Resource_Assigned__c;
this.currentRank = currentTrigger[0].Case_Rank__c;
}
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: The ModificationCheck method ensures various conditions are met, depending on the type
* of trigger that was fired, before modifying the ranks of the Cases assigned to the developer.
* #return: Void
***************************************************************************************/
private void ModificationCheck(String type) {
if (type == 'Insert') {
// the Case being inserted has a new rank not equal to null and if the assigned developer already has a Case with the
// same rank as the new rank, we will proceed to modification, if not the record will be inserted without modification.
if (newRank != null && !newList.isEmpty()) {
modify = true;
}
} else if (type == 'Update') {
// if the Case being updated has ranks with different values in both triggers we will proceed to the next check, if not the record is updated without modification.
if (newRank != currentRank) {
// if the Case being updated has a (new rank equal to null and a current rank not equal to 10) or
// if the Case being updated has a new rank not equal to null, we will proceed to the next check,
// if not the record is updated without modification.
if ((newRank == null && currentRank != 10) || newRank != null) {
// if the assigned developer on the Case being updated already has a Case with the same rank as the new or current rank, we will proceed to modification,
// if not the record is updated without modification.
if (!newList.isEmpty() || !currentList.isEmpty()) {
modify = true;
}
}
}
} else if (type == 'Delete') {
// if the Case being deleted has current rank not equal to null, we will proceed to modification, if not the record is deleted without modification.
if (currentRank != null) {
modify = true;
}
}
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: If a Case rank needs to be updated the ModificationLogic method calls the appropriate
* computation method based on trigger type and the values of newRank and currentRank.
* #return: Void
***************************************************************************************/
private void ModificationLogic(String type) {
if (type == 'Insert') {
for (Case c : newTrigger) {
// calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than or equal to the new rank.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c >= :newRank ORDER BY Case_Rank__c]);
}
} else if (type == 'Update') {
for (Case c : newTrigger) {
if (currentRank == null) {
// if the current rank is null - calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than or equal to the new rank.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c >= :newRank ORDER BY Case_Rank__c]);
} else if (newRank == null) {
// if the new rank is null - calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c > :currentRank ORDER BY Case_Rank__c]);
} else if (newRank > currentRank) {
// if the new rank is greater than the current rank - calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank less than or equal to the new rank and greater than to the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND (Case_Rank__c <= :newRank AND Case_Rank__c > :currentRank) ORDER BY Case_Rank__c]);
} else if (newRank < currentRank) {
// if the new rank is less than the current rank - calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank a.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND (Case_Rank__c >= :newRank AND Case_Rank__c < :currentRank) ORDER BY Case_Rank__c]);
}
}
} else if (type == 'Delete') {
for (Case c : currentTrigger) {
// calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :currentTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c > :currentRank ORDER BY Case_Rank__c]);
}
}
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: The DecreaseCaseRank method provides the logic required to properly
* decrease or null out the ranks of the Cases assigned the the developer.
* #return: Void
***************************************************************************************/
private void DecreaseCaseRank(List<Case> cases) {
// if the list of Cases passed in by the ModificationLogic method isn't empty then it will iterate through the
// list and decrease their ranks by 1 or null out the rank if it is not within the acceptable limits (1-10).
if (!cases.isEmpty()) {
for (Case c : cases) {
if (c.Case_Rank__c >= 1 && c.Case_Rank__c <= 10) {
c.Case_Rank__c = c.Case_Rank__c - 1;
} else {
c.Case_Rank__c = null;
}
}
update cases;
}
return;
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/16/15
* #brief: The IncreaseCaseRank method provides the logic required to properly
* increase or null out the ranks of the Cases assigned the the developer.
* #return: Void
***************************************************************************************/
private void IncreaseCaseRank(List<Case> cases) {
// if the list of Cases passed in by the ModificationLogic method isn't empty then it will iterate through the
// list and increase their ranks by 1 or null out the rank if it is not within the acceptable limits (1-10).
if (!cases.isEmpty()) {
for (Case c : cases) {
if (c.Case_Rank__c >= 1 && c.Case_Rank__c < 10) {
c.Case_Rank__c = c.Case_Rank__c + 1;
} else {
c.Case_Rank__c = null;
}
}
update cases;
}
return;
}
}
Trigger Handler Test Code -
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/24/15
* #brief: This is the test class for the CaseRankTriggerHandler class
***************************************************************************************/
#isTest
public with sharing class CaseRankTriggerHandlerTest {
// class level variables
static User testRequestor = createTestRequestor();
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/24/15
* #brief: The InsertCase_NewRankNull test method ensures that the insert functionality of the
* CaseRankTrigger is working as intended when a new Case is inserted with a null rank.
***************************************************************************************/
#isTest
static void InsertCase_NewRankNull() {
// creates the initial case load for 'Test Developer' by passing in a list of integers that will become the ranks for the cases
createDeveloperCase_Multiple(new List<Integer> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// starting the test by inserting a new Case with a null rank
Test.startTest();
createDeveloperCase_Single('Null', null);
Test.stopTest();
// queries the system to create a map of Cases assigned to 'Test Developer' that are keyed by Rank with Subject as the value
Map<Decimal, String> caseMap = createCaseMap();
// system asserts to ensure that Cases are in the proper order
System.assertEquals('Test Case (1)', caseMap.get(1), 'Test Developer should have \'Test Case (1)\' as rank 1 but instead has ' + caseMap.get(1));
System.assertEquals('Test Case (2)', caseMap.get(2), 'Test Developer should have \'Test Case (2)\' as rank 2 but instead has ' + caseMap.get(2));
System.assertEquals('Test Case (3)', caseMap.get(3), 'Test Developer should have \'Test Case (3)\' as rank 3 but instead has ' + caseMap.get(3));
System.assertEquals('Test Case (4)', caseMap.get(4), 'Test Developer should have \'Test Case (4)\' as rank 4 but instead has ' + caseMap.get(4));
System.assertEquals('Test Case (5)', caseMap.get(5), 'Test Developer should have \'Test Case (5)\' as rank 5 but instead has ' + caseMap.get(5));
System.assertEquals('Test Case (6)', caseMap.get(6), 'Test Developer should have \'Test Case (6)\' as rank 6 but instead has ' + caseMap.get(6));
System.assertEquals('Test Case (7)', caseMap.get(7), 'Test Developer should have \'Test Case (7)\' as rank 7 but instead has ' + caseMap.get(7));
System.assertEquals('Test Case (8)', caseMap.get(8), 'Test Developer should have \'Test Case (8)\' as rank 8 but instead has ' + caseMap.get(8));
System.assertEquals('Test Case (9)', caseMap.get(9), 'Test Developer should have \'Test Case (9)\' as rank 9 but instead has ' + caseMap.get(9));
System.assertEquals('Test Case (10)', caseMap.get(10), 'Test Developer should have \'Test Case (10)\' as rank 10 but instead has ' + caseMap.get(10));
System.assertEquals('Test Case (Null)', caseMap.get(null), 'Test Developer should have \'Test Case (Null)\' as rank null but instead has ' + caseMap.get(null));
delete [SELECT Id FROM Case WHERE Resource_Assigned__c = 'Test Developer'];
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/24/15
* #brief: The InsertCase_NewRankNotNull test method ensures that the insert functionality of the
* CaseRankTrigger is working as intended when a new Case is inserted with a rank that is not null.
***************************************************************************************/
#isTest
static void InsertCase_NewRankNotNull() {
// creates the initial case load for 'Test Developer' by passing in a list of integers that will become the ranks for the cases
createDeveloperCase_Multiple(new List<Integer> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// starting the test by inserting a new Case with a null rank
Test.startTest();
createDeveloperCase_Single('NewNotNull', 4);
Test.stopTest();
// queries the system to create a map of Cases assigned to 'Test Developer' that are keyed by Rank with Subject as the value
Map<Decimal, String> caseMap = createCaseMap();
// system asserts to ensure that Cases are in the proper order
System.assertEquals('Test Case (1)', caseMap.get(1), 'Test Developer should have \'Test Case (1)\' as rank 1 but instead has ' + caseMap.get(1));
System.assertEquals('Test Case (2)', caseMap.get(2), 'Test Developer should have \'Test Case (2)\' as rank 2 but instead has ' + caseMap.get(2));
System.assertEquals('Test Case (3)', caseMap.get(3), 'Test Developer should have \'Test Case (3)\' as rank 3 but instead has ' + caseMap.get(3));
System.assertEquals('Test Case (NewNotNull)', caseMap.get(4), 'Test Developer should have \'Test Case (NewNotNull)\' as rank 4 but instead has ' + caseMap.get(4));
System.assertEquals('Test Case (4)', caseMap.get(5), 'Test Developer should have \'Test Case (4)\' as rank 5 but instead has ' + caseMap.get(5));
System.assertEquals('Test Case (5)', caseMap.get(6), 'Test Developer should have \'Test Case (5)\' as rank 6 but instead has ' + caseMap.get(6));
System.assertEquals('Test Case (6)', caseMap.get(7), 'Test Developer should have \'Test Case (6)\' as rank 7 but instead has ' + caseMap.get(7));
System.assertEquals('Test Case (7)', caseMap.get(8), 'Test Developer should have \'Test Case (7)\' as rank 8 but instead has ' + caseMap.get(8));
System.assertEquals('Test Case (8)', caseMap.get(9), 'Test Developer should have \'Test Case (8)\' as rank 9 but instead has ' + caseMap.get(9));
System.assertEquals('Test Case (9)', caseMap.get(10), 'Test Developer should have \'Test Case (9)\' as rank 10 but instead has ' + caseMap.get(10));
System.assertEquals('Test Case (10)', caseMap.get(null), 'Test Developer should have \'Test Case (10)\' as rank null but instead has ' + caseMap.get(null));
delete [SELECT Id FROM Case WHERE Resource_Assigned__c = 'Test Developer'];
}
/***************************************************************************************
* #author: Michael *REDACTED*
* #email: michael.*REDACTED*#*REDACTED*.com
* #date: 11/24/15
* #brief: The createCaseMap method queries all the developers Cases then creates a map
* keyed by Rank with the Subject as the value. This map will be used to ensure that
* the Cases are in the proper order after any DML has been performed on a Case.
* #return: Map<Decimal, String>
***************************************************************************************/
static Map<Decimal, String> createCaseMap() {
List<Case> caseList = [SELECT Case_Rank__c, Subject FROM Case WHERE Resource_Assigned__c = 'Test Developer' ORDER BY Case_Rank__c];
Map<Decimal, String> caseMap = new Map<Decimal, String>();
for (Case c : caseList) {
caseMap.put(c.Case_Rank__c, c.Subject);
}
return caseMap;
}
/***************************************************************************************
* TEST DATA SECTION - Refactor out of test class after creating Test Data Factory
***************************************************************************************/
static User createTestRequestor() {
Profile testProfile = [SELECT Id from Profile where Name = 'Standard User'];
User requestor = new User(FirstName = 'Test', LastName = 'Requestor', Alias = 'Test.Req', Email = 'newtestrequestor#null.com', UserName = 'newtestrequestor#null.com', ProfileId = testProfile.Id,
TimeZoneSidKey = 'America/Los_Angeles', LocaleSidKey = 'en_US', EmailEncodingKey = 'UTF-8', LanguageLocaleKey = 'en_US');
insert requestor;
return requestor;
}
static List<Case> createDeveloperCase_Multiple(List<Integer> ranks) {
List<Case> developerCaseLoad = new List<Case>();
Case developerCase;
Integer count = 0;
for (Integer rank : ranks) {
count++;
developerCase = new Case(Subject = 'Test Case (' + count + ')', Service_Request_Type__c = 'Development', Requestor__c = testRequestor.Id, Description = 'Foo', Business_Value_of_Change__c = 'Bar',
Business_Area__c = 'Warranty', Requested_Implementation_Date__c = Date.today(), Resource_Assigned__c = 'Test Developer', Resource_Assigned_Email__c = 'newtestdeveloper#null.com', Case_Rank__c = rank);
developerCaseLoad.add(developerCase);
}
for (Case c : developerCaseLoad) {
}
upsert developerCaseLoad;
return developerCaseLoad;
}
static Case createDeveloperCase_Single(String name, Integer rank) {
Case developerCase = new Case(Subject = 'Test Case (' + name + ')', Service_Request_Type__c = 'Development', Requestor__c = testRequestor.Id, Description = 'Foo', Business_Value_of_Change__c = 'Bar',
Business_Area__c = 'Warranty', Requested_Implementation_Date__c = Date.today(), Resource_Assigned__c = 'Test Developer', Case_Rank__c = rank);
upsert developerCase;
return developerCase;
}
}
Workflow Code - I didn't write this one, but click to see pic
CASE( Resource_Assigned__c ,
"Kimberly REDACTED","foo#bar.com",
"Josh REDACTED","foo#bar.com",
"Robert REDACTED","foo#bar.com",
"Jose REDACTED","foo#bar.com",
"Ryan REDACTED","foo#bar.com",
"Lloyd REDACTED","foo#bar.com",
"Nathan REDACTED","foo#bar.com",
"Amber REDACTED","foo#bar.com",
"Ora REDACTED","foo#bar.com",
"Jason REDACTED","foo#bar.com",
"Shalini REDACTED","foo#bar.com",
"Siva REDACTED","foo#bar.com",
"Quinn REDACTED","foo#bar.com",
"Adrienne REDACTED","foo#bar.com",
"Vasantha REDACTED","foo#bar.com",
"Michael REDACTED","foo#bar.com",
"Sudheera REDACTED","foo#bar.com",
"Test Developer","newtestdeveloper#null.com",
"false")
I really appreciate any help you all can give me on this one!
Regards,
Michael
Here is what I did to fix my issue.
I refactored the test and removed the creation of the two test developers. Instead I grabbed two random developers that are contained in the dropdown list we use and then I used those developers in the test.
Due to the way everything is set up, I did not need to use production data (SeeAllData=true) to make this fix work and after making the code change I never had another issue with testing.
Related
Doctrine2 - Doctrine generating query with associated entity - InvalidFieldNameException
Yep, the title suggests: Doctrine is looking for a fieldname that's not there. That's both true and not true at the same time, though I cannot figure out how to fix it. The full error: File: D:\path\to\project\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\AbstractMySQLDriver.php:71 Message: An exception occurred while executing 'SELECT DISTINCT id_2 FROM (SELECT p0_.name AS name_0, p0_.code AS code_1, p0_.id AS id_2 FROM product_statuses p0_) dctrn_result ORDER BY p0_.language_id ASC, name_0 ASC LIMIT 25 OFFSET 0': SQLSTATE[42S22]: Column not found: 1054 Unknown column 'p0_.language_id' in 'order clause' The query the error is caused by (from error above): SELECT DISTINCT id_2 FROM ( SELECT p0_.name AS name_0, p0_.code AS code_1, p0_.id AS id_2 FROM product_statuses p0_ ) dctrn_result ORDER BY p0_.language_id ASC, name_0 ASC LIMIT 25 OFFSET 0 Clearly, that query is not going to work. The ORDER BY should be in the sub-query, or else it should replace p0_ in the ORDER BY with dctrn_result and also get the language_id column in the sub-query to be returned. The query is build using the QueryBuilder in the indexAction of a Controller in Zend Framework. All is very normal and the same function works perfectly fine when using a the addOrderBy() function for a single ORDER BY statement. In this instance I wish to use 2, first by language, then by name. But the above happens. If someone knows a full solution to this (or maybe it's a bug?), that would be nice. Else a hint in the right direction to help me solve this issue would be greatly appreciated. Below additional information - Entity and indexAction() ProductStatus.php - Entity - Note the presence of language_id column /** * #ORM\Table(name="product_statuses") * #ORM\Entity(repositoryClass="Hzw\Product\Repository\ProductStatusRepository") */ class ProductStatus extends AbstractEntity { /** * #var string * #ORM\Column(name="name", type="string", length=255, nullable=false) */ protected $name; /** * #var string * #ORM\Column(name="code", type="string", length=255, nullable=false) */ protected $code; /** * #var Language * #ORM\ManyToOne(targetEntity="Hzw\Country\Entity\Language") * #ORM\JoinColumn(name="language_id", referencedColumnName="id") */ protected $language; /** * #var ArrayCollection|Product[] * #ORM\OneToMany(targetEntity="Hzw\Product\Entity\Product", mappedBy="status") */ protected $products; [Getters/Setters] } IndexAction - Removed parts not directly related to QueryBuilder. Added in comments showing params as they are. /** #var QueryBuilder $qb */ $qb = $this->getEntityManager()->createQueryBuilder(); $qb->select($asParam) // 'pro' ->from($emEntity, $asParam); // Hzw\Product\Entity\ProductStatus, 'pro' if (count($queryParams) > 0 && !is_null($query)) { // [...] creates WHERE statement, unused in this instance } if (isset($orderBy)) { if (is_array($orderBy)) { // !!! This else is executed !!! <----- if (is_array($orderDirection)) { // 'ASC' // [...] other code } else { // $orderBy = ['language', 'name'], $orderDirection = 'ASC' foreach ($orderBy as $orderParam) { $qb->addOrderBy($asParam . '.' . $orderParam, $orderDirection); } } } else { // This works fine. A single $orderBy with a single $orderDirection $qb->addOrderBy($asParam . '.' . $orderBy, $orderDirection); } } ================================================ UPDATE: I found the problem The above issue is not caused by incorrect mapping or a possible bug. It's that the QueryBuilder does not automatically handle associations between entities when creating queries. My expectation was that when an entity, such as ProductStatus above, contains the id's of the relation (i.e. language_id column), that it would be possible to use those properties in the QueryBuilder without issues. Please see my own answer below how I fixed my functionality to be able to have a default handling of a single level of nesting (i.e. ProducStatus#language == Language, be able to use language.name as ORDER BY identifier).
Ok, after more searching around and digging into how and where this goes wrong, I found out that Doctrine does not handle relation type properties of entities during the generation of queries; or maybe does not default to using say, the primary key of an entity if nothing is specified. In the use case of my question above, the language property is of a #ORM\ManyToOne association to the Language entity. My use case calls for the ability to handle at lease one level of relations for default actions. So after I realized that this is not handled automatically (or with modifications such as language.id or language.name as identifiers) I decided to write a little function for it. /** * Adds order by parameters to QueryBuilder. * * Supports single level nesting of associations. For example: * * Entity Product * product#name * product#language.name * * Language being associated entity, but must be ordered by name. * * #param QueryBuilder $qb * #param string $tableKey - short alias (e.g. 'tab' with 'table AS tab') used for the starting table * #param string|array $orderBy - string for single orderBy, array for multiple * #param string|array $orderDirection - string for single orderDirection (ASC default), array for multiple. Must be same count as $orderBy. */ public function createOrderBy(QueryBuilder $qb, $tableKey, $orderBy, $orderDirection = 'ASC') { if (!is_array($orderBy)) { $orderBy = [$orderBy]; } if (!is_array($orderDirection)) { $orderDirection = [$orderDirection]; } // $orderDirection is an array. We check if it's of equal length with $orderBy, else throw an error. if (count($orderBy) !== count($orderDirection)) { throw new \InvalidArgumentException( $this->getTranslator()->translate( 'If you specify both OrderBy and OrderDirection as arrays, they should be of equal length.' ) ); } $queryKeys = [$tableKey]; foreach ($orderBy as $key => $orderParam) { if (strpos($orderParam, '.')) { if (substr_count($orderParam, '.') === 1) { list($entity, $property) = explode('.', $orderParam); $shortName = strtolower(substr($entity, 0, 3)); // Might not be unique... $shortKey = $shortName . '_' . (count($queryKeys) + 1); // Now it's unique, use $shortKey when continuing $queryKeys[] = $shortKey; $shortName = strtolower(substr($entity, 0, 3)); $qb->join($tableKey . '.' . $entity, $shortName, Join::WITH); $qb->addOrderBy($shortName . '.' . $property, $orderDirection[$key]); } else { throw new \InvalidArgumentException( $this->getTranslator()->translate( 'Only single join statements are supported. Please write a custom function for deeper nesting.' ) ); } } else { $qb->addOrderBy($tableKey . '.' . $orderParam, $orderDirection[$key]); } } } It by no means supports everything the QueryBuilder offers and is definitely not a final solution. But it gives a starting point and solid "default functionality" for an abstract function.
Salesforce Apex unit testing error
I am trying to create a Salesforce unit test for a new trigger I created. trigger SOSCreateCaseCustom on SOSSession (before insert) { List<Event> aplist = new List<Event>(); List<SOSSession> sosSess = Trigger.new; for (SOSSession s : sosSess) { try { Case caseToAdd = new Case(); caseToAdd.Subject = 'SOS Video Chat'; if (s.ContactId != null) { caseToAdd.ContactId = s.ContactId; } else { List<Contact> contactInfo = [SELECT Id from Contact WHERE Email = :s.AppVersion]; if (!contactInfo.isEmpty()) { caseToAdd.ContactId = contactInfo[0].Id; s.ContactId = contactInfo[0].Id; } } insert caseToAdd; s.CaseId = caseToAdd.Id; }catch(Exception e){} } } Here is my unit test: #isTest private class SOSCreateCaseCustomTest { static testMethod void validateSOSCreateCase() { String caseSubject = 'SOS Video Chat'; // set up case to add SOSSession s = new SOSSession(); insert s; Case caseToAdd = new Case(Subject='SOS Video Chat'); caseToAdd.ContactId = s.ContactId; insert caseToAdd; Case ca = [SELECT Subject, ContactId from Case where Subject =: caseSubject]; // Test that escaltion trigger correctly escalate the question to a case System.assertEquals(s.ContactId, ca.ContactId); } } I keep getting this error. System.QueryException: List has more than 1 row for assignment to SObject I am new to Apex and I have no idea how to fix this. Any Salesforce and Apex experts out there who can help? Thanks!
I think this one: Case ca = [SELECT Subject, ContactId from Case where Subject =: caseSubject]; Because the casSubject may query more then one Case.... You should use List
The following line is causing issue : Case ca = [SELECT Subject, ContactId from Case where Subject =: caseSubject]; It is returning two cases, the one you inserted in test data and other that is inserted by trigger. So it is having two records for Subject 'SOS Video Chat'; If you change the Subject from 'SOS Video Chat' to any other String it will run successfully.
Test class not covering method fully?
Hi i have a custom controller which clones opportunity with a new currency,clones quote and all its quote line item.A button on Quote page layout does all this.While writing a test class to cover this code,i could not cover certain lines and do not know why.The code is as follows : CloneOppNewCurrencyController.cls : public with sharing class CloneOppNewCurrencyController { public List<DatedConversionRate>lstCurrencies{get;set;} public String selectedCurrency{get;set;} public List<SelectOption> cTypes{get;set;} public boolean flag{get;set;} public Date closeDate; public String qId{get;set;} public Quote qObj{get;set;} public Quote qClone{get;set;} public Opportunity oppObj; public Opportunity oppClone{get;set;} public List<QuoteLineItem> lstQuoteLineItems; public List<QuoteLineItem> lstClonedQuoteLineItems; public QuoteLineItem clonedQuoteLineItem; public Double exchangeRate; public Map<String,Double>mapExchangeRates; public List<PriceBookEntry> pbe; public List<Id>pb; public Quote testClone{get;set;} public Map<Id,PricebookEntry>mapProIdNpBookEntry; public List<Renewed_Entitlement__c> lstRE; public List<Quoted_Asset__c> lstQuotedAsset; public Renewed_Entitlement__c cloneRE; public Quoted_Asset__c cloneQA; //Constructor public CloneOppNewCurrencyController() { pb = new List<Id>(); lstRE = new List<Renewed_Entitlement__c>(); lstQuotedAsset = new List<Quoted_Asset__c>(); lstclonedQuoteLineItems = new List<QuoteLineItem>(); mapProIdNpBookEntry = new Map<Id,PriceBookEntry>(); //get quote Id from Page qId=Apexpages.currentPage().getParameters().get('id'); //Get the Opp Id qObj=[Select q.Total_Price__c, q.Total_Margin__c, q.Total_Cost__c, q.TotalPrice, q.Tax, q.SystemModstamp, q.Subtotal, q.Status, q.Siebel_Id__c, q.ShippingStreet, q.ShippingState, q.ShippingPostalCode, q.ShippingName, q.ShippingHandling, q.ShippingCountry, q.ShippingCity, q.Revision_Number__c, q.Quote_Expiry__c, q.Quote_Date__c, q.QuoteToStreet, q.QuoteToState, q.QuoteToPostalCode, q.QuoteToName, q.QuoteToCountry, q.QuoteToCity, q.QuoteNumber, q.Pricebook2Id, q.Price_Book__c, q.Phone, q.Opportunity.Id, q.OpportunityId, q.Name, q.LineItemCount, q.LastModifiedDate, q.LastModifiedById, q.IsSyncing, q.IsDeleted, q.Internal_Notes__c, q.Id, q.GrandTotal, q.Fax, q.Email, q.Discount, q.Description, q.Default_Payment_Terms__c, q.Customer_Comments__c, q.CurrencyIsoCode, q.CreatedDate, q.CreatedById, q.ContactId, q.BillingStreet, q.BillingState, q.BillingPostalCode, q.BillingName, q.BillingCountry, q.BillingCity, q.Opportunity.CloseDate, q.AdditionalStreet, q.AdditionalState, q.AdditionalPostalCode, q.AdditionalName, q.AdditionalCountry, q.AdditionalCity, q.Account_Number__c, q.Account_Manager__c, q.Account_Manager_Phone__c, q.Account_Manager_Fax__c, q.Account_Manager_Email__c , q.Pricebook2.Organisation__c, q.Pricebook2.External_Id__c, q.Pricebook2.IsStandard, q.Pricebook2.Description, q.Pricebook2.IsActive, q.Pricebook2.CurrencyIsoCode, q.Pricebook2.Name From Quote q where q.Id =:qId]; closeDate = qObj.Opportunity.CloseDate; cTypes=new List<SelectOption>(); mapExchangeRates = new Map<String,Double>(); cTypes.add(new SelectOption('None','None')); for(DatedConversionRate d:[Select StartDate, NextStartDate, IsoCode, Id, ConversionRate From DatedConversionRate where StartDate<=:closeDate and NextStartDate >=:closeDate]){ cTypes.add(new SelectOption(d.IsoCode,d.IsoCode)); mapExchangeRates.put(d.IsoCode,d.ConversionRate); } //query all fields in Opp which has this quote to clone it oppObj = [Select o.X18_Character_Id__c, o.Win_Loss_Reason_Details__c, o.Win_Loss_Cancelled_Reason__c, o.Where_is_A_End__c, o.Upload_Date_Time__c, o.Type, o.Total_Price__c, o.Total_Margin_in_Account_Currency__c, o.Total_Margin__c, o.Total_Cost__c, o.Timescale__c, o.Technology_Margin__c, o.SystemModstamp, o.SyncedQuoteId, o.Suspended__c, o.Start_of_Close_Date_FY__c, o.Start_of_Close_Date_FY_2__c, o.Start_of_Close_Date_FY_1__c, o.StageName, o.Solution_Type__c, o.Solution_Description__c, o.Signed_SOW_Received__c, o.Siebel_Id__c, o.Service_Start_Date__c, o.SecureCall_Margin__c, o.Salesforce_Opportunity_Id__c, o.Risk_Level__c, o.Regional_Contact_for_SFOne__c, o.RecordTypeId, o.RFSDate__c, o.Probability, o.Primary_Competitor__c, o.Pricebook2Id, o.PS_Margin__c, o.PO_Number__c, o.Owner_Affiliates__c, o.OwnerId, o.Other_Margin__c, o.Organisation__c, o.OpenAir_Project_Reference__c, o.Number_of_Line_items__c, o.Non_Portfolio__c, o.NextStep, o.Need__c, o.Name, o.NTT_Service_Family_Name__c, o.Maintenance_Margin__c, o.MSS_Margin__c, o.Lost_to__c, o.LeadSource, o.Last_Update_Date_of_Phase__c, o.LastModifiedDate, o.LastModifiedById, o.LastActivityDate, o.Large_Deal__c, o.IsWon, o.IsDeleted, o.IsClosed, o.Id, o.HasOpportunityLineItem, o.Group_Collaborator_s_Name__c, o.ForecastCategoryName, o.ForecastCategory, o.FiscalYear, o.FiscalQuarter, o.Fiscal, o.ExpectedRevenue, o.End_Customer__c, o.Description, o.CurrencyIsoCode, o.CreatedDate, o.CreatedById, o.Contract_Term__c, o.Collaborate_with__c, o.Closed_Won_Status__c, o.Close_Date_this_FY__c, o.Close_Date_Last_FY__c, o.Pricebook2.CurrencyIsoCode, o.CloseDate, o.CampaignId, o.Budget__c, o.Authority__c, o.Approval_Status__c, o.Amount, o.Account_Owner_Id__c, o.Pricebook2.IsActive, o.Pricebook2.Organisation__c, o.Pricebook2.External_Id__c, o.Pricebook2.IsStandard, o.Pricebook2.Description, o.Pricebook2.Name , o.Account_Currency__c, o.AccountId, o.A_End_or_B_End_or_C_End__c From Opportunity o where o.Id =: qObj.OpportunityId]; lstQuoteLineItems = [Select q.Vendor_Cost_Name__c, q.UnitPrice, q.Total_Price__c, q.Total_Exchanged_Cost__c, q.Takeover_PPP__c, q.SystemModstamp, q.Subtotal_Price__c, q.Subtotal, q.SortOrder, q.Single_Creation__c, q.Siebel_Id__c, q.Service_Start_Date__c, q.Service_End_Date__c, q.Service_Calendar_Business_Hours__c, q.ServiceDate, q.Requested_Ship_Date__c, q.Renewed_Entitlement__c, q.Renewal_Product__c, q.QuoteId, q.Quantity, q.PricebookEntryId, q.Price__c, q.Organisation__c, q.ListPrice, q.Line_No__c, q.LineNumber, q.LastModifiedDate, q.LastModifiedById, q.IsDeleted, q.Install_Post_Code__c, q.Install_County_State__c, q.Install_Country__c, q.Install_Contact__c, q.Install_City__c, q.Id, q.Exchanged_Cost__c, q.Exchange_Rate__c, q.Discount__c, q.Discount, q.Description, q.Customer_Note__c, q.CurrencyIsoCode, q.CreatedDate, q.CreatedById, q.Covered_Product__c, q.Covered_Asset__c, q.Cost__c, q.Cost_Price_Original__c, q.Cost_Exchange_Date__c, q.Cost_Currency__c, q.Consolidated_Entitlement__c, q.Comments__c, q.Asset_Expires__c, q.Address_2__c, q.Vendor_RRP__c, q.PricebookEntry.IsDeleted, q.PricebookEntry.ProductCode, q.PricebookEntry.SystemModstamp, q.PricebookEntry.LastModifiedById, q.PricebookEntry.LastModifiedDate, q.PricebookEntry.CreatedById, q.PricebookEntry.CreatedDate, q.PricebookEntry.UseStandardPrice, q.PricebookEntry.IsActive, q.PricebookEntry.UnitPrice, q.PricebookEntry.CurrencyIsoCode, q.PricebookEntry.Product2Id, q.PricebookEntry.Pricebook2Id, q.PricebookEntry.Name, q.PricebookEntry.Id, q.Address_1__c From QuoteLineItem q where q.QuoteId =:qObj.Id]; for(QuoteLineItem oLineItem : lstQuoteLineItems) { pb.add(oLineItem.PricebookEntry.Product2Id); } //query renewal entitlements under the query to clone it lstRE = [Select r.Quote__c, r.Quote_Status__c, r.Quote_Line_Item__c, r.Opportunity__c, r.Name, r.Id, r.Entitlement__c, r.Entitlement_Start_Date__c, r.Entitlement_Service_Calendar__c, r.Entitlement_Part_Code__c, r.Entitlement_End_Date__c, r.CurrencyIsoCode From Renewed_Entitlement__c r where r.Quote__c =:qId ]; //query quoted assets under the query to clone it lstQuotedAsset = [Select q.Quote__c, q.Name, q.Id, q.CurrencyIsoCode, q.Asset__c From Quoted_Asset__c q where q.Quote__c =:qId]; } public void proceed() { try { if(selectedCurrency == 'None' || selectedCurrency == '') { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Please select a currency in order to proceed')); } pbe = [Select p.UnitPrice, p.Product2Id, p.Pricebook2Id, p.Name, p.IsActive, p.Id, p.CurrencyIsoCode From PricebookEntry p where p.CurrencyIsoCode =:selectedCurrency and p.Pricebook2Id =:oppObj.Pricebook2Id and p.Product2Id in :pb]; for(PricebookEntry pbe : pbe) { mapProIdNpBookEntry.put(pbe.Product2Id,pbe); } for(QuoteLineItem oLine:lstQuoteLineItems) { if(!mapProIdNpBookEntry.containsKey(oLine.PricebookEntry.Product2Id)) { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'No pricebook entry exists for this particular currency.Hence kindly create an entry with the selected currency before cloning Opportunity.')); return; } } if(selectedCurrency!='None') { if(oppObj.StageName!='Qualification') { oppClone = oppObj.clone(false,true); oppClone.ForecastCategoryName = 'Omitted'; oppClone.CurrencyIsoCode = selectedCurrency; System.debug('Pricebook2Id::::' +oppClone.Pricebook2Id); oppClone.Pricebook2Id = oppObj.Pricebook2Id; oppClone.Name = selectedCurrency + oppObj.Name; Database.insert(oppClone); System.debug('Inserted Opp Currency >>> ' + oppClone.CurrencyIsoCode); if(qObj.IsSyncing != true) { qClone = qObj.clone(); qClone.Name = selectedCurrency + qObj.Name; qClone.OpportunityId = oppClone.Id; Database.insert(qClone); System.debug('Inserted Quote Currency >>> ' + qClone.CurrencyIsoCode); testClone= [SELECT Id, CurrencyIsoCode FROM Quote WHERE ID=:qClone.Id]; oppClone.SyncedQuoteId = qClone.Id; } else { ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,'Synced Quote cannot be cloned.Please stop sync before cloning')); } Database.update(oppClone); System.debug('Inserted Quote Currency >>> ' + testClone.CurrencyIsoCode); } if((lstRE!=null || !(lstRE.isEmpty())) && qClone.Id !=null) { for(Renewed_Entitlement__c re:lstRE) { cloneRE = new Renewed_Entitlement__c(); cloneRE = re.clone(); cloneRE.Quote__c = qClone.Id; Database.insert(cloneRE); } } if((lstQuotedAsset!=null || !(lstQuotedAsset.isEmpty())) && qClone.Id !=null) { for(Quoted_Asset__c qa:lstQuotedAsset) { cloneQA = new Quoted_Asset__c(); cloneQA = qa.clone(); cloneQA.Quote__c = qClone.Id; Database.insert(cloneQA); } } System.debug('^^^aaa^^^^^^^^'+mapProIdNpBookEntry); if(qClone!=null) { if(lstQuoteLineItems!=null) { System.debug('not null line items');//tbd for(QuoteLineItem oLine:lstQuoteLineItems) { clonedQuoteLineItem = new QuoteLineItem(); clonedQuoteLineItem = oLine.clone(); system.debug('^^12111111' + oLine.PricebookEntry.Product2Id); if(mapProIdNpBookEntry.containsKey(oLine.PricebookEntry.Product2Id)) { clonedQuoteLineItem.PricebookEntryId=mapProIdNpBookEntry.get(oLine.PricebookEntry.Product2Id).Id; } clonedQuoteLineItem.QuoteId = qClone.Id; System.debug('Quote line Iso Code'+clonedQuoteLineItem.CurrencyIsoCode); //update price,margin and vendors if(mapExchangeRates.containsKey(selectedCurrency)) { System.debug('$$$containsKey iruku$$$'+'###value is###'+mapExchangeRates.get(selectedCurrency)); if(oLine.Price__c!=null && mapExchangeRates.get(selectedCurrency)!=0) { clonedQuoteLineItem.Price__c = oLine.Price__c / mapExchangeRates.get(clonedQuoteLineItem.CurrencyIsoCode); } if(oLine.UnitPrice != null && mapExchangeRates.get(selectedCurrency)!=0) { clonedQuoteLineItem.UnitPrice = oLine.UnitPrice / mapExchangeRates.get(clonedQuoteLineItem.CurrencyIsoCode); } if(oLine.Vendor_RRP__c != null && mapExchangeRates.get(selectedCurrency)!=0) { clonedQuoteLineItem.Vendor_RRP__c = oLine.Vendor_RRP__c / mapExchangeRates.get(clonedQuoteLineItem.CurrencyIsoCode); } } lstclonedQuoteLineItems.add(clonedQuoteLineItem); } System.debug(' >>> ' + lstclonedQuoteLineItems); if(lstclonedQuoteLineItems.size()>0) { insert lstclonedQuoteLineItems; } } } } }catch(Exception e) { } } public pageReference cancel() { return null; } } testCloneNewCurrencyController.cls #isTest(SeeALLData=true) private class testCloneNewCurrencyController { static testMethod void myUnitTest() { // TO DO: implement unit test Account account=new Account(Name='Test'); insert account; Contact cn=new Contact(Accountid=account.id); cn.LastName='test'; insert cn; // set up opportunity name and price book id String opportunityName = 'My Opportunity'; String standardPriceBookId = ''; PriceBook2 pb2Standard = [select Id from Pricebook2 where isStandard=true]; standardPriceBookId = pb2Standard.Id; // set up opp and Verify that the results are as expected. Opportunity o = new Opportunity(AccountId=account.Id, Name=opportunityName, StageName='Prospecting', CloseDate=Date.today(),Contract_Term__c = 6); insert o; Opportunity opp = [SELECT Name FROM Opportunity WHERE Id = :o.Id]; System.assertEquals(opportunityName, opp.Name); // set up product2 and Verify that the results are as expected. Product2 p2 = new Product2(Name='Test Product',isActive=true,Product_Code_Upload__c = 'Test Product 1' , ProductCode = 'Test Product 1'); insert p2; Product2 p2ex = [SELECT Name FROM Product2 WHERE Id = :p2.Id]; System.assertEquals('Test Product', p2ex.Name); // set up PricebookEntry and Verify that the results are as expected. PricebookEntry pbe = new PricebookEntry(Pricebook2Id=standardPriceBookId, Product2Id=p2.Id, UnitPrice=99, isActive=true ); insert pbe; PricebookEntry pbeex = [SELECT Pricebook2Id FROM PricebookEntry WHERE Id = :pbe.Id]; System.assertEquals(standardPriceBookId, pbeex.Pricebook2Id); // set up OpportunityLineItem and Verify that the results are as expected. OpportunityLineItem oli = new OpportunityLineItem(PriceBookEntryId=pbe.Id, OpportunityId=o.Id, Quantity=1, UnitPrice=99 , Cost_Currency__c = 'USD',Cost__c=2000); insert oli; OpportunityLineItem oliex = [SELECT PriceBookEntryId FROM OpportunityLineItem WHERE Id = :oli.Id]; oli.Cost_Currency__C = 'GBP'; update oli; update account; Quote quot1 = new Quote(Name='Quote1',OpportunityId=o.Id,Pricebook2Id=standardPriceBookId); insert quot1; // set up OpportunityLineItem and Verify that the results are as expected. QuoteLineItem qli = new QuoteLineItem(QuoteId = quot1.Id,PriceBookEntryId=pbe.Id,UnitPrice=1000, Quantity=1, Price__c=3500 , Cost_Currency__c = 'USD',Cost__c=2000); insert qli; // Create two Asset records and relate them to the oli through Ordered Asset records Asset ass1 = new Asset(Name='Asset1',SerialNumber='12345',AccountId=account.Id,ContactId=cn.Id); insert ass1; Asset ass2 = new Asset(Name='Asset1',SerialNumber='678910',AccountId=account.Id,ContactId=cn.Id); insert ass2; Quoted_Asset__c ordass1 = new Quoted_Asset__c(Asset__c=ass1.Id,Quote_Line_Item__c=qli.Id,Quote__c=quot1.Id); insert ordass1; Quoted_Asset__c ordass2 = new Quoted_Asset__c(Asset__c=ass2.Id,Quote_Line_Item__c=qli.Id,Quote__c=quot1.Id); insert ordass2; // Check that the Serial numbers field on the QLI is as expected QuoteLineItem quot1test = [SELECT LineNumber, Serial_Numbers__c FROM QuoteLineItem WHERE Id =: qli.Id]; Test.startTest(); Test.setCurrentPage(new Pagereference('/apex/CloneOppNewCurrency?id='+quot1.Id)); CloneOppNewCurrencyController c = new CloneOppNewCurrencyController(); c.selectedCurrency = 'USD'; system.assertEquals(c.selectedCurrency, 'USD'); //c.qObj = quot1; c.proceed(); c.selectedCurrency = 'USD'; System.debug('%%%%%'+c.oppClone+'$$$$$'+c.selectedCurrency+'Opp OBj is:'+c.oppObj); Test.setCurrentPage(new Pagereference('/apex/CloneOppNewCurrency?id='+quot1.Id)); CloneOppNewCurrencyController c1 = new CloneOppNewCurrencyController(); c1.selectedCurrency = 'None'; Pagereference pr = c1.cancel(); Test.stopTest(); } } Lines from 127 are not covered.The proceed method is not covered.Can anyone help me please?
What line does 127 correspond to in your example? Is it if(selectedCurrency!='None') { in the proceed() method? I'd suggest to extend your exception handling. You basically have: public void proceed() { try { // Most of method body here } catch(Exception e) { } } It is likely that an exception has occured and has been ignored/lost due to the empty Exception handling. Minimally, write the exception out to the log: } catch(Exception e) { System.debug(LoggingLevel.Error, 'BANG! ' + e); } That may well reveal in the debug log where your test method is failing.
count * with where clause using JPA criteria query
I want to find records for related_elements table, where relationId belongs to a list suppose a Set tempSet contains[2,3,4] I have to check for these value contains in related_element table using jpa criteria query CriteriaBuilder cb1=entityManager.getCriteriaBuilder(); CriteriaQuery<RelatedElements> cq1=cb1.createQuery(RelatedElements.class); Root<RelatedElements> RelatedElementsRoot=cq1.from(RelatedElements.class); for (Integer tSet : tempSet) { ParameterExpression<Integer> pRelatedElement=cb1.parameter(Integer.class); cq1.multiselect(cb1.count(RelatedElementsRoot.<RelatedElements>get("relatedElementsPk").<Integer>get("relationId"))).where(cb1.equal(RelatedElementsRoot.get("relationId"), pRelatedElement)); TypedQuery<RelatedElements> qry = entityManager.createQuery(cq1); qry.setParameter(pRelatedElement, tSet); count = entityManager.createQuery(cq1).getSingleResult().getRelationId(); } but its now working...any suggessions second try CriteriaBuilder cb1=entityManager.getCriteriaBuilder(); CriteriaQuery<Integer> cq1 = cb1.createQuery(Integer.class); Root<RelatedElements> RelatedElementsRoot=cq1.from(RelatedElements.class); for (Integer tSet : tempSet) { ParameterExpression<Integer> pRelatedElement=cb1.parameter(Integer.class); cq1.multiselect(cb1.count(cq1.from(RelatedElements.class))); cq1.where((cb1.equal(RelatedElementsRoot.get("relatedElementsPk").get("relationId"), pRelatedElement))); TypedQuery<Integer> qry = entityManager.createQuery(cq1); qry.setParameter(pRelatedElement, tSet); count =qry.getSingleResult(); } its giving exception at qry.setParameter Unable to locate appropriate constructor on class [java.lang.Integer] [select new java.lang.Integer(count(*)) from com.mcd.webex.model.RelatedElements as generatedAlias0, com.mcd.webex.model.RelatedElements as generatedAlias1 where generatedAlias0.relatedElementsPk.relationId=:param0]
CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Long> cq = cb.createQuery(Long.class); Root<Dzialy> root = cq.from(Dzialy.class); EntityType <Dzialy> Dzialy_ = root.getModel(); cq.select((cb.countDistinct(root))); cq.where( cb.equal( root.get(Dzialy_.getSingularAttribute("DZI_id")), 1) ); long l = em.createQuery(cq).getSingleResult(); em - EntityManager DZI_id - column name 1 - searching value Dzialy - entity class
As documented, CriteriaBuilder.count returns Expression<java.lang.Long>. Consequently type argument to CriteriaQuery and TypedQuery should be Long as well. Same holds true for type of count variable. When there is only one value to be selected, then it makes sense to use CriteriaQuery.select instead of multiselect, because then such an error is catched already in compile time. Long count; ... CriteriaQuery<Long> cq1 = cb1.createQuery(Long.class); ... cq1.select(cb1.count(cq1.from(RelatedElements.class))); ... TypedQuery<Long> qry = entityManager.createQuery(cq1);
CriteriaBuilder qb = entityManager.getCriteriaBuilder(); CriteriaQuery<Long> cq = qb.createQuery(Long.class); cq.select(qb.count(cq.from(YourEntity.class))); cq.where(/*your stuff*/); return entityManager.createQuery(cq).getSingleResult();
Doctrine 2 date in where-clause (querybuilder)
I've the following problem: I want a where-clause that checks if a user is active AND if he has the right date. A user contains the following: State Startdate Enddate So, State should stay on 1, then he should look for state = 1 AND the current date is between the start and enddate. I've the following right now, and it works fine. But the start and enddate is not required. So it could be NULL. How can i get a query like: SELECT * FROM user/entity/user WHERE state = 1 AND ( (startdate <= CURRENT_DATE AND enddate >= CURRENT_DATE) OR startdate == NULL OR enddate == NULL ) , so i get all my active users, and not only the temporary users. I've set up the following code right now: Repository: public function searchUser($columns, $order_by, $order) { //Create a Querybuilder $qb = $this->_em->createQueryBuilder(); //andx is used to build a WHERE clause like (expression 1 AND expression 2) $or = $qb->expr()->andx(); //Select all from the User-Entity $qb->select('u') ->from('User\Entity\User', 'u'); foreach($columns as $column => $name) { if($column == 'state') { if($columns['state']['value'] == '1') { $or = $this->betweenDate($qb, $or); $or = $this->like($columns, $qb, $or); } elseif($columns['state']['value'] == '2') { $or = $this->like($columns, $qb, $or); } elseif($columns['state']['value'] == '3') { $or = $this->outOfDate($qb, $or); } } else { //Get a where clause from the like function $or = $this->like($columns, $qb, $or); } } //Set the where-clause $qb->where($or); //When there is a order_by, set it with the given parameters if(isset($order_by)) { $qb->orderBy("u.$order_by", $order); } //Make the query $query = $qb->getQuery(); /*echo('<pre>'); print_r($query->getResult()); echo('</pre>');*/ //Return the result return $query->getResult(); } public function betweenDate($qb, $or) { $or->add($qb->expr()->lte("u.startdate", ":currentDate")); $or->add($qb->expr()->gte("u.enddate", ":currentDate")); //$or->add($qb->expr()->orx($qb->expr()->eq("u.startdate", null))); $qb->setParameter('currentDate', new \DateTime('midnight')); return $or; } //This one works perfect public function like($columns, $qb, $or) { //Foreach column, get the value from the inputfield/dropdown and check if the value //is in the given label. foreach($columns as $label=>$column){ $value = $column['value']; $or->add($qb->expr()->like("u.$label", ":$label")); $qb->setParameter($label, '%' . $value . '%'); } return $or; } I use this "usersearch" also for other fields. So it should pick all the data out of the database, only state is different because "out of date" is not in the database. So it has to check differently. Hope somebody can help. Problem solved $startdate = $qb->expr()->gt("u.startdate", ":currentDate"); $enddate = $qb->expr()->lt("u.enddate", ":currentDate"); $or->add($qb->expr()->orX($startdate, $enddate));
That's how I would build the where clause: //CONDITION 1 -> STATE = 1 $state = $qb->expr()->eq( 'state', ':state' ); //CONDITION 2 -> startdate <= CURRENT_DATE AND enddate >= CURRENT_DATE $betweenDates = $qb->expr()->andX( $qb->expr()->lte("u.startdate", ":currentDate"), $qb->expr()->gte("u.enddate", ":currentDate") ); //CONDITION 3 -> startdate == NULL $startDateNull = $qb->expr()->isNull( 'startdate' ); //CONDITION 4 -> enddate == NULL $endDateNull = $qb->expr()->isNull( 'enddate' ); //CONDITION 5 -> <CONDITION 2> OR <CONDITION 3> OR <CONDITION 4> $dates = $qb->expr()->orX( $betweenDates, $startDateNull, $endDateNull ); //CONDITION 6 -> <CONDITION 1> AND <CONDITION 5> $whereClause = $qb->expr()->andX( $state, $dates );