praogramatically fetch any particular object's related objects from its related list - salesforce

I am pretty new to SFDC. I am trying to implement a clone functionality of a custom object by which when I am cloning an object, the object as well as all the object in its related list are to be cloned. I have implemented the part of cloning a object but stuck how to get the object list associated with a object's related list. pls let me know , how to implement this.
Thanks

You can try this...
public class PurchaseOrderCloneWithItemsController {
//added an instance varaible for the standard controller
private ApexPages.StandardController controller {get; set;}
// add the instance for the variables being passed by id on the url
private Purchase_Order__c po {get;set;}
// set the id of the record that is created -- ONLY USED BY THE TEST CLASS
public ID newRecordId {get;set;}
// initialize the controller
public PurchaseOrderCloneWithItemsController(ApexPages.StandardController controller) {
//initialize the stanrdard controller
this.controller = controller;
// load the current record
po = (Purchase_Order__c)controller.getRecord();
}
// method called from the VF's action attribute to clone the po
public PageReference cloneWithItems() {
// setup the save point for rollback
Savepoint sp = Database.setSavepoint();
Purchase_Order__c newPO;
try {
//copy the purchase order - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
po = [select Id, Name, Ship_To__c, PO_Number__c, Supplier__c, Supplier_Contact__c, Date_Needed__c, Status__c, Type_of_Purchase__c, Terms__c, Shipping__c, Discount__c from Purchase_Order__c where id = :po.id];
newPO = po.clone(false);
insert newPO;
// set the id of the new po created for testing
newRecordId = newPO.id;
// copy over the line items - ONLY INCLUDE THE FIELDS YOU WANT TO CLONE
List<Purchased_Item__c> items = new List<Purchased_Item__c>();
for (Purchased_Item__c pi : [Select p.Id, p.Unit_Price__c, p.Quantity__c, p.Memo__c, p.Description__c From Purchased_Item__c p where Purchase_Order__c = :po.id]) {
Purchased_Item__c newPI = pi.clone(false);
newPI.Purchase_Order__c = newPO.id;
items.add(newPI);
}
insert items;
} catch (Exception e){
// roll everything back in case of error
Database.rollback(sp);
ApexPages.addMessages(e);
return null;
}
return new PageReference('/'+newPO.id+'/e?retURL=%2F'+newPO.id);
}

Sounds like you need to "Deep Clone" - check out the links below for reference:
https://salesforce.stackexchange.com/questions/8493/deep-clone-parent-child-grand-child
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_System_List_deepClone.htm

Related

How to get query covered in apex test class for salesforce

I'm having a tough time trying to get all code covered in my test class for an apex class.
Apex class:
public with sharing class myclass {
**public List<CustomObject1> listvar {get;set;}**
public myclass(ApexPages.StandardController sc){
CustomObject2 var = [SELECT Id, Field1__c FROM CustomObject2 WHERE Id = :ApexPages.currentPage().getParameters().get('id')];
**listvar = [SELECT Id,Name,Field1__c,Field2__c,Field3__c,Field4__c,Field5__c,CreatedDate,CreatedById FROM CustomObject1 WHERE Field2__c = :var.Field1__c ORDER BY CreatedDate DESC];**
}
}
Test Class:
#isTest
public class myclass_Test {
static testmethod void dosomething(){
Account a = new Account();
a.Name = 'Test acct';
insert a;
CustomObject4__c v = new CustomObject4__c();
v.Field1__c = '123 ABC';
v.Name = 'test name';
v.Field2__c = True;
v.Account__c = a.Id;
insert v;
... more record creates including ones for the object being queried...
PageReference pageref = Page.myVFpage;
Test.setCurrentPageReference(pageref);
ApexPages.StandardController sc = new ApexPages.StandardController(v);
myclass myPageCon = new myclass(sc);
}
}
I've tried creating a new list for the underneath the last line in the test class and populating the list, but I cannot get 100% code coverage. I marked the lines that I'm not getting any coverage from the test class with. Any suggestions?
You should put some asserts into your test Class. Something like
System.assertEquals(5, yourListsize)
I figured out that the listvar list for CustomObject1 wasn't getting populated because an Id wasn't being passed to var for CustomObject2. In the test class I had to put the record Id using ApexPages.currentPage().getParameters().put('Id', something.id);
with the Id for the record created in the test class for that object. Thanks anyways guys :-)

TryUpdateModel showing the curect data but db is not saving it

