-
ChatterFeed
-
0Best Answers
-
0Likes Received
-
0Likes Given
-
9Questions
-
14Replies
Is it possible to query data in a VisualForce page before insert?
I have a visualforce page that we use to add products to opporttunities. Before inserting the line items I need to run an aggregateresult query to get the values of several fields in order to insert an additional support line-item and populate it's fields with the aggregated results from the query. In order to accomplish this, I need to query the values that have been input in the Visualforce page before they are inserted into the database so that I can insert all of the line-items at once. Thanks for your help!
Here is my relevant code snippet:
public PageReference onSave(){ Decimal supportQty; Decimal svcTerm; Decimal sprice; List<Dynamic_Pricing__c> dplist = [SELECT Pricing_Coefficient__c, Pricing_Type__c FROM Dynamic_Pricing__c]; Map<string, decimal> supportPricingMap = new Map<string, decimal>(); List<PriceBookEntry> supportSkus = [SELECT Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c FROM PriceBookEntry WHERE Pricebook2Id =: theBook.id AND Product2.Type__c =: 'Support']; Map<String,PricebookEntry> supportTypeMap = new Map<String, PricebookEntry>(); // If previously selected products are now removed, we need to delete them if(forDeletion.size()>0) delete(forDeletion); for(Dynamic_Pricing__c dp : dplist){ if(!supportPricingMap.containsKey(dp.Pricing_Type__c)){ supportPricingMap.put(dp.Pricing_Type__c, dp.Pricing_Coefficient__c); } } for(PricebookEntry d : supportSkus){ if(!supportTypeMap.containsKey(d.Product2.SKU__c)){ supportTypeMap.put(d.Product2.SKU__c,d); } } AggregateResult[] groupedResults =[SELECT SUM(Quantity)sumQty, MAX(Service_Term_in_Months__c)svcTerm, SUM(UnitPrice)sprice FROM OpportunityLineItem WHERE Id in: shoppingCartProxy AND PriceBookEntry.Product2.Family =: 'MTP' AND PriceBookEntry.Product2.Type__c =: 'Subscription']; if(groupedResults.isempty()){ ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING,'Please enter value')); } for(AggregateResult ar : groupedResults){ supportQty = (Decimal)ar.get('sumQty'); svcTerm = (Decimal)ar.get('svcTerm'); sprice = (Decimal)ar.get('sprice'); } if(supportType!=null && supportType!='none'){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=supportTypeMap.get(supportType), PriceBookEntryId=supportTypeMap.get(supportType).Id, Quantity=supportQty, Service_Term_in_Months__c = 12, UnitPrice=12)); //* supportPricingMap.get(supportType), Discount_off_list_manual__c = 0)); } // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here try{ if(shoppingCart.size()>0) upsert(shoppingCart); } catch(Exception e){ ApexPages.addMessages(e); return null; } // After save return the user to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); }
And here is the entire controller:
public with sharing class opportunityProductEntryExtension { public Opportunity theOpp {get;set;} public String searchString {get;set;} public String supportType {get;set;} public OpportunityLineItem[] shoppingCart {get;set;} public List<OpportunityLineItem> shoppingCartProxy {get;set;} public PriceBookEntry[] availableProducts {get;set;} public Pricebook2 theBook {get;set;} public String toSelect {get; set;} public String toUnselect {get; set;} public Decimal Total {get;set;} public Boolean overLimit {get;set;} public Boolean multipleCurrencies {get; set;} private Boolean forcePricebookSelection = false; private OpportunityLineItem[] forDeletion = new opportunityLineItem[]{}; private ApexPages.StandardController std; public opportunityProductEntryExtension(ApexPages.StandardController controller) { std=controller; // Need to know if org has multiple currencies enabled multipleCurrencies = UserInfo.isMultiCurrencyOrganization(); // Get information about the Opportunity being worked on if(multipleCurrencies) theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1'); else theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1]; // If products were previously selected need to put them in the "selected products" section to start with shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from opportunityLineItem where OpportunityId=:theOpp.Id]; shoppingCartProxy =[select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from OpportunityLineItem where OpportunityId=:std.getId()]; // Check if Opp has a pricebook associated yet if(theOpp.Pricebook2Id == null){ Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2]; if(activepbs.size() == 2){ forcePricebookSelection = true; theBook = new Pricebook2(); } else{ theBook = activepbs[0]; } } else{ theBook = theOpp.Pricebook2; } if(!forcePricebookSelection) updateAvailableList(); } // this is the 'action' method on the page public PageReference priceBookCheck(){ // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen if(forcePricebookSelection){ return changePricebook(); } else{ //if there is only one active pricebook we go with it and save the opp if(theOpp.pricebook2Id != theBook.Id){ try{ theOpp.Pricebook2Id = theBook.Id; update(theOpp); } catch(Exception e){ ApexPages.addMessages(e); } } return null; } } public String getChosenCurrency(){ if(multipleCurrencies) return (String)theOpp.get('CurrencyIsoCode'); else return ''; } public void updateAvailableList() { String supportstr = 'Support'; // We dynamically build a query string and exclude items already in the shopping cart String qString = 'select Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c from PricebookEntry where IsActive=true AND Pricebook2Id = \'' + theBook.Id + '\''; if(multipleCurrencies) qstring += ' and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\''; // note that we are looking for the search string entered by the user in the name OR description // modify this to search other fields if desired if(searchString!=null){ qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')'; } Set<Id> selectedEntries = new Set<Id>(); for(opportunityLineItem d:shoppingCart){ selectedEntries.add(d.PricebookEntryId); } if(selectedEntries.size()>0){ String tempFilter = ' and Id not in ('; for(Id i : selectedEntries){ tempFilter+= '\'' + (String)i + '\','; } String extraFilter = tempFilter.substring(0,tempFilter.length()-1); extraFilter+= ')'; qString+= extraFilter; } qString+= ' AND Product2.Type__c != \'' + supportstr + '\''; qString+= ' order by Product2.Name'; qString+= ' limit 101'; system.debug('qString:' +qString); AvailableProducts = database.query(qString); // We only display up to 100 results... if there are more than we let the user know (see vf page) if(AvailableProducts.size()==101){ AvailableProducts.remove(100); overLimit = true; } else{ overLimit=false; } } public void addToShoppingCart(){ list<Price_Book_Discounts__c> discounts = [SELECT Discount__c, Name FROM Price_Book_Discounts__c WHERE Name =: theBook.Name]; list<SKU_Discounts__c> skus = [SELECT Discount__c, SKU__c, Price_Book_Name__c FROM SKU_Discounts__c WHERE Price_Book_Name__c =: theBook.Name]; list<Product2> products = [SELECT Product_Term_Years__c, Name FROM Product2]; Map<String, Decimal> pbNameToDiscountMap = new Map<String, Decimal>(); Map<String, Decimal> skuToDiscountMap = new Map<String, Decimal>(); Map<Decimal, Decimal> yearsToMonthsMap = new Map<Decimal, Decimal>(); // This function runs when a user hits "select" button next to a product for(Price_Book_Discounts__c discount : discounts){ pbNameToDiscountMap.put(discount.Name, discount.Discount__c); } for(SKU_Discounts__c sku : skus){ if (!SkuToDiscountMap.containsKey(sku.SKU__c)) { SkuToDiscountMap.put(sku.SKU__c, sku.Discount__c); } } for(Product2 prod : products){ if(!yearsToMonthsMap.containsKey(prod.Product_Term_Years__c)){ if(prod.Product_Term_Years__c == null){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 12); } if(prod.Product_Term_Years__c == 1){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 12); } if(prod.Product_Term_Years__c == 3){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 36); } } } for(PricebookEntry d : AvailableProducts){ if((String)d.Id==toSelect && d.Product2.Type__c!='Non-Revenue' && d.Product2.Type__c!='Support'){ if(d.Product2.SKU_Level_Discount__c == false){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = pbNameToDiscountMap.get(theBook.Name))); break; } if(d.Product2.SKU_Level_Discount__c == true){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = SkuToDiscountMap.get(d.Product2.SKU__c))); break; } } if((String)d.Id==toSelect && d.Product2.Type__c=='Non-Revenue' && d.Product2.Type__c=='Support'){ if(d.Product2.Type__c=='Non-Revenue'){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = 2, UnitPrice=d.UnitPrice, Discount_off_list_manual__c = 0)); break; }else{ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = 0)); break; } } } updateAvailableList(); } public PageReference removeFromShoppingCart(){ // This function runs when a user hits "remove" on an item in the "Selected Products" section Integer count = 0; for(opportunityLineItem d : shoppingCart){ if((String)d.PriceBookEntryId==toUnselect){ if(d.Id!=null) forDeletion.add(d); shoppingCart.remove(count); break; } count++; } updateAvailableList(); return null; } public PageReference onSave(){ Decimal supportQty; Decimal svcTerm; Decimal sprice; List<Dynamic_Pricing__c> dplist = [SELECT Pricing_Coefficient__c, Pricing_Type__c FROM Dynamic_Pricing__c]; Map<string, decimal> supportPricingMap = new Map<string, decimal>(); List<PriceBookEntry> supportSkus = [SELECT Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c FROM PriceBookEntry WHERE Pricebook2Id =: theBook.id AND Product2.Type__c =: 'Support']; Map<String,PricebookEntry> supportTypeMap = new Map<String, PricebookEntry>(); // If previously selected products are now removed, we need to delete them if(forDeletion.size()>0) delete(forDeletion); for(Dynamic_Pricing__c dp : dplist){ if(!supportPricingMap.containsKey(dp.Pricing_Type__c)){ supportPricingMap.put(dp.Pricing_Type__c, dp.Pricing_Coefficient__c); } } for(PricebookEntry d : supportSkus){ if(!supportTypeMap.containsKey(d.Product2.SKU__c)){ supportTypeMap.put(d.Product2.SKU__c,d); } } AggregateResult[] groupedResults =[SELECT SUM(Quantity)sumQty, MAX(Service_Term_in_Months__c)svcTerm, SUM(UnitPrice)sprice FROM OpportunityLineItem WHERE Id in: shoppingCartProxy AND PriceBookEntry.Product2.Family =: 'MTP' AND PriceBookEntry.Product2.Type__c =: 'Subscription']; if(groupedResults.isempty()){ ApexPages.addmessage(new ApexPages.message(ApexPages.severity.WARNING,'Please enter value')); } for(AggregateResult ar : groupedResults){ supportQty = (Decimal)ar.get('sumQty'); svcTerm = (Decimal)ar.get('svcTerm'); sprice = (Decimal)ar.get('sprice'); } if(supportType!=null && supportType!='none'){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=supportTypeMap.get(supportType), PriceBookEntryId=supportTypeMap.get(supportType).Id, Quantity=supportQty, Service_Term_in_Months__c = 12, UnitPrice=12)); //* supportPricingMap.get(supportType), Discount_off_list_manual__c = 0)); } // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here try{ if(shoppingCart.size()>0) upsert(shoppingCart); } catch(Exception e){ ApexPages.addMessages(e); return null; } // After save return the user to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference onCancel(){ // If user hits cancel we commit no changes and return them to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference changePricebook(){ // This simply returns a PageReference to the standard Pricebook selection screen // Note that is uses retURL parameter to make sure the user is sent back after they choose PageReference ref = new PageReference('/oppitm/choosepricebook.jsp'); ref.getParameters().put('id',theOpp.Id); ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id); return ref; } }
- Gabe Rothman 8
- June 14, 2016
- Like
- 0
Problem with controller method referenced in VF Page using apex:actionsupport tag
To accomplish this, I added a method to my controller and used an apex:actionsupport tag to refresh the quantity when an MTP product Quantity is set or updated. It's almost working correctly, but it seems to be double-counting the quantity from the MTP Products, and I can't figure out why. Any suggestions would be very much appreciated. Cheers!
Screen Shot:
Relevant Controller Method:
public PageReference calculateSupport(){ Decimal supportQuantity; supportQuantity = 0; for(OpportunityLineItem soli : supportShoppingCart){ if(supportQuantity!=null){ supportQuantity+=soli.Quantity; } } for(OpportunityLineItem oli : shoppingCart){ if(oli.PriceBookEntry.Product2.Type__c=='Support'){ oli.Quantity=supportQuantity; } } return null; }
Relevant VF Code:
<!--********** Quantity **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Quantity.Label}"> <apex:inputField value="{!s.Quantity}" id="quant" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support',False, True)}"> <apex:actionSupport event="onchange" reRender="tot"/> <apex:actionSupport event="onchange" action="{!calculateSupport}"/> </apex:inputField> <apex:outputText id="quantreadonly" value="{!s.Quantity}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support',True, False)}"/> </apex:column>
Entire Controller:
public with sharing class opportunityProductEntryExtension { public Opportunity theOpp {get;set;} public String searchString {get;set;} public OpportunityLineItem[] shoppingCart {get;set;} public OpportunityLineItem[] supportShoppingCart {get;set;} public PriceBookEntry[] AvailableProducts {get;set;} public Pricebook2 theBook {get;set;} public String toSelect {get; set;} public String toUnselect {get; set;} public Decimal Total {get;set;} public Boolean overLimit {get;set;} public Boolean multipleCurrencies {get; set;} private Boolean forcePricebookSelection = false; private opportunityLineItem[] forDeletion = new opportunityLineItem[]{}; public opportunityProductEntryExtension(ApexPages.StandardController controller) { // Need to know if org has multiple currencies enabled multipleCurrencies = UserInfo.isMultiCurrencyOrganization(); // Get information about the Opportunity being worked on if(multipleCurrencies) theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1'); else theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1]; // If products were previously selected need to put them in the "selected products" section to start with shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from opportunityLineItem where OpportunityId=:theOpp.Id]; supportShoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from opportunityLineItem where OpportunityId=:theOpp.Id AND PriceBookEntry.Product2.Family=:'MTP' AND PricebookEntry.Product2.Type__c=:'Subscription']; // Check if Opp has a pricebook associated yet if(theOpp.Pricebook2Id == null){ Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2]; if(activepbs.size() == 2){ forcePricebookSelection = true; theBook = new Pricebook2(); } else{ theBook = activepbs[0]; } } else{ theBook = theOpp.Pricebook2; } if(!forcePricebookSelection) updateAvailableList(); } // this is the 'action' method on the page public PageReference priceBookCheck(){ // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen if(forcePricebookSelection){ return changePricebook(); } else{ //if there is only one active pricebook we go with it and save the opp if(theOpp.pricebook2Id != theBook.Id){ try{ theOpp.Pricebook2Id = theBook.Id; update(theOpp); } catch(Exception e){ ApexPages.addMessages(e); } } return null; } } public String getChosenCurrency(){ if(multipleCurrencies) return (String)theOpp.get('CurrencyIsoCode'); else return ''; } public void updateAvailableList() { // We dynamically build a query string and exclude items already in the shopping cart String qString = 'select Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c from PricebookEntry where IsActive=true and Pricebook2Id = \'' + theBook.Id + '\''; if(multipleCurrencies) qstring += ' and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\''; // note that we are looking for the search string entered by the user in the name OR description // modify this to search other fields if desired if(searchString!=null){ qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')'; } Set<Id> selectedEntries = new Set<Id>(); for(opportunityLineItem d:shoppingCart){ selectedEntries.add(d.PricebookEntryId); } if(selectedEntries.size()>0){ String tempFilter = ' and Id not in ('; for(Id i : selectedEntries){ tempFilter+= '\'' + (String)i + '\','; } String extraFilter = tempFilter.substring(0,tempFilter.length()-1); extraFilter+= ')'; qString+= extraFilter; } qString+= ' order by Product2.Name'; qString+= ' limit 101'; system.debug('qString:' +qString); AvailableProducts = database.query(qString); // We only display up to 100 results... if there are more than we let the user know (see vf page) if(AvailableProducts.size()==101){ AvailableProducts.remove(100); overLimit = true; } else{ overLimit=false; } } public void addToShoppingCart(){ // This function runs when a user hits "select" button next to a product list<Price_Book_Discounts__c> discounts = [SELECT Discount__c, Name FROM Price_Book_Discounts__c WHERE Name =: theBook.Name]; list<SKU_Discounts__c> skus = [SELECT Discount__c, SKU__c, Price_Book_Name__c FROM SKU_Discounts__c WHERE Price_Book_Name__c =: theBook.Name]; list<Product2> products = [SELECT Product_Term_Years__c, Name FROM Product2]; Map<String, Decimal> pbNameToDiscountMap = new Map<String, Decimal>(); Map<String, Decimal> skuToDiscountMap = new Map<String, Decimal>(); Map<Decimal, Decimal> yearsToMonthsMap = new Map<Decimal, Decimal>(); for(Price_Book_Discounts__c discount : discounts){ pbNameToDiscountMap.put(discount.Name, discount.Discount__c); } for(SKU_Discounts__c sku : skus){ if (!SkuToDiscountMap.containsKey(sku.SKU__c)) { SkuToDiscountMap.put(sku.SKU__c, sku.Discount__c); } } for(Product2 prod : products){ if(!yearsToMonthsMap.containsKey(prod.Product_Term_Years__c)){ if(prod.Product_Term_Years__c == null){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 12); } if(prod.Product_Term_Years__c == 1){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 12); } if(prod.Product_Term_Years__c == 3){ yearsToMonthsMap.put(prod.Product_Term_Years__c, 36); } } } for(PricebookEntry d : AvailableProducts){ if((String)d.Id==toSelect && d.Product2.Name.contains('Support')){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = 12)); break; } if((String)d.Id==toSelect && d.UnitPrice<>0 && !d.Product2.Name.contains('Support')){ if(d.Product2.SKU_Level_Discount__c == false){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = pbNameToDiscountMap.get(theBook.Name))); break; } if(d.Product2.SKU_Level_Discount__c == true){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = SkuToDiscountMap.get(d.Product2.SKU__c))); break; } } if((String)d.Id==toSelect && d.UnitPrice==0){ if(d.Product2.Name.contains('Evaluation')){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = 2, UnitPrice=d.UnitPrice, Discount_off_list_manual__c = 0)); break; }else{ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, Quantity=0, Service_Term_in_Months__c = yearsToMonthsMap.get(d.Product2.Product_Term_Years__c), UnitPrice=d.UnitPrice, Discount_off_list_manual__c = 0)); break; } } } for(OpportunityLineItem soli : shoppingCart){ if(soli.PriceBookEntry.Product2.Type__c=='Subscription' && soli.PriceBookEntry.Product2.Family=='MTP'){ supportShoppingCart.add(soli); } } updateAvailableList(); } public PageReference calculateSupport(){ Decimal supportQuantity; supportQuantity = 0; for(OpportunityLineItem soli : supportShoppingCart){ if(supportQuantity!=null){ supportQuantity+=soli.Quantity; } } for(OpportunityLineItem oli : shoppingCart){ if(oli.PriceBookEntry.Product2.Type__c=='Support'){ oli.Quantity=supportQuantity; } } return null; } public PageReference removeFromShoppingCart(){ // This function runs when a user hits "remove" on an item in the "Selected Products" section Integer count = 0; for(opportunityLineItem d : shoppingCart){ if((String)d.PriceBookEntryId==toUnselect){ if(d.Id!=null) forDeletion.add(d); shoppingCart.remove(count); break; } count++; } updateAvailableList(); return null; } public PageReference onSave(){ // If previously selected products are now removed, we need to delete them if(forDeletion.size()>0) delete(forDeletion); // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here try{ if(shoppingCart.size()>0) upsert(shoppingCart); } catch(Exception e){ ApexPages.addMessages(e); return null; } // After save return the user to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference onCancel(){ // If user hits cancel we commit no changes and return them to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference changePricebook(){ // This simply returns a PageReference to the standard Pricebook selection screen // Note that is uses retURL parameter to make sure the user is sent back after they choose PageReference ref = new PageReference('/oppitm/choosepricebook.jsp'); ref.getParameters().put('id',theOpp.Id); ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id); return ref; } }
- Gabe Rothman 8
- June 09, 2016
- Like
- 0
Possible to use apex:actionSupport or Javascript to update values of one Opportunity Line Item with the values from another?
Hey folks, I have a somewhat complicated use-case I'm trying to solve. I have a visualforce page that we use to configure products. What I'm trying to do is derive the quantity of one particular type of product based on the quantity or quantities of another type of products and I want to do this within the browser and then insert the derived value into the database on Save. Specifically we have a product called "MTP" and another generic "Support" product. Whenever MTP product quantities are added or updated to the product "shopping-cart" visualforce page, I want ONLY the total of all MTP line-items to be automatically populated in the quantity field of the support product. See my example below:
Here's my current code:
Controller:
public with sharing class opportunityProductEntryExtension { public Opportunity theOpp {get;set;} public String searchString {get;set;} public opportunityLineItem[] shoppingCart {get;set;} public priceBookEntry[] AvailableProducts {get;set;} public Pricebook2 theBook {get;set;} public String toSelect {get; set;} public String toUnselect {get; set;} public Decimal Total {get;set;} public Boolean overLimit {get;set;} public Boolean multipleCurrencies {get; set;} private Boolean forcePricebookSelection = false; private opportunityLineItem[] forDeletion = new opportunityLineItem[]{}; public opportunityProductEntryExtension(ApexPages.StandardController controller) { // Need to know if org has multiple currencies enabled multipleCurrencies = UserInfo.isMultiCurrencyOrganization(); // Get information about the Opportunity being worked on if(multipleCurrencies) theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1'); else theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1]; // If products were previously selected need to put them in the "selected products" section to start with shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from opportunityLineItem where OpportunityId=:theOpp.Id]; // Check if Opp has a pricebook associated yet if(theOpp.Pricebook2Id == null){ Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2]; if(activepbs.size() == 2){ forcePricebookSelection = true; theBook = new Pricebook2(); } else{ theBook = activepbs[0]; } } else{ theBook = theOpp.Pricebook2; } if(!forcePricebookSelection) updateAvailableList(); } // this is the 'action' method on the page public PageReference priceBookCheck(){ // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen if(forcePricebookSelection){ return changePricebook(); } else{ //if there is only one active pricebook we go with it and save the opp if(theOpp.pricebook2Id != theBook.Id){ try{ theOpp.Pricebook2Id = theBook.Id; update(theOpp); } catch(Exception e){ ApexPages.addMessages(e); } } return null; } } public String getChosenCurrency(){ if(multipleCurrencies) return (String)theOpp.get('CurrencyIsoCode'); else return ''; } public void updateAvailableList() { // We dynamically build a query string and exclude items already in the shopping cart String qString = 'select Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c from PricebookEntry where IsActive=true and Pricebook2Id = \'' + theBook.Id + '\''; if(multipleCurrencies) qstring += ' and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\''; // note that we are looking for the search string entered by the user in the name OR description // modify this to search other fields if desired if(searchString!=null){ qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')'; } Set<Id> selectedEntries = new Set<Id>(); for(opportunityLineItem d:shoppingCart){ selectedEntries.add(d.PricebookEntryId); } if(selectedEntries.size()>0){ String tempFilter = ' and Id not in ('; for(Id i : selectedEntries){ tempFilter+= '\'' + (String)i + '\','; } String extraFilter = tempFilter.substring(0,tempFilter.length()-1); extraFilter+= ')'; qString+= extraFilter; } qString+= ' order by Product2.Name'; qString+= ' limit 101'; system.debug('qString:' +qString); AvailableProducts = database.query(qString); // We only display up to 100 results... if there are more than we let the user know (see vf page) if(AvailableProducts.size()==101){ AvailableProducts.remove(100); overLimit = true; } else{ overLimit=false; } } public void addToShoppingCart(){ // This function runs when a user hits "select" button next to a product list<Price_Book_Discounts__c> discounts = [SELECT Discount__c, Name FROM Price_Book_Discounts__c WHERE Name =: theBook.Name]; list<SKU_Discounts__c> skus = [SELECT Discount__c, SKU__c, Price_Book_Name__c FROM SKU_Discounts__c WHERE Price_Book_Name__c =: theBook.Name]; Map<String, Decimal> pbNameToDiscountMap = new Map<String, Decimal>(); Map<String, Decimal> SkuToDiscountMap = new Map<String, Decimal>(); for(Price_Book_Discounts__c discount : discounts){ pbNameToDiscountMap.put(discount.Name, discount.Discount__c); } for(SKU_Discounts__c sku : skus){ if (!SkuToDiscountMap.containsKey(sku.SKU__c)) { SkuToDiscountMap.put(sku.SKU__c, sku.Discount__c); } } for(PricebookEntry d : AvailableProducts){ if((String)d.Id==toSelect && d.Product2.Type__c == 'Support'){ shoppingCart.add(new opportunityLineItem(OpportunityId = theOpp.Id, PriceBookEntry = d, PriceBookEntryId = d.Id )); break; } if((String)d.Id==toSelect && d.Product2.Type__c == 'Non-Revenue'){ shoppingCart.add(new opportunityLineItem(OpportunityId = theOpp.Id, PriceBookEntry = d, PriceBookEntryId = d.Id, Service_Term_in_Months__c = 2, Quantity = 100 )); break; } if((String)d.Id==toSelect && d.Product2.Type__c == 'Subscription'){ if(SkuToDiscountMap.get(d.Product2.SKU__c)!=null){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.Disti_Transfer_Price__c, Disti_Transfer_Price2__c = d.Disti_Transfer_Price__c, List_Price__c = d.UnitPrice, Discount_off_list_manual__c = SkuToDiscountMap.get(d.Product2.SKU__c))); break; } if(SkuToDiscountMap.get(d.Product2.SKU__c)==null){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.Disti_Transfer_Price__c, Disti_Transfer_Price2__c = d.Disti_Transfer_Price__c, List_Price__c = d.UnitPrice, Discount_off_list_manual__c = pbNameToDiscountMap.get(theBook.Name))); break; } } } updateAvailableList(); } public PageReference calculateSupport(){ List<Id> parents = new list<Id>(); for(opportunityLineItem d : shoppingCart){ parents.add(d.OpportunityId); } List<OpportunityLineItem> olis = [SELECT Quantity, PriceBookEntry.Product2.Family FROM OpportunityLineItem WHERE Id in: parents AND PriceBookEntry.Product2.Family =: 'MTP']; double suppQuant = 0; for(OpportunityLineItem mtpOli : olis){ suppQuant += mtpOli.Quantity; } for(opportunityLineItem d : shoppingCart){ if(d.PriceBookEntry.Product2.Type__c == 'Support'){ d.Quantity = suppQuant; }else{ d.Quantity = d.Quantity; } } return null; } public PageReference calculateSalesPrice(){ for(opportunityLineItem d : shoppingCart){ decimal price = d.PriceBookEntry.UnitPrice - (d.PriceBookEntry.UnitPrice * (d.Discount_off_list_manual__c/100)); d.UnitPrice = price.setscale(2); } return null; } public PageReference calculateDiscount(){ for(opportunityLineItem d : shoppingCart){ decimal discount = ((d.PriceBookEntry.UnitPrice - d.UnitPrice)/d.PriceBookEntry.UnitPrice)*100; d.Discount_off_list_manual__c = discount.setscale(2); } return null; } public PageReference removeFromShoppingCart(){ // This function runs when a user hits "remove" on an item in the "Selected Products" section Integer count = 0; for(opportunityLineItem d : shoppingCart){ if((String)d.PriceBookEntryId==toUnselect){ if(d.Id!=null) forDeletion.add(d); shoppingCart.remove(count); break; } count++; } updateAvailableList(); return null; } public PageReference onSave(){ // If previously selected products are now removed, we need to delete them if(forDeletion.size()>0) delete(forDeletion); // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here try{ if(shoppingCart.size()>0) upsert(shoppingCart); } catch(Exception e){ ApexPages.addMessages(e); return null; } // After save return the user to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference onCancel(){ // If user hits cancel we commit no changes and return them to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference changePricebook(){ // This simply returns a PageReference to the standard Pricebook selection screen // Note that is uses retURL parameter to make sure the user is sent back after they choose PageReference ref = new PageReference('/oppitm/choosepricebook.jsp'); ref.getParameters().put('id',theOpp.Id); ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id); return ref; } }Visualforce Page:
<apex:page standardController="Opportunity" extensions="opportunityProductEntryExtension" action="{!priceBookCheck}" > <apex:sectionHeader Title="Manage {!$ObjectType.Product2.LabelPlural}" subtitle="{!opportunity.Name}"/> <apex:messages style="color:red"/> <style> .search{ font-size:14pt; margin-right: 20px; } .fyi{ color:red; font-style:italic; } .label{ margin-right:10px; font-weight:bold; vertical-align: text-bottom; } .pbTitle { white-space: nowrap; } </style> <script type='text/javascript'> // This script assists the search bar functionality // It will execute a search only after the user has stopped typing for more than 1 second // To raise the time between when the user stops typing and the search, edit the following variable: var waitTime = 0.1; var countDown = waitTime+1; var started = false; function resetTimer(){ countDown=waitTime+0.1; if(started==false){ started=true; runCountDown(); } } function runCountDown(){ countDown--; if(countDown<=0){ fetchResults(); started=false; } else{ window.setTimeout(runCountDown,1000); } } </script> <apex:form > <div id="InternalDiv" styleClass="pbTitle" style="width:600px; float:right; margin-top:-60px; font-size:9px;"> </div> <apex:outputPanel id="mainBody"> <div id="pbName" styleClass="pbName" style="margin-top:0px;"> <apex:outputLabel styleClass="label">PriceBook: </apex:outputLabel> <apex:outputText value="{!theBook.Name}"/> <apex:commandLink action="{!changePricebook}" value="change" immediate="true"/> </div> <br/> <!-- not everyone is using multi-currency, so this section may or may not show --> <apex:outputPanel rendered="{!multipleCurrencies}"> <apex:outputLabel styleClass="label">Currency: </apex:outputLabel> <apex:outputText value="{!chosenCurrency}"/> <br/> </apex:outputPanel> <br/> <!-- this is the upper table... a.k.a. the "Shopping Cart"--> <!-- notice we use a lot of $ObjectType merge fields... I did that because if you have changed the labels of fields or objects it will reflect your own lingo --> <apex:pageBlock title="Selected {!$ObjectType.Product2.LabelPlural}" id="selected"> <apex:pageblockTable value="{!shoppingCart}" var="s"> <apex:column > <apex:commandLink value="Remove" action="{!removeFromShoppingCart}" reRender="selected,searchResults" immediate="true"> <!-- this param is how we send an argument to the controller, so it knows which row we clicked 'remove' on --> <apex:param value="{!s.PriceBookEntryId}" assignTo="{!toUnselect}" name="toUnselect"/> </apex:commandLink> </apex:column> <!--********** Product Name **********--> <apex:column headerValue="{!$ObjectType.Product2.LabelPlural}" value="{!s.PriceBookEntry.Product2.Name}"/> <!--********** Product SKU **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.SKU__c.Label}" value="{!s.PriceBookEntry.Product2.SKU__c}"/> <!--********** Annual Product Term **********--> <apex:column headerValue="Annual Term" id="pt" rendered="false" value="{!s.PriceBookEntry.Product2.Product_Term_Years__c}"/> <!--********** Quantity **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Quantity.Label}"> <apex:inputField value="{!s.Quantity}" id="quant" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onkeyup" reRender="tot"/> <apex:actionSupport event="onchange" action="{!calculateSupport}"/> </apex:inputField> <apex:outputText id="quants" value="{0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.Quantity}"/> </apex:outputText> </apex:column> <!--********** Service Term in Months **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Service_Term_in_Months__c.Label}"> <apex:inputField value="{!s.Service_Term_in_Months__c}" id="svc" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onkeyup" reRender="tot"/> </apex:inputField> <apex:outputText id="svcs" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Discount (Manual) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Discount_off_list_manual__c.Label}"> <apex:inputField value="{!s.Discount_off_list_manual__c}" id="disc" style="width:70px" required="True" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onchange" reRender="tot"/> <apex:actionSupport event="onkeyup" action="{!calculateSalesPrice}"/> </apex:inputField> <apex:outputText id="discs" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Sales Price **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.UnitPrice.Label}"> <apex:inputField value="{!s.UnitPrice}" id="sp" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onchange" reRender="tot"/> <apex:actionSupport event="onkeyup" action="{!calculateDiscount}"/> </apex:inputField> <apex:outputText id="sps" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Disti Transfer Price (Custom) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Disti_Transfer_Price2__c.Label}"> <apex:outputText id="dpc" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!s.PriceBookEntry.Disti_Transfer_Price__c}"/> </apex:outputText> </apex:column> <!--********** List Price (Custom) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.List_Price__c.Label}"> <apex:outputText id="lpc" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Disti Transfer Price (Standard) Hidden **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Disti_Transfer_Price__c.Label}" value="{!s.PriceBookEntry.Disti_Transfer_Price__c}" id="dp" rendered="false"/> <!--********** List Price (Standard) Hidden **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.ListPrice.Label}" id="lp" value="{!s.PriceBookEntry.UnitPrice}" rendered="false"/> <!--********** Total Price (display only field) **********--> <apex:column headerValue="Total Price"> <apex:outputText id="tot" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!IF(s.PriceBookEntry.Product2.Product_Term_Years__c != null,(s.Quantity * s.Service_Term_in_Months__c * s.UnitPrice)/(s.PriceBookEntry.Product2.Product_Term_Years__c*12),(s.Quantity * s.UnitPrice)) }"/> </apex:outputText> </apex:column> </apex:pageblockTable> <apex:pageBlockButtons > <apex:commandButton action="{!onSave}" value="Save"/> <apex:commandButton action="{!onCancel}" value="Cancel" immediate="true"/> </apex:pageBlockButtons> </apex:pageBlock> <!-- this is the lower table: search bar and search results --> <apex:pageBlock > <apex:outputPanel styleClass="search"> Search for {!$ObjectType.Product2.LabelPlural}: </apex:outputPanel> <apex:actionRegion renderRegionOnly="false" immediate="true"> <apex:actionFunction name="fetchResults" action="{!updateAvailableList}" reRender="searchResults" status="searchStatus"/> <!-- here we invoke the scripting to get out fancy 'no button' search bar to work --> <apex:inputText value="{!searchString}" onkeydown="if(event.keyCode==13){this.blur();}else{resetTimer();}" style="width:300px"/> <i> <!-- actionStatus component makes it easy to let the user know when a search is underway --> <apex:actionStatus id="searchStatus" startText="searching..." stopText=" "/> </i> </apex:actionRegion> <br/> <br/> <apex:outputPanel id="searchResults"> <apex:pageBlockTable value="{!AvailableProducts}" var="a"> <apex:column headerValue="{!$ObjectType.Product2.Fields.Name.Label}" value="{!a.Product2.Name}" /> <apex:column headerValue="{!$ObjectType.Product2.Fields.SKU__c.Label}" value="{!a.Product2.SKU__c}"/> <apex:column headerValue="{!$ObjectType.Product2.Fields.Description.Label}" value="{!a.Product2.Description}"/> <apex:column > <!-- command button in a column... neato --> <apex:commandButton value="Select" action="{!addToShoppingCart}" reRender="selected,searchResults" immediate="true"> <!-- again we use apex:param to be able to tell the controller which row we are working with --> <apex:param value="{!a.Id}" assignTo="{!toSelect}" name="toSelect"/> </apex:commandButton> </apex:column> </apex:pageBlockTable> <!-- We put up a warning if results exceed 100 rows --> <apex:outputPanel styleClass="fyi" rendered="{!overLimit}"> <br/> Your search returned over 100 results, use a more specific search string if you do not see the desired {!$ObjectType.Product2.Label}. <br/> </apex:outputPanel> </apex:outputPanel> </apex:pageBlock> </apex:outputPanel> </apex:form> </apex:page>
- Gabe Rothman 8
- June 04, 2016
- Like
- 0
Banging my head against a wall -- visualforce page pass picklist value into apex:iframe src parameter
Can someone help me sort it out? Thanks!
Controller:
public class domoDashboardController { public List<String> pagename { get { if (pagename == null) { pagename = new List<String>(); list<ApexPage> pages = [SELECT Name FROM ApexPage WHERE Name != 'domoDashBoardViewer' AND Name LIKE 'DOMO_%' ORDER BY Name ASC]; for (ApexPage page : pages){ pagename.add(page.Name); } } return pagename; } set; } }
VF Page:
<apex:page controller="domoDashboardController"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <apex:pageBlock title="DOMO Dashboard Viewer" mode="edit"> <apex:form> <table width="100%" border="0"> <tr> <td style="font-weight:bold; padding-left: 5px; padding-top:10px; width: 125px;"> Select Dashboard: </td> <td style="font-weight:bold; padding-top:8px;"> <select id="pagename"> <option value=""></option> <apex:repeat value="{!pagename}" var="pag"> <option value="{!pag}">{!pag}</option> </apex:repeat> </select> </td> </tr> </table> <apex:pageBlock> <table width="100%" border="0"> <tr> <apex:page id="thePage"> <apex:iframe src="" scrolling="true" id="theIframe"/> </apex:page> </tr> </table> </apex:pageBlock> </Apex:form> </apex:pageBlock> </apex:page>
- Gabe Rothman 8
- March 30, 2016
- Like
- 0
Can anyone help me to bulk-safe this class? Any help would be greatly appreciated...
public with sharing class setAccountGeoFromCountryService { public static void updateGeo(List<Account> Accounts) { List<String> listCountries = new List<String>(); List<String> listStates = new List<String>(); List<String> listZips = new List<String>(); for (Account a : Accounts) { if(a.BillingCountry == null){ return; } if(a.BillingCountry != null){ listCountries.add(a.BillingCountry ); if(a.BillingCountry != 'US'){ listStates.add('NULL'); listZips.add('NULL'); } if(a.BillingCountry == 'US'){ listStates.add(a.BillingState ); if(a.BillingState == 'CA' || a.BillingState == 'California'){ listZips.add(a.BillingPostalCode); } if(a.BillingState != 'CA' && a.BillingState != 'California'){ listZips.add('NULL'); } } } } try{ Territories__c territory = [SELECT Country_Code__c, Region__c, Theater__c, Country__c, State__c, State_Code__c, Postal_Code__c FROM Territories__c WHERE Country_Code__c IN: listCountries AND (State__c =: listStates OR State_Code__c =: listStates) AND Postal_Code__c =: listZips]; system.debug(territory); for (Account a2: Accounts){ if(a2.Owner_is_Federal__c==true){ a2.Geography__c = 'Federal'; a2.Country2__c = territory.Country__c; a2.Region__c = territory.Region__c; } if(a2.Owner_is_Federal__c==false){ a2.Geography__c = territory.Theater__c; a2.Country2__c = territory.Country__c; a2.Region__c = territory.Region__c; } } }catch(QueryException e){} } }
- Gabe Rothman 8
- December 31, 2016
- Like
- 0
Can someone help me bulkify this class?
"rfp: execution of BeforeInsert
caused by: System.QueryException: List has more than 1 row for assignment to SObject
Class.setRfpOwner.setOwners: line 15, column 1
Trigger.rfp: line 4, column 1 "
After looking at my code and the records I'm querying, I the query in line 15 of my service class should only yield one result, which leads me to believe that my service class is somehow not bulk safe. Can anyone point me in the right direction:
Trigger:
trigger rfp on RFP_Response__c (before insert, before update) { if(trigger.isBefore && trigger.isInsert){ setRfpOwner.setOwners(trigger.new); rfpKwBuilder.buildKeyWords(Trigger.new); } if(trigger.isBefore && trigger.isUpdate){ if(checkRecursive.flag == true){ rfpKwBuilder.buildKeyWords(Trigger.new); for(RFP_Response__c rfp : Trigger.new){ RFP_Response__c oldRfp = Trigger.oldMap.get(rfp.Id); if(oldRfp.Response__c != rfp.Response__c){ setResponseToggle.toggleResponse(trigger.new); } } for(RFP_Response__c rfp : Trigger.new){ RFP_Response__c oldRfp = Trigger.oldMap.get(rfp.Id); if(oldRfp.Category__c != rfp.Category__c){ setRfpOwner.setOwners(trigger.new); } } } checkRecursive.flag = false; } }
Class:
public class setRfpOwner { public static void setOwners(list<RFP_Response__c> rfpUpdateList){ List<String> listCategories = new List<String>(); for (RFP_Response__c rfp : rfpUpdateList) { if(rfp.Category__c == null){ return; } if(rfp.Category__c != null){ listCategories.add(rfp.Category__c); } } RFP_Owner_Mappings__c mapping = [SELECT Name, RFP_Category_Owner__c, RFP_Category_Backup_Owner__c,RFP_Category_Owner__r.Email__c,RFP_Category_Backup_Owner__r.Email__c FROM RFP_Owner_Mappings__c WHERE Name IN :listCategories]; for (RFP_Response__c rfp2 : rfpUpdateList){ if(rfp2.Category__c == mapping.Name ){ rfp2.RFP_Category_Owner__c = mapping.RFP_Category_Owner__c; rfp2.RFP_Category_Backup_Owner__c = mapping.RFP_Category_Backup_Owner__c ; rfp2.Category_Owner_Email__c = mapping.RFP_Category_Owner__r.Email__c; rfp2.Category_Backup_Owner_Email__c = mapping.RFP_Category_Backup_Owner__r.Email__c ; } } } }
- Gabe Rothman 8
- December 10, 2015
- Like
- 0
Can anyone help me optimize this class? (needs to perform better in bulk processing)
public class syncContactOwnerWithAccount { public static list<Account> filterAcc(map<id, Account> oldMap, list<Account> newList) { list<Account> temp=new list<Account>(); for (Account acc : newList){ if (oldMap.get(acc.id).OwnerId != acc.OwnerId){ temp.add(acc); } } return temp; } public static void updateContacts(List<Account> Accounts) { list<Account> alist = new list <Account>(); for (Account a2 : Accounts){ alist.add(a2); } list<Contact> clist = [SELECT AccountId, Account.OwnerId FROM Contact WHERE AccountId in: alist]; list<Contact> updatelist = new list<Contact>(); for(Contact c : clist){ c.OwnerId = c.Account.OwnerId; updatelist.add(c); } update updatelist; } }
- Gabe Rothman 8
- October 21, 2015
- Like
- 0
Help: getting "list has no rows for assignment" error, but debug log says otherwise...
Trigger:
trigger Feature on Feature__c (after update) { if(trigger.isAfter && trigger.isUpdate){ for(Feature__c f : trigger.new){ if(f.Status__c == 'Accepted'){ updateFrsFromApprovedFeature.updateFeatureRequests(updateFrsFromApprovedFeature.filterFeature(trigger.oldmap,trigger.new)); } } } }
Helper Class:
public class updateFrsFromApprovedFeature { public static list<Feature__c> filterFeature(map<id, Feature__c> oldMap, list<Feature__c> newList) { list<Feature__c> temp=new list<Feature__c>(); for (Feature__c newFeat : newList) { if (oldMap.get(newFeat.id).Status__c != newFeat.Status__c && newFeat.Status__c == 'Accepted'){ temp.add(newFeat); } } return temp; } public static void updateFeatureRequests(list<Feature__c> frUpdateList) { Feature__c featproxy = [SELECT Id, Status__c FROM Feature__c WHERE Id in: frUpdateList]; system.debug(featproxy); List<Feature_Request__c> frs = [SELECT Feature__c, Proposed_Feature__c FROM Feature_Request__c WHERE Proposed_Feature__c =: featproxy.id]; system.debug(frs); List<Feature_Request__c> updateFrs = new list<Feature_Request__c>(); for(Feature_Request__c updatefr : frs){ updatefr.Feature__c = featproxy.Id; updatefr.Proposed_Feature__c = null; updateFrs.add(updatefr); } system.debug(updateFrs); If(updateFrs.size()>0){ update updateFrs; } } }
Debug Statements:
09:10:30:109 USER_DEBUG [17]|DEBUG|Feature__c:{Id=a11i000000CcIJYAA3} 09:10:30:114 USER_DEBUG [21]|DEBUG|(Feature_Request__c:{Feature__c=a11i000000CdxuPAAR, Proposed_Feature__c=a11i000000CcIJYAA3, Id=a10e0000002L7K4AAK}, Feature_Request__c:{Feature__c=a11i000000CdxuPAAR, Proposed_Feature__c=a11i000000CcIJYAA3, Id=a10e0000002L7K9AAK}) 09:10:30:115 USER_DEBUG [28]|DEBUG|(Feature_Request__c:{Feature__c=a11i000000CcIJYAA3, Proposed_Feature__c=null, Id=a10e0000002L7K4AAK}, Feature_Request__c:{Feature__c=a11i000000CcIJYAA3, Proposed_Feature__c=null, Id=a10e0000002L7K9AAK})
Link to Complete Debug Log: https://docs.google.com/document/d/1S7n2LWqWfG7NU97sJ9E1ZAooIwDdiHiTpgEyWShzHXg/edit?usp=sharing
- Gabe Rothman 8
- August 12, 2015
- Like
- 0
Need help mapping productId to pricebookentryid
I'm working a bit of code to automatically re-add products to an opportunity if they get deleted as a result of the user changing the pricebook. I experimented with trying to clone the OLIs in a beforeDelete trigger, but couldn't get that work, I assume becuase updating the pricebook deleted the newly created clones in addition to the old products.
Instead I've built a custom object called OLI_Clone__c for which records are inserted every time a new OLI is added to an oppty and then deleted later when the oppty goes to closed won or closed lost. Basically, when the pricebook updates on the oppty, the code queries back to the OLI_Clone__c records associated with the oppty and then uses the data stored there to recreate the OLIs. The biggest challenge is that I can't create a lookup to ProcebookEntry on a custom object so I had to create a lookup to Product and then use a combination of the Opportunity's new Pricebook Id and the OLI_Clone__c's product Id to query for the correct PricebookEntry.
I've been able to get a list of PricebookEntryIds, but I can't figure out how get the correct PricebookEntryId for each OLI_Clone__c without querying for it inside of my for loop, which is obviously a big no no. I do know that I should be using a map, but I am relatively novice coder, and I just can't seem to get the syntax right.
Thanks in advance for your help!
I've pasted my service class below with a few comments:
public with sharing class autoAddProductsAfterPBChange { //create a filter to listen for a change in the pricebook below public static list<opportunity> filterOpp(map<id, opportunity> oldMap, list<opportunity> newList) { list<opportunity> temp=new list<opportunity>(); for (opportunity newOpp : newList){ if (oldMap.get(newOpp.id).PriceBook2Id != newOpp.PriceBook2Id){ temp.add(newOpp); } } return temp; } //find the pricebook entries and recreate the OLIs public static void autoAdd(list<Opportunity> opps){ Opportunity opp = new Opportunity(); Pricebook2 pb = new Pricebook2(); for(Opportunity opp2 : opps){ opp.id = opp2.Id; pb.id = opp2.Pricebook2Id; } list<OLI_Clone__c> olics = [SELECT Quantity__c, Additional_Discount_off_List__c, Manual_Discount__c, Opportunity__c, Product__c, Sales_Price__c, Service_Term_in_Months__c FROM OLI_Clone__c WHERE Opportunity__c =: opp.id]; list<Id> prodids = new list<Id>(); for(OLI_Clone__c olicprod : olics){ prodids.add(olicprod.Product__c); } List<PricebookEntry>pbeids = [SELECT Id, Product2Id FROM PricebookEntry WHERE Product2Id in: prodids AND Pricebook2Id =: pb.id]; Map<Id, PricebookEntry> pbeMap = new Map<Id, PricebookEntry>(pbeids); // Is this map correct? list<OpportunityLineItem>toInsert = new list<OpportunityLineItem>(); for(OLI_Clone__c olic : olics){ if(pbeMap.containsKey(olic.Product__c)){ OpportunityLineItem oli = new OpportunityLineItem(); PricebookEntry pbe = pbeMap.get(); // I know I need to be "getting" something here but I'm not sure how to structure the syntax, and I'm not sure that my map above is configured correctly either. Basically I need to get the correct PricebookEntryId based on the Product__c field on the OLI_Clone__c in this loop and the parent Opportunity's pricebook. oli.Quantity = olic.Quantity__c; oli.UnitPrice = olic.Sales_Price__c; toInsert.add(oli); } } insert toInsert; } }
- Gabe Rothman 8
- July 21, 2015
- Like
- 0
Possible to use apex:actionSupport or Javascript to update values of one Opportunity Line Item with the values from another?
Hey folks, I have a somewhat complicated use-case I'm trying to solve. I have a visualforce page that we use to configure products. What I'm trying to do is derive the quantity of one particular type of product based on the quantity or quantities of another type of products and I want to do this within the browser and then insert the derived value into the database on Save. Specifically we have a product called "MTP" and another generic "Support" product. Whenever MTP product quantities are added or updated to the product "shopping-cart" visualforce page, I want ONLY the total of all MTP line-items to be automatically populated in the quantity field of the support product. See my example below:
Here's my current code:
Controller:
public with sharing class opportunityProductEntryExtension { public Opportunity theOpp {get;set;} public String searchString {get;set;} public opportunityLineItem[] shoppingCart {get;set;} public priceBookEntry[] AvailableProducts {get;set;} public Pricebook2 theBook {get;set;} public String toSelect {get; set;} public String toUnselect {get; set;} public Decimal Total {get;set;} public Boolean overLimit {get;set;} public Boolean multipleCurrencies {get; set;} private Boolean forcePricebookSelection = false; private opportunityLineItem[] forDeletion = new opportunityLineItem[]{}; public opportunityProductEntryExtension(ApexPages.StandardController controller) { // Need to know if org has multiple currencies enabled multipleCurrencies = UserInfo.isMultiCurrencyOrganization(); // Get information about the Opportunity being worked on if(multipleCurrencies) theOpp = database.query('select Id, Pricebook2Id, Pricebook2.Name, CurrencyIsoCode from Opportunity where Id = \'' + controller.getRecord().Id + '\' limit 1'); else theOpp = [select Id, Pricebook2Id, PriceBook2.Name from Opportunity where Id = :controller.getRecord().Id limit 1]; // If products were previously selected need to put them in the "selected products" section to start with shoppingCart = [select Id, Quantity, TotalPrice, UnitPrice, Description, List_Price__c, Disti_Transfer_Price2__c, Disti_Transfer_Price__c, PriceBookEntryId, PriceBookEntry.Name, PriceBookEntry.UnitPrice, PriceBookEntry.IsActive, PriceBookEntry.Product2Id, PriceBookEntry.Product2.Name, PriceBookEntry.Product2.Product_Term_Years__c, PriceBookEntry.PriceBook2Id, PriceBookEntry.Product2.SKU__c, PriceBookEntry.Product2.Family, PriceBookEntry.Product2.Type__c, Service_Term_in_Months__c, PriceBookEntry.Disti_Transfer_Price__c, Discount_off_list_manual__c from opportunityLineItem where OpportunityId=:theOpp.Id]; // Check if Opp has a pricebook associated yet if(theOpp.Pricebook2Id == null){ Pricebook2[] activepbs = [select Id, Name from Pricebook2 where isActive = true limit 2]; if(activepbs.size() == 2){ forcePricebookSelection = true; theBook = new Pricebook2(); } else{ theBook = activepbs[0]; } } else{ theBook = theOpp.Pricebook2; } if(!forcePricebookSelection) updateAvailableList(); } // this is the 'action' method on the page public PageReference priceBookCheck(){ // if the user needs to select a pricebook before we proceed we send them to standard pricebook selection screen if(forcePricebookSelection){ return changePricebook(); } else{ //if there is only one active pricebook we go with it and save the opp if(theOpp.pricebook2Id != theBook.Id){ try{ theOpp.Pricebook2Id = theBook.Id; update(theOpp); } catch(Exception e){ ApexPages.addMessages(e); } } return null; } } public String getChosenCurrency(){ if(multipleCurrencies) return (String)theOpp.get('CurrencyIsoCode'); else return ''; } public void updateAvailableList() { // We dynamically build a query string and exclude items already in the shopping cart String qString = 'select Id, Name, Pricebook2.Name, Pricebook2Id, IsActive, Product2.Name, Product2.Family, Product2.Type__c, Product2.IsActive, Product2.Description, UnitPrice, Product2.SKU__c, Disti_Transfer_Price__c, Product2.Product_Term_Years__c, Product2.SKU_Level_Discount__c from PricebookEntry where IsActive=true and Pricebook2Id = \'' + theBook.Id + '\''; if(multipleCurrencies) qstring += ' and CurrencyIsoCode = \'' + theOpp.get('currencyIsoCode') + '\''; // note that we are looking for the search string entered by the user in the name OR description // modify this to search other fields if desired if(searchString!=null){ qString+= ' and (Product2.Name like \'%' + searchString + '%\' or Product2.Description like \'%' + searchString + '%\')'; } Set<Id> selectedEntries = new Set<Id>(); for(opportunityLineItem d:shoppingCart){ selectedEntries.add(d.PricebookEntryId); } if(selectedEntries.size()>0){ String tempFilter = ' and Id not in ('; for(Id i : selectedEntries){ tempFilter+= '\'' + (String)i + '\','; } String extraFilter = tempFilter.substring(0,tempFilter.length()-1); extraFilter+= ')'; qString+= extraFilter; } qString+= ' order by Product2.Name'; qString+= ' limit 101'; system.debug('qString:' +qString); AvailableProducts = database.query(qString); // We only display up to 100 results... if there are more than we let the user know (see vf page) if(AvailableProducts.size()==101){ AvailableProducts.remove(100); overLimit = true; } else{ overLimit=false; } } public void addToShoppingCart(){ // This function runs when a user hits "select" button next to a product list<Price_Book_Discounts__c> discounts = [SELECT Discount__c, Name FROM Price_Book_Discounts__c WHERE Name =: theBook.Name]; list<SKU_Discounts__c> skus = [SELECT Discount__c, SKU__c, Price_Book_Name__c FROM SKU_Discounts__c WHERE Price_Book_Name__c =: theBook.Name]; Map<String, Decimal> pbNameToDiscountMap = new Map<String, Decimal>(); Map<String, Decimal> SkuToDiscountMap = new Map<String, Decimal>(); for(Price_Book_Discounts__c discount : discounts){ pbNameToDiscountMap.put(discount.Name, discount.Discount__c); } for(SKU_Discounts__c sku : skus){ if (!SkuToDiscountMap.containsKey(sku.SKU__c)) { SkuToDiscountMap.put(sku.SKU__c, sku.Discount__c); } } for(PricebookEntry d : AvailableProducts){ if((String)d.Id==toSelect && d.Product2.Type__c == 'Support'){ shoppingCart.add(new opportunityLineItem(OpportunityId = theOpp.Id, PriceBookEntry = d, PriceBookEntryId = d.Id )); break; } if((String)d.Id==toSelect && d.Product2.Type__c == 'Non-Revenue'){ shoppingCart.add(new opportunityLineItem(OpportunityId = theOpp.Id, PriceBookEntry = d, PriceBookEntryId = d.Id, Service_Term_in_Months__c = 2, Quantity = 100 )); break; } if((String)d.Id==toSelect && d.Product2.Type__c == 'Subscription'){ if(SkuToDiscountMap.get(d.Product2.SKU__c)!=null){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.Disti_Transfer_Price__c, Disti_Transfer_Price2__c = d.Disti_Transfer_Price__c, List_Price__c = d.UnitPrice, Discount_off_list_manual__c = SkuToDiscountMap.get(d.Product2.SKU__c))); break; } if(SkuToDiscountMap.get(d.Product2.SKU__c)==null){ shoppingCart.add(new opportunityLineItem(OpportunityId=theOpp.Id, PriceBookEntry=d, PriceBookEntryId=d.Id, UnitPrice=d.Disti_Transfer_Price__c, Disti_Transfer_Price2__c = d.Disti_Transfer_Price__c, List_Price__c = d.UnitPrice, Discount_off_list_manual__c = pbNameToDiscountMap.get(theBook.Name))); break; } } } updateAvailableList(); } public PageReference calculateSupport(){ List<Id> parents = new list<Id>(); for(opportunityLineItem d : shoppingCart){ parents.add(d.OpportunityId); } List<OpportunityLineItem> olis = [SELECT Quantity, PriceBookEntry.Product2.Family FROM OpportunityLineItem WHERE Id in: parents AND PriceBookEntry.Product2.Family =: 'MTP']; double suppQuant = 0; for(OpportunityLineItem mtpOli : olis){ suppQuant += mtpOli.Quantity; } for(opportunityLineItem d : shoppingCart){ if(d.PriceBookEntry.Product2.Type__c == 'Support'){ d.Quantity = suppQuant; }else{ d.Quantity = d.Quantity; } } return null; } public PageReference calculateSalesPrice(){ for(opportunityLineItem d : shoppingCart){ decimal price = d.PriceBookEntry.UnitPrice - (d.PriceBookEntry.UnitPrice * (d.Discount_off_list_manual__c/100)); d.UnitPrice = price.setscale(2); } return null; } public PageReference calculateDiscount(){ for(opportunityLineItem d : shoppingCart){ decimal discount = ((d.PriceBookEntry.UnitPrice - d.UnitPrice)/d.PriceBookEntry.UnitPrice)*100; d.Discount_off_list_manual__c = discount.setscale(2); } return null; } public PageReference removeFromShoppingCart(){ // This function runs when a user hits "remove" on an item in the "Selected Products" section Integer count = 0; for(opportunityLineItem d : shoppingCart){ if((String)d.PriceBookEntryId==toUnselect){ if(d.Id!=null) forDeletion.add(d); shoppingCart.remove(count); break; } count++; } updateAvailableList(); return null; } public PageReference onSave(){ // If previously selected products are now removed, we need to delete them if(forDeletion.size()>0) delete(forDeletion); // Previously selected products may have new quantities and amounts, and we may have new products listed, so we use upsert here try{ if(shoppingCart.size()>0) upsert(shoppingCart); } catch(Exception e){ ApexPages.addMessages(e); return null; } // After save return the user to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference onCancel(){ // If user hits cancel we commit no changes and return them to the Opportunity return new PageReference('/' + ApexPages.currentPage().getParameters().get('Id')); } public PageReference changePricebook(){ // This simply returns a PageReference to the standard Pricebook selection screen // Note that is uses retURL parameter to make sure the user is sent back after they choose PageReference ref = new PageReference('/oppitm/choosepricebook.jsp'); ref.getParameters().put('id',theOpp.Id); ref.getParameters().put('retURL','/apex/opportunityProductEntry?id=' + theOpp.Id); return ref; } }Visualforce Page:
<apex:page standardController="Opportunity" extensions="opportunityProductEntryExtension" action="{!priceBookCheck}" > <apex:sectionHeader Title="Manage {!$ObjectType.Product2.LabelPlural}" subtitle="{!opportunity.Name}"/> <apex:messages style="color:red"/> <style> .search{ font-size:14pt; margin-right: 20px; } .fyi{ color:red; font-style:italic; } .label{ margin-right:10px; font-weight:bold; vertical-align: text-bottom; } .pbTitle { white-space: nowrap; } </style> <script type='text/javascript'> // This script assists the search bar functionality // It will execute a search only after the user has stopped typing for more than 1 second // To raise the time between when the user stops typing and the search, edit the following variable: var waitTime = 0.1; var countDown = waitTime+1; var started = false; function resetTimer(){ countDown=waitTime+0.1; if(started==false){ started=true; runCountDown(); } } function runCountDown(){ countDown--; if(countDown<=0){ fetchResults(); started=false; } else{ window.setTimeout(runCountDown,1000); } } </script> <apex:form > <div id="InternalDiv" styleClass="pbTitle" style="width:600px; float:right; margin-top:-60px; font-size:9px;"> </div> <apex:outputPanel id="mainBody"> <div id="pbName" styleClass="pbName" style="margin-top:0px;"> <apex:outputLabel styleClass="label">PriceBook: </apex:outputLabel> <apex:outputText value="{!theBook.Name}"/> <apex:commandLink action="{!changePricebook}" value="change" immediate="true"/> </div> <br/> <!-- not everyone is using multi-currency, so this section may or may not show --> <apex:outputPanel rendered="{!multipleCurrencies}"> <apex:outputLabel styleClass="label">Currency: </apex:outputLabel> <apex:outputText value="{!chosenCurrency}"/> <br/> </apex:outputPanel> <br/> <!-- this is the upper table... a.k.a. the "Shopping Cart"--> <!-- notice we use a lot of $ObjectType merge fields... I did that because if you have changed the labels of fields or objects it will reflect your own lingo --> <apex:pageBlock title="Selected {!$ObjectType.Product2.LabelPlural}" id="selected"> <apex:pageblockTable value="{!shoppingCart}" var="s"> <apex:column > <apex:commandLink value="Remove" action="{!removeFromShoppingCart}" reRender="selected,searchResults" immediate="true"> <!-- this param is how we send an argument to the controller, so it knows which row we clicked 'remove' on --> <apex:param value="{!s.PriceBookEntryId}" assignTo="{!toUnselect}" name="toUnselect"/> </apex:commandLink> </apex:column> <!--********** Product Name **********--> <apex:column headerValue="{!$ObjectType.Product2.LabelPlural}" value="{!s.PriceBookEntry.Product2.Name}"/> <!--********** Product SKU **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.SKU__c.Label}" value="{!s.PriceBookEntry.Product2.SKU__c}"/> <!--********** Annual Product Term **********--> <apex:column headerValue="Annual Term" id="pt" rendered="false" value="{!s.PriceBookEntry.Product2.Product_Term_Years__c}"/> <!--********** Quantity **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Quantity.Label}"> <apex:inputField value="{!s.Quantity}" id="quant" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onkeyup" reRender="tot"/> <apex:actionSupport event="onchange" action="{!calculateSupport}"/> </apex:inputField> <apex:outputText id="quants" value="{0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.Quantity}"/> </apex:outputText> </apex:column> <!--********** Service Term in Months **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Service_Term_in_Months__c.Label}"> <apex:inputField value="{!s.Service_Term_in_Months__c}" id="svc" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onkeyup" reRender="tot"/> </apex:inputField> <apex:outputText id="svcs" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Discount (Manual) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Discount_off_list_manual__c.Label}"> <apex:inputField value="{!s.Discount_off_list_manual__c}" id="disc" style="width:70px" required="True" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onchange" reRender="tot"/> <apex:actionSupport event="onkeyup" action="{!calculateSalesPrice}"/> </apex:inputField> <apex:outputText id="discs" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Sales Price **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.UnitPrice.Label}"> <apex:inputField value="{!s.UnitPrice}" id="sp" style="width:70px" required="true" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', false, true)}"> <apex:actionSupport event="onchange" reRender="tot"/> <apex:actionSupport event="onkeyup" action="{!calculateDiscount}"/> </apex:inputField> <apex:outputText id="sps" value="${0, number, ###,###,###,##0.00}" rendered="{!IF(s.PriceBookEntry.Product2.Type__c == 'Support', true, false)}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Disti Transfer Price (Custom) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Disti_Transfer_Price2__c.Label}"> <apex:outputText id="dpc" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!s.PriceBookEntry.Disti_Transfer_Price__c}"/> </apex:outputText> </apex:column> <!--********** List Price (Custom) **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.List_Price__c.Label}"> <apex:outputText id="lpc" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!s.PriceBookEntry.UnitPrice}"/> </apex:outputText> </apex:column> <!--********** Disti Transfer Price (Standard) Hidden **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.Disti_Transfer_Price__c.Label}" value="{!s.PriceBookEntry.Disti_Transfer_Price__c}" id="dp" rendered="false"/> <!--********** List Price (Standard) Hidden **********--> <apex:column headerValue="{!$ObjectType.OpportunityLineItem.Fields.ListPrice.Label}" id="lp" value="{!s.PriceBookEntry.UnitPrice}" rendered="false"/> <!--********** Total Price (display only field) **********--> <apex:column headerValue="Total Price"> <apex:outputText id="tot" value="${0, number, ###,###,###,##0.00}"> <apex:param value="{!IF(s.PriceBookEntry.Product2.Product_Term_Years__c != null,(s.Quantity * s.Service_Term_in_Months__c * s.UnitPrice)/(s.PriceBookEntry.Product2.Product_Term_Years__c*12),(s.Quantity * s.UnitPrice)) }"/> </apex:outputText> </apex:column> </apex:pageblockTable> <apex:pageBlockButtons > <apex:commandButton action="{!onSave}" value="Save"/> <apex:commandButton action="{!onCancel}" value="Cancel" immediate="true"/> </apex:pageBlockButtons> </apex:pageBlock> <!-- this is the lower table: search bar and search results --> <apex:pageBlock > <apex:outputPanel styleClass="search"> Search for {!$ObjectType.Product2.LabelPlural}: </apex:outputPanel> <apex:actionRegion renderRegionOnly="false" immediate="true"> <apex:actionFunction name="fetchResults" action="{!updateAvailableList}" reRender="searchResults" status="searchStatus"/> <!-- here we invoke the scripting to get out fancy 'no button' search bar to work --> <apex:inputText value="{!searchString}" onkeydown="if(event.keyCode==13){this.blur();}else{resetTimer();}" style="width:300px"/> <i> <!-- actionStatus component makes it easy to let the user know when a search is underway --> <apex:actionStatus id="searchStatus" startText="searching..." stopText=" "/> </i> </apex:actionRegion> <br/> <br/> <apex:outputPanel id="searchResults"> <apex:pageBlockTable value="{!AvailableProducts}" var="a"> <apex:column headerValue="{!$ObjectType.Product2.Fields.Name.Label}" value="{!a.Product2.Name}" /> <apex:column headerValue="{!$ObjectType.Product2.Fields.SKU__c.Label}" value="{!a.Product2.SKU__c}"/> <apex:column headerValue="{!$ObjectType.Product2.Fields.Description.Label}" value="{!a.Product2.Description}"/> <apex:column > <!-- command button in a column... neato --> <apex:commandButton value="Select" action="{!addToShoppingCart}" reRender="selected,searchResults" immediate="true"> <!-- again we use apex:param to be able to tell the controller which row we are working with --> <apex:param value="{!a.Id}" assignTo="{!toSelect}" name="toSelect"/> </apex:commandButton> </apex:column> </apex:pageBlockTable> <!-- We put up a warning if results exceed 100 rows --> <apex:outputPanel styleClass="fyi" rendered="{!overLimit}"> <br/> Your search returned over 100 results, use a more specific search string if you do not see the desired {!$ObjectType.Product2.Label}. <br/> </apex:outputPanel> </apex:outputPanel> </apex:pageBlock> </apex:outputPanel> </apex:form> </apex:page>
- Gabe Rothman 8
- June 04, 2016
- Like
- 0
Banging my head against a wall -- visualforce page pass picklist value into apex:iframe src parameter
Can someone help me sort it out? Thanks!
Controller:
public class domoDashboardController { public List<String> pagename { get { if (pagename == null) { pagename = new List<String>(); list<ApexPage> pages = [SELECT Name FROM ApexPage WHERE Name != 'domoDashBoardViewer' AND Name LIKE 'DOMO_%' ORDER BY Name ASC]; for (ApexPage page : pages){ pagename.add(page.Name); } } return pagename; } set; } }
VF Page:
<apex:page controller="domoDashboardController"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <apex:pageBlock title="DOMO Dashboard Viewer" mode="edit"> <apex:form> <table width="100%" border="0"> <tr> <td style="font-weight:bold; padding-left: 5px; padding-top:10px; width: 125px;"> Select Dashboard: </td> <td style="font-weight:bold; padding-top:8px;"> <select id="pagename"> <option value=""></option> <apex:repeat value="{!pagename}" var="pag"> <option value="{!pag}">{!pag}</option> </apex:repeat> </select> </td> </tr> </table> <apex:pageBlock> <table width="100%" border="0"> <tr> <apex:page id="thePage"> <apex:iframe src="" scrolling="true" id="theIframe"/> </apex:page> </tr> </table> </apex:pageBlock> </Apex:form> </apex:pageBlock> </apex:page>
- Gabe Rothman 8
- March 30, 2016
- Like
- 0
Can anyone help me to bulk-safe this class? Any help would be greatly appreciated...
public with sharing class setAccountGeoFromCountryService { public static void updateGeo(List<Account> Accounts) { List<String> listCountries = new List<String>(); List<String> listStates = new List<String>(); List<String> listZips = new List<String>(); for (Account a : Accounts) { if(a.BillingCountry == null){ return; } if(a.BillingCountry != null){ listCountries.add(a.BillingCountry ); if(a.BillingCountry != 'US'){ listStates.add('NULL'); listZips.add('NULL'); } if(a.BillingCountry == 'US'){ listStates.add(a.BillingState ); if(a.BillingState == 'CA' || a.BillingState == 'California'){ listZips.add(a.BillingPostalCode); } if(a.BillingState != 'CA' && a.BillingState != 'California'){ listZips.add('NULL'); } } } } try{ Territories__c territory = [SELECT Country_Code__c, Region__c, Theater__c, Country__c, State__c, State_Code__c, Postal_Code__c FROM Territories__c WHERE Country_Code__c IN: listCountries AND (State__c =: listStates OR State_Code__c =: listStates) AND Postal_Code__c =: listZips]; system.debug(territory); for (Account a2: Accounts){ if(a2.Owner_is_Federal__c==true){ a2.Geography__c = 'Federal'; a2.Country2__c = territory.Country__c; a2.Region__c = territory.Region__c; } if(a2.Owner_is_Federal__c==false){ a2.Geography__c = territory.Theater__c; a2.Country2__c = territory.Country__c; a2.Region__c = territory.Region__c; } } }catch(QueryException e){} } }
- Gabe Rothman 8
- December 31, 2016
- Like
- 0
Can someone help me bulkify this class?
"rfp: execution of BeforeInsert
caused by: System.QueryException: List has more than 1 row for assignment to SObject
Class.setRfpOwner.setOwners: line 15, column 1
Trigger.rfp: line 4, column 1 "
After looking at my code and the records I'm querying, I the query in line 15 of my service class should only yield one result, which leads me to believe that my service class is somehow not bulk safe. Can anyone point me in the right direction:
Trigger:
trigger rfp on RFP_Response__c (before insert, before update) { if(trigger.isBefore && trigger.isInsert){ setRfpOwner.setOwners(trigger.new); rfpKwBuilder.buildKeyWords(Trigger.new); } if(trigger.isBefore && trigger.isUpdate){ if(checkRecursive.flag == true){ rfpKwBuilder.buildKeyWords(Trigger.new); for(RFP_Response__c rfp : Trigger.new){ RFP_Response__c oldRfp = Trigger.oldMap.get(rfp.Id); if(oldRfp.Response__c != rfp.Response__c){ setResponseToggle.toggleResponse(trigger.new); } } for(RFP_Response__c rfp : Trigger.new){ RFP_Response__c oldRfp = Trigger.oldMap.get(rfp.Id); if(oldRfp.Category__c != rfp.Category__c){ setRfpOwner.setOwners(trigger.new); } } } checkRecursive.flag = false; } }
Class:
public class setRfpOwner { public static void setOwners(list<RFP_Response__c> rfpUpdateList){ List<String> listCategories = new List<String>(); for (RFP_Response__c rfp : rfpUpdateList) { if(rfp.Category__c == null){ return; } if(rfp.Category__c != null){ listCategories.add(rfp.Category__c); } } RFP_Owner_Mappings__c mapping = [SELECT Name, RFP_Category_Owner__c, RFP_Category_Backup_Owner__c,RFP_Category_Owner__r.Email__c,RFP_Category_Backup_Owner__r.Email__c FROM RFP_Owner_Mappings__c WHERE Name IN :listCategories]; for (RFP_Response__c rfp2 : rfpUpdateList){ if(rfp2.Category__c == mapping.Name ){ rfp2.RFP_Category_Owner__c = mapping.RFP_Category_Owner__c; rfp2.RFP_Category_Backup_Owner__c = mapping.RFP_Category_Backup_Owner__c ; rfp2.Category_Owner_Email__c = mapping.RFP_Category_Owner__r.Email__c; rfp2.Category_Backup_Owner_Email__c = mapping.RFP_Category_Backup_Owner__r.Email__c ; } } } }
- Gabe Rothman 8
- December 10, 2015
- Like
- 0
Help: getting "list has no rows for assignment" error, but debug log says otherwise...
Trigger:
trigger Feature on Feature__c (after update) { if(trigger.isAfter && trigger.isUpdate){ for(Feature__c f : trigger.new){ if(f.Status__c == 'Accepted'){ updateFrsFromApprovedFeature.updateFeatureRequests(updateFrsFromApprovedFeature.filterFeature(trigger.oldmap,trigger.new)); } } } }
Helper Class:
public class updateFrsFromApprovedFeature { public static list<Feature__c> filterFeature(map<id, Feature__c> oldMap, list<Feature__c> newList) { list<Feature__c> temp=new list<Feature__c>(); for (Feature__c newFeat : newList) { if (oldMap.get(newFeat.id).Status__c != newFeat.Status__c && newFeat.Status__c == 'Accepted'){ temp.add(newFeat); } } return temp; } public static void updateFeatureRequests(list<Feature__c> frUpdateList) { Feature__c featproxy = [SELECT Id, Status__c FROM Feature__c WHERE Id in: frUpdateList]; system.debug(featproxy); List<Feature_Request__c> frs = [SELECT Feature__c, Proposed_Feature__c FROM Feature_Request__c WHERE Proposed_Feature__c =: featproxy.id]; system.debug(frs); List<Feature_Request__c> updateFrs = new list<Feature_Request__c>(); for(Feature_Request__c updatefr : frs){ updatefr.Feature__c = featproxy.Id; updatefr.Proposed_Feature__c = null; updateFrs.add(updatefr); } system.debug(updateFrs); If(updateFrs.size()>0){ update updateFrs; } } }
Debug Statements:
09:10:30:109 USER_DEBUG [17]|DEBUG|Feature__c:{Id=a11i000000CcIJYAA3} 09:10:30:114 USER_DEBUG [21]|DEBUG|(Feature_Request__c:{Feature__c=a11i000000CdxuPAAR, Proposed_Feature__c=a11i000000CcIJYAA3, Id=a10e0000002L7K4AAK}, Feature_Request__c:{Feature__c=a11i000000CdxuPAAR, Proposed_Feature__c=a11i000000CcIJYAA3, Id=a10e0000002L7K9AAK}) 09:10:30:115 USER_DEBUG [28]|DEBUG|(Feature_Request__c:{Feature__c=a11i000000CcIJYAA3, Proposed_Feature__c=null, Id=a10e0000002L7K4AAK}, Feature_Request__c:{Feature__c=a11i000000CcIJYAA3, Proposed_Feature__c=null, Id=a10e0000002L7K9AAK})
Link to Complete Debug Log: https://docs.google.com/document/d/1S7n2LWqWfG7NU97sJ9E1ZAooIwDdiHiTpgEyWShzHXg/edit?usp=sharing
- Gabe Rothman 8
- August 12, 2015
- Like
- 0
Need help mapping productId to pricebookentryid
I'm working a bit of code to automatically re-add products to an opportunity if they get deleted as a result of the user changing the pricebook. I experimented with trying to clone the OLIs in a beforeDelete trigger, but couldn't get that work, I assume becuase updating the pricebook deleted the newly created clones in addition to the old products.
Instead I've built a custom object called OLI_Clone__c for which records are inserted every time a new OLI is added to an oppty and then deleted later when the oppty goes to closed won or closed lost. Basically, when the pricebook updates on the oppty, the code queries back to the OLI_Clone__c records associated with the oppty and then uses the data stored there to recreate the OLIs. The biggest challenge is that I can't create a lookup to ProcebookEntry on a custom object so I had to create a lookup to Product and then use a combination of the Opportunity's new Pricebook Id and the OLI_Clone__c's product Id to query for the correct PricebookEntry.
I've been able to get a list of PricebookEntryIds, but I can't figure out how get the correct PricebookEntryId for each OLI_Clone__c without querying for it inside of my for loop, which is obviously a big no no. I do know that I should be using a map, but I am relatively novice coder, and I just can't seem to get the syntax right.
Thanks in advance for your help!
I've pasted my service class below with a few comments:
public with sharing class autoAddProductsAfterPBChange { //create a filter to listen for a change in the pricebook below public static list<opportunity> filterOpp(map<id, opportunity> oldMap, list<opportunity> newList) { list<opportunity> temp=new list<opportunity>(); for (opportunity newOpp : newList){ if (oldMap.get(newOpp.id).PriceBook2Id != newOpp.PriceBook2Id){ temp.add(newOpp); } } return temp; } //find the pricebook entries and recreate the OLIs public static void autoAdd(list<Opportunity> opps){ Opportunity opp = new Opportunity(); Pricebook2 pb = new Pricebook2(); for(Opportunity opp2 : opps){ opp.id = opp2.Id; pb.id = opp2.Pricebook2Id; } list<OLI_Clone__c> olics = [SELECT Quantity__c, Additional_Discount_off_List__c, Manual_Discount__c, Opportunity__c, Product__c, Sales_Price__c, Service_Term_in_Months__c FROM OLI_Clone__c WHERE Opportunity__c =: opp.id]; list<Id> prodids = new list<Id>(); for(OLI_Clone__c olicprod : olics){ prodids.add(olicprod.Product__c); } List<PricebookEntry>pbeids = [SELECT Id, Product2Id FROM PricebookEntry WHERE Product2Id in: prodids AND Pricebook2Id =: pb.id]; Map<Id, PricebookEntry> pbeMap = new Map<Id, PricebookEntry>(pbeids); // Is this map correct? list<OpportunityLineItem>toInsert = new list<OpportunityLineItem>(); for(OLI_Clone__c olic : olics){ if(pbeMap.containsKey(olic.Product__c)){ OpportunityLineItem oli = new OpportunityLineItem(); PricebookEntry pbe = pbeMap.get(); // I know I need to be "getting" something here but I'm not sure how to structure the syntax, and I'm not sure that my map above is configured correctly either. Basically I need to get the correct PricebookEntryId based on the Product__c field on the OLI_Clone__c in this loop and the parent Opportunity's pricebook. oli.Quantity = olic.Quantity__c; oli.UnitPrice = olic.Sales_Price__c; toInsert.add(oli); } } insert toInsert; } }
- Gabe Rothman 8
- July 21, 2015
- Like
- 0
Rerender inputField using VisualForce actionSupport
I have a requirement to default the Company field on Lead creation page to First Name +" "+ Last Name as soon as user enters the Last Name. I am trying to use VisualForce actionSupport for the same. But rerender does not seem to work for me. I am attaching my code below. Any help would be much appreciated.
Visualforce Page:-
<apex:page standardController="Lead" extensions="leadControllerExtension">
<apex:sectionHeader title="{!$ObjectType.Lead.label} Edit" subtitle="{!IF(ISNULL(Lead.name), 'New Lead', Lead.name)}"/>
<apex:form >
<apex:pageBlock mode="edit" title="{!$ObjectType.Lead.label} Edit">
<apex:pageblockbuttons >
<apex:commandbutton value="Save" action="{!Save}"/>
<apex:commandbutton value="Cancel" action="{!Cancel}"/>
</apex:pageblockbuttons>
<apex:pageblocksection id="LeadInformationPBS" title="Lead Information">
<!-- Make Owner field editable -->
<apex:inputfield value="{!Lead.OwnerId}"></apex:inputfield>
<apex:inputfield value="{!Lead.Phone}"></apex:inputfield>
<!-- Since we need to group two input fields together we need a pageBlockSectionItem with an Output panel. We also needed to create a label so we know what field we are entering in -->
<apex:pageblocksectionitem >
<apex:outputlabel value="{!$ObjectType.Lead.Fields.FirstName.label}"></apex:outputlabel>
<apex:outputpanel >
<apex:inputfield value="{!Lead.Salutation}"></apex:inputfield>
<apex:inputfield value="{!Lead.FirstName}"></apex:inputfield>
</apex:outputpanel>
</apex:pageblocksectionitem>
<apex:inputfield value="{!Lead.MobilePhone}"></apex:inputfield>
<apex:inputfield value="{!Lead.LastName}">
<apex:actionSupport event="onchange" reRender="CompanyDef" action="{!getCompany}"/>
</apex:inputfield>
<apex:inputfield value="{!Lead.Fax}"></apex:inputfield>
<apex:inputField value="{!Lead.Company}" id="CompanyDef">
</apex:inputField>
<apex:inputfield value="{!Lead.Email}" required="true"></apex:inputfield>
<apex:inputfield value="{!Lead.Title}"></apex:inputfield>
<apex:inputfield value="{!Lead.Website}"></apex:inputfield>
<apex:inputfield value="{!Lead.Leadsource}"></apex:inputfield>
<apex:inputfield value="{!Lead.Status}"></apex:inputfield>
<!-- <apex:inputField value="{!Lead.Campaign}" />
Campaign field is not able to be used unless you write your own custom class/method to create a campaign member
Post explaining this issue: http://boards.developerforce.com/t5/Apex-Code-Development/Cannot-Access-to-Campaign-Field-from-Lead-Object/td-p/161715
-->
<apex:inputfield value="{!Lead.Rating}"></apex:inputfield>
<apex:inputfield value="{!Lead.Industry}"></apex:inputfield>
<apex:inputfield value="{!Lead.NumberOfEmployees}"></apex:inputfield>
</apex:pageblocksection>
</apex:pageBlock>
</apex:form>
</apex:page>
Controller:-
public class leadControllerExtension {
transient public Lead lead {get; set;}
transient public String Company {get; set;}
// The extension constructor initializes the private member
// variable acct by using the getRecord method from the standard
// controller.
public leadControllerExtension(ApexPages.StandardController stdController) {
this.lead = (Lead)stdController.getRecord();
}
public String getCompany() {
Company = lead.FirstName + ' ' + lead.LastName;
Company='Hello Wprld';
return (Company);
}
}
- Vandana Rattan
- July 03, 2014
- Like
- 0