Insert CSV using Apex Batch Class Salesforce for OpportunityLineItem - salesforce

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.

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 :-)

How to get the code coverage required to deploy Apex Class

Please Help!!!
I have this Apex controller class that I'm trying to deploy from Salesforce Sandbox to Production but I'm not getting the code coverage required!!
the class is consuming 3 objects, one is stranded and 2 are custom and constructing a hierarchy tree view for those three objects.
//Apex Class
public class TeeView {
/* Wrapper class to contain the nodes and their children */
public class cNodes
{
Public StandardObject gparent {get;set;}
public List<CustomObject1__c> parent {get; set;}
Public List<CustomObject2__c> child {get;set;}
public cNodes(StandardObject gp, List<CustomObject1__c> p, List<CustomObject2__c> c)
{
parent = p;
gparent = gp;
child = c;
}
}
/* end of Wrapper class */
Public List<cNodes> hierarchy;
Public List<cNodes> getmainnodes()
{
hierarchy = new List<cNodes>();
List<StandardObject> tempparent = [Select Id,Name , End_Date__c, Owner.Name Account.Name from Contract ];
for (Integer i =0; i< tempparent.size() ; i++)
{
List<CustomObject1__c> tempchildren = [Select Id,Name, Owner.Name , (select Id,Name, Owner.Name from CustomObject2__r) from CustomObject1__c where Related_Field__c = :tempparent[i].Id];
List<CustomObject2__c> tempchild = [Select Id,Name Owner.Name from CustomObject2__c where Related_Field__c= :tempparent[i].Id];
hierarchy.add(new cNodes(tempparent[i],tempchildren, tempchild));
}
return hierarchy;
}
}
//Test Class
#isTest
public class treeviewTest
{
static testMethod void test1()
{
test.startTest();
Account acc = new Account(Name = 'Unit test account');
insert acc;
StandardObject c = new StandardObject(
Name = 'test',
AccountId = acc.Id,
Status = 'Draft',
StartDate = System.today());
try
{
insert c;
}
catch (Exception ex)
{
ApexPages.addMessages(ex);
}
List<StandardObject> standard = [select Id, Name from StandardObject where Name = 'test'];
system.assertequals(standard.size(), 1);
CustomObject1__c s = new CustomObject1__c(
Related_StandardObjectField__c = c.Id,
Name = 'test'
);
try
{
insert s;
}
catch (Exception ex)
{
ApexPages.addMessages(ex);
}
List<CustomObject1__c> cus1 = [select Id, Name from CustomObject1__c where Name = 'test'];
system.assertequals(cus1.size(), 1);
insert new CustomObject2__c(Related_StandardObjectField__c = c.Id, Description__c = 'test');
List<CustomObject2__c> cus2 = [select Id, Name from CustomObject2__c where Description__c = 'test'];
system.assertequals(cus2.size(), 1);
insert new CustomObject2__c(Related_CustomObject1Field__c = s.Id, Description__c = 'test');
List<Mods__c> cus3 = [select Id, Name from Mods__c where Description__c = 'test'];
system.assertequals(cus3.size(), 1);
treeView view = new treeView();
view.getmainnodes();
test.stopTest();
}
}
Oh man, where do we even start... Looks like you copy-pasted some code at random trying to get the test to work? Your code works on Contract, CustomObject1__c, CustomObject2__c. Your unit test inserts Contract, Subaward__c, Mods__c. Was this some attempt at anonymizing your code? And what's this List<StandardObject> thing, it shouldn't even compile.
I think you need help of a developer with more experience, you've cobbled something together but it doesn't follow the Salesforce best practices for code...
About the main class
Smart use of subqueries will mean
you waste less SOQL (it's bad idea to blindly select all records without any filters but whatever),
don't run into errors related to "too many SOQL statements" (the limit is 100 so since you query in a loop - your code will crash and burn the moment you have around 50 Contracts)
lead to compact code occupying less lines (easier to maintain & cover with tests).
I'd go with something like that, query Contract & first related list in one go and then the parents + children in another query. Using a Map to link them for displaying.
List<Contract> levelOneAndTwo = [SELECT Id, Name, End_Date__c, Owner.Name, Account.Name,
(SELECT Id,Name, Owner.Name FROM Subawards__r)
FROM Contract];
Map<Id, SubAward__c> levelTwoAndThree = new Map<Id, Subaward__c>([SELECT Id,
(SELECT Id,Name Owner.Name FROM Mods__r)
FROM Subaward__c WHERE Contract__c = :levelOneAndTwo];
);
This wastes only 2 queries but fetches all contracts and their related data. Then you can loop over results building your wrapper object or just feed it directly to Visualforce for displaying.
About the unit test
You don't get the coverage because you're spread too thin. One unit test methods inserts only Contracts (so your main query will hit something, give you some coverage). Then another method (in completely different context) inserts some child objects - but they don't matter. In that other method there are 0 contracts so it won't even enter your main loop. Combine it into one?
Account acc = new Account(Name = 'Unit test account');
insert acc;
Contract c = new Contract(
Name = 'test contract',
AccountId = acc.Id,
Status = 'Draft',
StartDate = System.today()
);
insert c;
Subaward__c s = new Subaward(
Contract__c = c.Id,
Name = 'test'
);
insert s;
insert new Mod__c(Subaward__c = c.Id, Description__c = 'test');
Now you have nice tree hierarchy set up, from Account all the way down. Queries in the test should hit some data.
I was finally able to get %100 code coverage for the above class by adding these lines to the test class:
treeView view = new treeView();
view.getmainnodes();
system.assert(view.hierarchy.size() > 0);

How to search for empty strings in a text field with Entity Framework?

I'd like to know how can I search for empty strings when I'm using a text type field with Entity Framework.
I've looked the SQL query that Entity is generating and It's using LIKE to compare because It's searching in a text type field, so when I use .Equals(""), == "", == string.Empty, .Contains(""), .Contains(string.Empty), and everything else, It's returning all results because it sql query is like '' and the == command throws exception because It uses the = command that is not valid with text type field.
When I try to use .Equals(null), .Contains(null), == null, It returns nothing, because It is generating FIELD ISNULL command.
I already tried the .Lenght == 0 but It throws an exception...
This works for me:
public class POCO
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
static void Main(string[] args)
{
var pocos = new List<POCO>
{
new POCO { Id = 1, Name = "John", Description = "basic" },
new POCO { Id = 2, Name = "Jane", Description = "" },
new POCO { Id = 3, Name = "Joey", Description = string.Empty }
};
pocos.Where(x => x.Description == string.Empty)
.ToList()
.ForEach(x => Console.WriteLine($"{x.Id} {x.Name} {x.Description}"));
}
However the issue MAY BE that your T4 generated object is not fully realized with data you can use, if you are using Entity Framework. EG the translation from the database is not populating objects to interrogate correctly. I would just do an operation like this to see:
using (var context = new YOURCONTEXTNAME())
{
var persons = context.YOURDATABASEOBJECT.ToList();
persons.ForEach(x => Console.WriteLine($"{x.COLUMNINQUESTION}"));
}
If you are successfully having data in it, it should be retrieved. I would not use text if possible. Use a varchar(max) nvarchar(max) xml, whatever text will be deprecated eventually and is bad form so to speak to continue using at this point.
EDIT
Okay I see, the answer is you cannot interogate the object until it is fully realized when it is text. I did a test on my local database and created a context and tested it and sure enough you cannot do a '== string.empty', '== ""', or 'String.IsNullOrEmpty()' on a text. However you can do it once the object is materialized in a realized object. EG:
// Won't work as context does not understand type
//var persons = context.tePersons.Where(x => x.Description == string.Empty).ToList();
//Works fine as transformation got the object translated to a string in .NET
var start = context.tePersons.ToList();
var persons = start.Where(x => x.Description == String.Empty).ToList();
This poses a problem obviously as you need to get ALL your data potentially before performing a predicate. Not the best means by any measure. You could do a sql object for this instead then to do a function, proc, or view to change this.

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

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

FIELD_CUSTOM_VALIDATION_EXCEPTION

I am writing a test class for a trigger. But i am not able to run it properly it only contain 68%.
The error is
System.DmlException: Insert failed. First exception on row 0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, This part is not forecastable.
#isTest
private class TestTriggers
{
static testMethod void testService()
{
//Insert part
list<Opportunity> Opportunity = new list<Opportunity>();
Opportunity = [Select id from Opportunity];
list<Product2> Product = new list<Product2>();
Product = [Select id from Product2];
Part__c p = new Part__c(Stage__c = 'idea',Product__c=Product[0].id,Opportunity__c=Opportunity[0].id);
insert p;
//Update part
list<part__c> partlist = new list<part__c>();
partlist = [Select Stage__c from part__c where Stage__c = 'idea'];
partlist[0].Stage__c = 'update';
update partlist;/* */
}
}
thanks
Anuraj
There is a validation rule on the Part object. To view the validation rule(s), go to Setup > Create > Objects > Part > Validation Rules.
You'll need to modify your code to create a Part__c record that complies with the validation rules.

Resources