I'm trying to save a single item into a SQL Server tableusing TryUpdateModel. When debugging, I can see the value that needs to be updated, but the db.SaveChanges() call is not saving it.
My code:
[HttpGet]
public PartialViewResult _SubmitRev(int? id)
{
return PartialView();
}
[HttpPost]
public PartialViewResult _SubmitRev(int? id, WriterSubjectReviewVm model)
{
var loggedInUserId = User.Identity.GetUserId();
var member = db.Members.SingleOrDefault(m => m.ApplicationUserId == loggedInUserId);
var MySubjectDetails = (from c in db.subjects.Where(s => s.SubjectId == id) select c).AsNoTracking().Single();
model.rev.SubjectId = (int)id;
model.sub.SubjectId = MySubjectDetails.SubjectId;
var bad = MySubjectDetails.Bad;
model.sub.Bad = bad;
if (model.rev.GBU == "Bad")
{
int iBadRating = Convert.ToInt32(bad);
iBadRating++;
model.sub.Bad = iBadRating;
}
if (ModelState.IsValid)
{
// TryUpdateModel(model.sub, "Subject");
TryUpdateModel(model.sub);
db.SaveChanges();
return PartialView();
}
return PartialView(model);
}
Looking at your code, I would say that you aren't re-attaching your model back to the context. Let's break it down:
First, your model is coming into the method as a new object:
public PartialViewResult _SubmitRev(int? id, WriterSubjectReviewVm model)
Then you modify it a bit using data from your DB:
var MySubjectDetails = (from c in db.subjects.Where(s => s.SubjectId == id) select c).AsNoTracking().Single();
model.rev.SubjectId = (int)id;
model.sub.SubjectId = MySubjectDetails.SubjectId;
Important to note that you are pulling MySubjectDetails using .AsNoTracking(), which pulls it disconnected from the context, so this won't automatically save at all unless you re-attach it.
You then assign that disconnected entity to your model:
var bad = MySubjectDetails.Bad;
model.sub.Bad = bad;
Then you modify some more properties, then you check if the model is valid and try and save it:
if (ModelState.IsValid)
{
// TryUpdateModel(model.sub, "Subject");
TryUpdateModel(model.sub);
db.SaveChanges();
return PartialView();
}
At no point have you reconnected your model object back to the context (db), so when you call .SaveChanges(), what are you saving?
The Solution
At some stage you need to map the properties as posted to your Action (in the form of the WriterSubjectReviewVm view model) back onto a data model. Otherwise if that view model is actually a data model (and exists on your DB context in a collection somewhere) then you need to reattach it:
db.WriterSubjectReviews.Attach(model)
Or something similar - then when you call SaveChanges() it will actually save.

Insert CSV using Apex Batch Class Salesforce for OpportunityLineItem

I want to add a button to my opportunity header record that is called Insert Products. This will send the opportunity ID to a visualforce page which will have a select file button and an insert button that will loop through the CSV and insert the records to the related opportunity.
This is for non technical users so using Data loader is not an option.
I got this working using standard apex class however hit a limit when i load over 1,000 records (which would happen regularly).
I need to convert this to a batch process however am not sure how to do this.
Any one able to point me in the right direction? I understand a batch should have a start, execute and finish. However i am not sure where i should split the csv and where to read and load?
I found this link which i could not work out how to translate into my requirements: http://developer.financialforce.com/customizations/importing-large-csv-files-via-batch-apex/
Here is the code i have for the standard apex class which works.
public class importOppLinesController {
public List<OpportunityLineItem> oLiObj {get;set;}
public String recOppId {
get;
// *** setter is NOT being called ***
set {
recOppId = value;
System.debug('value: '+value);
}
}
public Blob csvFileBody{get;set;}
public string csvAsString{get;set;}
public String[] csvFileLines{get;set;}
public List<OpportunityLineItem> oppLine{get;set;}
public importOppLinesController(){
csvFileLines = new String[]{};
oppLine = New List<OpportunityLineItem>();
}
public void importCSVFile(){
PricebookEntry pbeId;
String unitPrice = '';
try{
csvAsString = csvFileBody.toString();
csvFileLines = csvAsString.split('\n');
for(Integer i=1;i<csvFileLines.size();i++){
OpportunityLineItem oLiObj = new OpportunityLineItem() ;
string[] csvRecordData = csvFileLines[i].split(',');
String pbeCode = csvRecordData[0];
pbeId = [SELECT Id FROM PricebookEntry WHERE ProductCode = :pbeCode AND Pricebook2Id = 'xxxx HardCodedValue xxxx'][0];
oLiObj.PricebookEntryId = pbeId.Id;
oLiObj.Quantity = Decimal.valueOf(csvRecordData[1]) ;
unitPrice = String.valueOf(csvRecordData[2]);
oLiObj.UnitPrice = Decimal.valueOf(unitPrice);
oLiObj.OpportunityId = 'recOppId';;
insert (oLiObj);
}
}
catch (Exception e)
{
ApexPages.Message errorMessage = new ApexPages.Message(ApexPages.severity.ERROR, e + ' - ' + unitPrice);
ApexPages.addMessage(errorMessage);
}
}
}
First problem that I can sense is that the insert DML statement is inside FOR-loop. Can you put the new "oLiObj" into a List that is declared before the FOR-loop starts and then try inserting the list after the FOR-loop ?
It should bring some more sanity in your code.

