I have an apex batch class which I need to update a contact boolean field 'Active_Subscriptions__c' based on the state of a related custom object field.
The custom object 'Subscriptions__c' has a 'Contact__c' master detail field, and also has a 'IsActive__c' boolean field.
I want to run a batch that will find any subscriptions that are related to a contact record by Id. If it finds any subscriptions that are active, it needs to set the 'Active_Subscriptions__c' on the contact record to true, else set to false.
I am fairly new to apex and can't seem to get this to trigger the results I need, thanks in advance.
global class BatchContactUpdate implements Database.Batchable<sObject>{
List <Subscription__c> mapSubs = new List <Subscription__c> ();
List <Contact> contactlist = new List <Contact> ();
global Database.QueryLocator start(Database.BatchableContext BC) {
return DataBase.getQueryLocator([SELECT Id, Contact__c FROM Subscription__c WHERE Contact__c =:contactlist]);
}
global void execute(Database.BatchableContext BC , List <Subscription__c> mapSubs) {
for (Subscription__c sub : mapSubs){
for (Contact con : contactList){
if (con.Id == sub.Contact__c && sub.IsActive__c == true){
contactlist.add(new Contact(
Active_Subscriptions__c = true
));
} else {
contactlist.add(new Contact(
Active_Subscriptions__c = false
));
}
}
}
update contactlist;
}
global void finish(Database.BatchableContext BC){
}
}
Looks like you contact list is empty to start off with, so it wont return any results. But for your query, why dont you do something like
[SELECT Id, (SELECT Id FROM Subscriptions__r WHERE Is_Active__c = true) FROM Contact];
and in your execute do
List<Contact> cList = new List<contact>();
for(Contact c : scope){
c.Active_Subscriptions__c = c.Subscriptions__r.size() > 0;
cList.add(c);
}
//etc... to start
Related
I have question on my Salesforce WebService and Apex Code.
In a relationship, we have one Notice and multiple attachments in my salesforce. But I don't know how to fix below requirements:
when "GET" webservices incoming thru specific URL API, it supposed to return with JSON format
JSON Format should {Notice1 : attach1{link},attach2{link} , etc }
#RestResource(urlMapping='/API/V1/notice/*')
global with sharing class API_Notice {
#HttpGet(UrlMapping='/API/V1/notice/all')
global static List<String> getNotice(){
Set<Id> NoticeIds = new Set<Id>();
Set<Id> VersionIds = new Set<Id>();
String compares;
List<String> returnJSON = new List<String>();
List<Notice__c> reConts = [select Id, ClosingDate__c ,Name, Contents__c from notice__c];
Map<Id,Notice__c> addMap = new Map <Id,Notice__c>();
Map<Map<Id,Notice__c>,Map<Id,contentdistribution>> addsMap = new Map<Map<Id,Notice__c>,Map<Id,contentdistribution>>();
//SET NOTICE ID
if(!reConts.isEmpty()){
for(Notice__c nc : reConts){
NoticeIds.add(nc.Id);
addMap.put(nc.id,nc);
}
}
//GET public Image URL
if(!NoticeIds.isEmpty()){
Map<Id,ContentDocumentLink> contentMap = new Map<Id,ContentDocumentLink>([
select contentDocumentid,LinkedEntityId from ContentDocumentLink where LinkedEntityId IN:NoticeIds
]);
for(ContentDocumentLink Key : contentMap.values()){
VersionIds.add(Key.ContentDocumentId);
}
if(!VersionIds.isEmpty()){
Map<Id, contentdistribution> cdb = new Map <Id, contentdistribution> ([
select DistributionPublicUrl from contentdistribution where contentDocumentid IN:VersionIds
]);
addsMap.put(addMap,cdb);
}
}
return null;
}
}
Below is the trigger update a field on an attachment or file insert , can some one suggest a test class for this trigger?
Can some one please help in writing a #isTest class for below trigger to achieve 100 % code completion
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
if(trigger.isAfter) {
if(trigger.isInsert) {
ContentDocumentLinkTriggerHandler.onAfterInsert(trigger.new);
}
}
}
here is the class
public class ContentDocumentLinkTriggerHandler {
public static void onAfterInsert(list<ContentDocumentLink> lstCntLinks) {
set<Id> setTaskIds = new set<Id>();
list<task> lstTask=new list<task>();
Id recordTypeId = Schema.SObjectType.Task.getRecordTypeInfosByName().get('ERT_Task_Record_Type').getRecordTypeId();
for(ContentDocumentLink clIterator : lstCntLinks) {
setTaskIds.add(clIterator.LinkedEntityId);//Set all task id
}
if(!setTaskIds.isEmpty()) {
lstTask= [SELECT Id, Name,Attachment_Indicator__c FROM task WHERE Id IN :setTaskIds and recordtypeid=: recordTypeId ]; //Get all the task
}
for(task varTsk: lstTask){
varTsk.Attachment_Indicator__c=true;//Set Attachment Indicator Flag true
}
if(lstTask.size()>0){
update lstTask; //update task
}
}
}
Thanks in advance.
Carolyn
We aren't really a code writing service, you might want to hire some consultancy or even post the job on https://appexchange.salesforce.com/developers (it should be vetted by SF themselves, haven't used that part of appexchange personally). It's different if you have specific problem, wrote some code, got stuck with particular problem...
This should give you some ideas how it could be attacked. No promises it'll even compile. It's not pixel perfect, there's no negative test case (linking to task of different record type should not set the flag etc)
but it's a start.
#isTest
public with sharing class ContentDocumentLinkTriggerTest {
#isTest
public static void testSettingTaskFlag(){
Task t = new Task(
Subject = 'unit test here we go',
RecordTypeId = Schema.SObjectType.Task.getRecordTypeInfosByName().get('ERT_Task_Record_Type').getRecordTypeId()
);
insert t;
ContentVersion cv = new ContentVersion(
Title = 'Some document',
PathOnClient = 'some document.txt',
VersionData = Blob.valueOf('Lorem ipsum dolor sit amet...')
);
insert cv;
ContentDocument cd = [SELECT Id FROM ContentDocument WHERE LatestPublishedVersionId = :cv.Id];
ContentDocumentLink cdl = new ContentDocumentLink(
ContentDocumentId = cd.Id,
LinkedEntityId = t.Id
);
Test.startTest();
insert cdl;
Test.stopTest();
Task newTask = [SELECT Attachment_Indicator__c FROM Task WHERE Id = :t.Id];
System.assert(newTask.Attachment_Indicator__c);
}
}
I am trying to deploy a trigger to prod on salesforce. I was hoping someone could help me with an example of tests for this trigger.
Here is my trigger. It does its purpose, which is to update a bool field when a new contentNote (or anything of content type) that then has collateral effects through process builder.
trigger NewNote on ContentDocumentLink (before insert) {
Set<Id> setParentId = new Set<Id>();
List<Client_Relationships__c> crlst = new List<Client_Relationships__c>();
for (ContentDocumentLink cdl : trigger.new ) {
setParentId.add(cdl.LinkedEntityId);
}
crlst = [select Id , newNote__c from Client_Relationships__c where Id IN :setParentId];
For(Client_Relationships__c e : crlst)
{
e.newNote__c = True;
}
update crlst;
}
The trigger you wrote can be more efficient by omitting the SOQL query as seen below:
trigger NewNote on ContentDocumentLink (before insert) {
List<Client_Relationships__c> crlst = new List<Client_Relationships__c>();
for (ContentDocumentLink cdl : trigger.new ) {
if(cdl.LinkedEntityId.getSObjectType().getDescribe().getName() == 'Client_Relationships__c'){
crlst.add(
new Client_Relationships__c(
Id = cdl.LinkedEntityId,
newNote__c = true
)
);
}
}
update crlst;
}
The best practice would be to add your code to a handler or utility class and to only have one trigger per object. The name of this trigger could be changed to "ContentDocumentLinkTrigger" if you adopt that practice.
The test class for that trigger is below. I could not test the compilation because I don't have the same custom object.
#IsTest
private class ContentDocumentLinkTriggerTest {
#TestSetup
static void setupTest() {
insert new ContentVersion(
Title = 'Test_Document.txt',
VersionData = Blob.valueOf('This is my file body.'),
SharingPrivacy = 'N',
SharingOption = 'A',
Origin = 'H',
PathOnClient = '/Test_Document.txt'
);
List<Client_Relationships__c> relationships = new List<Client_Relationships__c>();
for(Integer i = 0; i < 300; i++){
relationships.add(
new Client_Relationships__c(
//add required field names and values
)
);
}
insert relationships;
}
static testMethod void testInsertTrigger() {
//prepare data
List<ContentVersion> contentVersions = new List<ContentVersion>([
SELECT Id, ContentDocumentId FROM ContentVersion
]);
System.assertNotEquals(0, contentVersions.size(), 'ContentVersion records should have been retrieved');
List<Client_Relationships__c> relationships = getAllClientRelationships();
System.assertNotEquals(0, relationships.size(), 'Client Relationship records should have been retrieved.');
List<ContentDocumentLink> documentLinks = new List<ContentDocumentLink>();
for(Integer i = 0; i < 252; i++){
documentLinks.add(
new ContentDocumentLink(
ContentDocumentId = contentVersions[0].ContentDocumentId,
LinkedEntityId = relationships[i].Id,
ShareType = 'I'
)
);
}
//test functionality
Test.startTest();
insert documentLinks;
Test.stopTest();
//assert expected results
List<Client_Relationships__c> relationshipsAfterProcessing = getAllClientRelationships();
for(Client_Relationships__c relationship : relationshipsAfterProcessing){
System.assert(relationship.newNote__c, 'The newNote__c field value should be true.');
}
}
private static List<Client_Relationships__c> getAllClientRelationships(){
return new List<Client_Relationships__c>([
SELECT Id, newNote__c FROM Client_Relationship__c
]);
}
}
For setting up test data, it is helpful to have a utility class that centralizes the creation of well-formed records. This is extremely useful when your code base gets large and a validation rule affects the insertion of new data in many test classes. With a centralized method, the inserted data only needs to be altered once.
I have a trigger that i have written that I need tested to be able to get code coverage in salesforce:
here is the trigger:
Trigger MyCh_SALESFORCETRIGGERUPDATE_tr_I on Account (after insert)
{
set<ID> ids = Trigger.newMap.keyset();
for(ID id : ids)
{
MyCh_Account__c change = new MyCh_Account__c();
change.Action__c = 'insert';
change.Link__c = id;
change.Processed__c = false;
change.Map__c = 'SALESFORCE TRIGGER UPDATE';
insert change;
}
}
I have tried :
#isTest
public class MyAccountcreationTest
{
static testMethod void testMethod1()
{
Account testAccount = new Account();
testAccount.Name='Test Account' ;
insert testAccount;
Account acc1 = [Select Id, Link__c, Action__c, Processed__c, Map__c from Account where Id =: testAccount.Id];
System.assertEquals(acc1.Name,'Test Account');
System.assertEquals(acc1.Link__c, acc1.Id);
System.assertEquals(acc1.Processed__c,false);
System.assertEquals(acc1.Map__c,'SALESFORCE TRIGGER UPDATE');
System.assertEquals(acc1.Action__c,'insert');
}
}
I expect the test to pass but it gives an error :
Map__c , Action__c , Link__c are not fields of Account object.
Also , how does the test actually link to the trigger itself ?
You are creating a MyCh_Account__c record in your trigger but you're not testing that in your test. You need to query for the record created in your trigger and assert that the fields are correct.
I have a function that creates a new value inside an associative array.
var array:Array = new Array(new Array());
var i : int = 0;
function personVelgLand(evt:MouseEvent)
{
for(i = 0; i < personListe.dataProvider.length; i++)
{
if(txtPersonVelg.text == personListe.dataProvider.getItemAt(i).label)
{
personListe.dataProvider.getItemAt(i).reise = array;
personListe.dataProvider.getItemAt(i).reise.push(landListe.selectedItem.land);
}
}
}
What happens is that the 'array' array which becomes 'personListe.dataProvider.getItemAt(i).reise' applies to every item in the list. I want it so that each time the function runs that the .reise only applies to the item chosen and not all of them.
EDIT:
I did this:
personListe.dataProvider.getItemAt(i).reise = new Array(array);
And now they are not the same but now each item in the list can not have multiple .reise values...
EDIT 2: dataProvider is nothing it would work just as fine without it. .reise is created in the function I originally posted it creates .reise in the object getItemAt(i).
personListe is a list which the users add their own items to by the use of a input textfield. It is given by this function:
function regPerson(evt:MouseEvent)
{
regPersoner.push(txtRegPerson.text);
personListe.addItem({label:regPersoner});
regPersoner = new Array();
txtRegPerson.text = "";
}
EDIT 3 : The user can register names which turn in to labels in a list. There is also list with 3 countries, Spain, Portugal and England. The user can then register a country to a person they select. Thats when I want to create the .reise inside the "name" items in the first list which contains the countries they have selected. I want every name to be able to select multiple countries which then will be created in the element .reise inside the item that holds their name. This would be easy if I could use strings. But later I plan to have the user type in a country and then something that would show every user that have selected that country. That is why the countries need to be stored as arrays inside the "name" items..
You should first create a class for the User data that you are modelling. You already know all the properties.
The user can register names
The user can then register a country to a person they select.
able to select multiple countries
Such a class could look like this:
package
{
public class User
{
private var _name:String;
private var _countries:Array;
public function User(name:String)
{
_name = name;
_countries = [];
}
public function get name():String
{
return _name;
}
public function get countries():Array
{
return _countries;
}
public function set countries(value:Array):void
{
_countries = value;
}
}
}
Now create a DataProvider, fill it with objects of that class and use it for the list as described here:
import fl.controls.List;
import fl.data.DataProvider;
var users:List = new List();
users.dataProvider = new DataProvider([
new User("David"),
new User("Colleen"),
new User("Sharon"),
new User("Ronnie"),
new User("James")]);
addChild(users);
users.move(150, 150);
In order to get a label from a User object, define a labelFunction
import fl.controls.List;
import fl.data.DataProvider;
var users:List = new List();
users.labelFunction = userLabelFunction;
function userLabelFunction(item:Object):String
{
return item.name;
}
users.dataProvider = new DataProvider([
new User("David"),
new User("Colleen"),
new User("Sharon"),
new User("Ronnie"),
new User("James")]);
addChild(users);
users.move(150,150);
This way you do not have to add a label property that you don't want in your class.
Selecting a name means selecting a user. The list of countries associated to the name should show up in a second List.
The DataProvider of that List remains constant, a list of all the available countries.
import fl.controls.List;
import fl.data.DataProvider;
// list of users
var users:List = new List();
addChild(users);
users.move(150,150);
users.labelFunction = userLabelFunction;
function userLabelFunction(item:Object):String
{
return item.name;
}
users.dataProvider = new DataProvider([
new User("David"),
new User("Colleen"),
new User("Sharon"),
new User("Ronnie"),
new User("James")]);
// lsit of countries
var countries:List = new List();
addChild(countries);
countries.move(550,150); // adjut position as desired
countries.dataProvider = new DataProvider([
{label:"a"},
{label:"b"},
{label:"c"},
{label:"d"},
{label:"e"},
{label:"f"}]);
Now all you have to do is to wire it all up. If a user is selected, select his countries in the countries list. If a country is selected, add that to the currently selected users list of countries. That could look somethign like this:
users.addEventLsitener(Event.CHANGE, onUserSelected);
function onUserSelected(e:Event):void
{
countries.selectedItems = users.selectedItem.countries;
}
countries.addEventLsitener(Event.CHANGE, onCountrySelected);
function onCountrySelected(e:Event):void
{
users.selectedItem.countries = countries.selectedItems;
}
The full code could look like this. I did not test this, but you get the idea.
// list of users
var users:List = new List();
addChild(users);
users.move(150,150);
users.labelFunction = userLabelFunction;
function userLabelFunction(item:Object):String
{
return item.name;
}
users.dataProvider = new DataProvider([
new User("David"),
new User("Colleen"),
new User("Sharon"),
new User("Ronnie"),
new User("James")]);
// list of countries
var countries:List = new List();
addChild(countries);
countries.move(550,150); // adjut position as desired
countries.dataProvider = new DataProvider([
{label:"a"},
{label:"b"},
{label:"c"},
{label:"d"},
{label:"e"},
{label:"f"}]);
// events
users.addEventLsitener(Event.CHANGE, onUserSelected);
function onUserSelected(e:Event):void
{
countries.selectedItems = users.selectedItem.countries;
}
countries.addEventLsitener(Event.CHANGE, onCountrySelected);
function onCountrySelected(e:Event):void
{
users.selectedItem.countries = countries.selectedItems;
}
From what I understand this seems to work except for the fact that the names are already provided when the program starts. What I want is that the user adds the name themselves while the program is running.
You can add new items with the methods provided by the DataProvider class, like addItem() for example. Just add new User objects.