How can I make Entity Framework only update object dependencies?

I'd like to know how can I make Entity Framework update an object instead of always inserting a new one for each new main object.
For example:
I have these objects:
Main Object:
public class ExtraArticleAttributes
{
[Key]
public int extraarticleattributes_id { get; set; }
virtual public WorldData world_data { get; set; }
}
Its dependencie:
public class WorldData
{
[Key]
public int worlddata_id { get; set; }
public string country { get; set; }
So, how can I make Entity Framework when inserting a new ExtraArticleAttributes verify if already exists a WorldData object and only update it?
I've been reading some articles about it and I notice that Entity Framework identify an existing object in DB with a HASH code, so when I get it from an API, and try to insert It in the DB, even though the object has the same data, the Entity Framework doesn't recognize like an existed object in DB. Does exist a way of make It, without spending request to the DB to verify if the object exists, if true get It.
Set the entity state to Modified:
using System.Data.Entity;
// Assuming that there is already an existing WorldData record in the database with id 1 and country 'foo', and you want to change the country to 'bar'
using (var context = new MyContext())
{
var extraArticleAttributes = new ExtraArticleAttributes
{
world_data = new WorldData
{
worlddata_id = 1,
country = "bar"
}
};
db.ExtraArticleAttributes.Add(extraArticleAttributes);
db.Entry<WorldData>(extraArticleAttributes.world_data).State = EntityState.Modified;
db.SaveChanges();
// world data 1 country is now 'bar'
}

Hibernate #OneToMany remove child from list when updating parent

I have the following entities:
TEAM
#Entity
#Table
public class Team {
[..]
private Set<UserTeamRole> userTeamRoles;
/**
* #return the userTeamRoles
*/
#OneToMany(cascade = { CascadeType.ALL }, mappedBy = "team", fetch = FetchType.LAZY)
public Set<UserTeamRole> getUserTeamRoles() {
return userTeamRoles;
}
/**
* #param userTeamRoles
* the userTeamRoles to set
*/
public void setUserTeamRoles(Set<UserTeamRole> userTeamRoles) {
this.userTeamRoles = userTeamRoles;
}
}
and
USER_TEAM_ROLE
#Entity
#Table(name = "user_team_role")
public class UserTeamRole {
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinColumn(name = "FK_TeamId")
public Team getTeam() {
return team;
}
}
Now, when updating a Team entity that contains for example Team.userTeamRoles = {UTR1, UTR2} with {UTR1, UTR3}, I want UTR2 to be deleted. But the way I do it now, the old list remains the same and it only adds UTR3 to the list.
This is how I do it at the moment:
if (!usersDualListData.getTarget().isEmpty()) {
// the role for each user within the team will be "employee"
team.setUserTeamRoles(new HashSet<UserTeamRole>());
Role roleForUser = roleService
.getRoleByName(RoleNames.ROLE_EMPLOYEE.name());
for (User user : usersDualListData.getTarget()) {
UserTeamRole utr = new UserTeamRole();
utr.setUser(user);
utr.setTeam(team);
utr.setRole(roleForUser);
team.getUserTeamRoles().add(utr);
}
}
teamService.updateTeam(team);
I thought that by doing team.setUserTeamRoles(new HashSet<UserTeamRole>()); the list would be reset and because of the cascades the previous list would be deleted.
Any help is appreciated. Thank you
Instead of replacing the collection (team.setUserTeamRoles(new HashSet<UserTeamRole>());) you have to clear() the existing one. This happens because if Hibernate loads the entity (and its collections) from DB, it "manages" them, ie. tracks their changes. Generally when using Hibernate it's better not to create any setters for collections (lists, sets). Create only the getter, and clear the collection returned by it, ie:
team.getUserTeamRoles().clear();
Another thing is that you miss orphan deletion (ie. delete child object when it's removed from collection in the parent). To enable it, you need to add #OneToMany(orphanRemoval=true) in owning entity.

Resources