+ Start a Discussion
Steve CairneySteve Cairney 

Is it possible to create records within a VF page

Hi, I'm creating something that makes use of four custom objects.

The objects are related to the Opp object and are as follows
-Booking object
-Product (not SF native product, it's just names the same)
-Design
-Delivery

What I'm discovering is that to add a booking to the Opp with products, designs and deliveries is extremely unweildy for the user and requeries a great deal of clicks and I want to design the process to be as easy as possible.

Is it possible to create a visual force page all the fields neccessary for each object are visible? I fear this may require trigger writing, but if that's the case I'll post the question elswhere but I thought I'd ask here first.
 
Best Answer chosen by Steve Cairney
Krishna SambarajuKrishna Sambaraju
Here is a sample controller and visualforce page based on the details you provided. Change the object names and field names accordingly.
public class CreateBookingController{
    public Booking__c booking {get; set;}
    public Product__c product {get; set;}
    public Design__c design {get; set;}
	public Delivery__c delivery {get; set;}
    private opportunityId;
    public CreateBookingController()
    {
		opportunityId = ApexPages.currentPage().getParameters().get('oppId');
        booking = new Booking__c();
        product = new Product__c();
        design = new Design__c();
		delivery = new Delivery__c();
    }
    
    public PageReference Save()
    {
		booking.Opportunity__c = opportunityId;
        insert booking;
        product.BookingReference__c = booking.Id;
        insert product;
        design.ProductReference__c  = product.Id;
        insert design;
		delivery.ProductReference__c  = product.Id;
        insert delivery;
		
        return new PageReference ('/' + opportunityId);
    }
}
 
<apex:page controller="CreateBookingController">
    <apex:form>
        <apex:pageBlock title="Booking Wizard">
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Save" action="{!Save}"/>    
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1" title="Booking Details">
                <apex:inputField value="{!booking.Name}" label="Booking Title: "/>
				<apex:inputField value="{!booking.InChargeDate__c}" label="In-Charge Date: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Product Details">
                <apex:inputField value="{!product.ProductReference__c}" label="Product Reference: "/>
                <apex:inputField value="{!product.ProductCode__c}" label="Product Code: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Design Details">
                <apex:inputField value="{!design.Name}" label="Design Name: "/>
            </apex:pageBlockSection>
			<apex:pageBlockSection columns="1" title="Delivery Details">
                <apex:inputField value="{!delivery.Quantity__c}" label="Quantity: "/>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Add or change the fields in the above visualforce page according to your need. Don't use the Auto-Number fields, as they are automatically generated. 

Then create a custom button on the opportunity object. If you want the button to work on a particular stage, then select the behaviour as "Execute Javascript" and in the text area use the following code.
{!REQUIRESCRIPT("/soap/ajax/15.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/15.0/apex.js")} 

var oppStage = "{!Opportunity.StageName}";

if (oppStage == "StageName_To_Check")
{
     window.location.href = "/apex/CreateBookingPage?oppId={!Opportunity.Id}";
}
else
{
    alert("This function is only available for stage <StageName_To_Check>");
}
Change the stage name to check accordingly.

Hope this helps.

All Answers

Krishna SambarajuKrishna Sambaraju
Hi Steve,

Yes, it is possible to create records from visualforce page. You can use the standard conroller or create an extend the standard controller with a custom class to fit the purpose. But your use case scenario is not clear. Can you provide more details on your custom objects and their relationships and the relationship with the opportunity object.

Provide more details, so I can help.

Regards,
Krishna.
Steve CairneySteve Cairney
Thanks Krishna. I'm afraid this might sound very complex, but it's a requirement! I'm not going to include fields unless you tell me otherwise. The custom objects and fields have been created. As I said there are 4 custom objects (Booking, Product,Design and Delivery)

Ok, so when an opportunity reaches a certain criteria the user must do the following

Create a new Booking. Each Booking as a specific date (In-charge date)
Within the Booking there can be multiple Products
Each Product can have multiple Designs
Each Design can have multple Deliveries and each delivery has a quantity.

Note, Opps can have multiple Bookings.

Currently the Opp looks like this (with related lists), but it was extremely time consuming to fill this out.

User-added image
Krishna SambarajuKrishna Sambaraju
Hi Steve,

If I understand correctly, you want the visualforce page to display the fields related to each of those objects and the user fills all those fields at once and create the records for each of these objects in one click. Is that right?

Regards,
Krishna.
Steve CairneySteve Cairney
In the simplest terms that's right. I'm sure it's more complex than that but essentially.
Krishna SambarajuKrishna Sambaraju
Here is a sample controller and visualforce page based on the details you provided. Change the object names and field names accordingly.
public class CreateBookingController{
    public Booking__c booking {get; set;}
    public Product__c product {get; set;}
    public Design__c design {get; set;}
	public Delivery__c delivery {get; set;}
    private opportunityId;
    public CreateBookingController()
    {
		opportunityId = ApexPages.currentPage().getParameters().get('oppId');
        booking = new Booking__c();
        product = new Product__c();
        design = new Design__c();
		delivery = new Delivery__c();
    }
    
    public PageReference Save()
    {
		booking.Opportunity__c = opportunityId;
        insert booking;
        product.BookingReference__c = booking.Id;
        insert product;
        design.ProductReference__c  = product.Id;
        insert design;
		delivery.ProductReference__c  = product.Id;
        insert delivery;
		
        return new PageReference ('/' + opportunityId);
    }
}
 
<apex:page controller="CreateBookingController">
    <apex:form>
        <apex:pageBlock title="Booking Wizard">
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Save" action="{!Save}"/>    
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1" title="Booking Details">
                <apex:inputField value="{!booking.Name}" label="Booking Title: "/>
				<apex:inputField value="{!booking.InChargeDate__c}" label="In-Charge Date: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Product Details">
                <apex:inputField value="{!product.ProductReference__c}" label="Product Reference: "/>
                <apex:inputField value="{!product.ProductCode__c}" label="Product Code: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Design Details">
                <apex:inputField value="{!design.Name}" label="Design Name: "/>
            </apex:pageBlockSection>
			<apex:pageBlockSection columns="1" title="Delivery Details">
                <apex:inputField value="{!delivery.Quantity__c}" label="Quantity: "/>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Add or change the fields in the above visualforce page according to your need. Don't use the Auto-Number fields, as they are automatically generated. 

Then create a custom button on the opportunity object. If you want the button to work on a particular stage, then select the behaviour as "Execute Javascript" and in the text area use the following code.
{!REQUIRESCRIPT("/soap/ajax/15.0/connection.js")} 
{!REQUIRESCRIPT("/soap/ajax/15.0/apex.js")} 

var oppStage = "{!Opportunity.StageName}";

if (oppStage == "StageName_To_Check")
{
     window.location.href = "/apex/CreateBookingPage?oppId={!Opportunity.Id}";
}
else
{
    alert("This function is only available for stage <StageName_To_Check>");
}
Change the stage name to check accordingly.

Hope this helps.
This was selected as the best answer
Steve CairneySteve Cairney
Some clarifcations please before I start
 
public Booking__c booking {get; set;}

I'm interested to know what should follow the API name? In your example it's "booking" but is this the Object Name?

So using real terms it would be 
public ItsApproved_Booking__c ItsApproved_Booking {get; set;}



 
Steve CairneySteve Cairney
Also, there seems to be a syntax error due to ';' on this line
 
private OpportunityId;

 
Krishna SambarajuKrishna Sambaraju
You can use any name after api name. For example your API name is ItsApproved_Booking__c you can declare the variable as follows.
public ItsApproved_Booking__c booking {get; set;}

For the opportunityId, there is a small correction. it should be private string opportunityId;

Hope this helps.
 
Steve CairneySteve Cairney
Hi, can you explain the following please in the class?
booking.Opportunity__c = opportunityId;
This makes in the first instance as I assume that will be the lookup field value for the Opportunity when the booking is created. So in my class it looks like this
 
ItsApprovedBooking.Opportunity__c = opportunityId;
        insert ItsApprovedBooking;

However, the next lines aren't to clear. Should they be the corresponding lookup fields for the object? Example
 
product.BookingReference__c = booking.Id;

Is this looking for the lookup field from the first object (the ItsApprovedBooking) so it can relate that to the Product?

Sorry I'm asking so many questions but I'm unfortunately not technically a developer (but tht's life!)
Krishna SambarajuKrishna Sambaraju
Yes. You need to use the Lookup fields for those. As I don't know the field names, I used the names based on the labels in your screenshot, 
Steve CairneySteve Cairney
Thanks, I'm slowly working it out for the above code it would be:
 
ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id; //insert the reference field for the parent and declare variable id
        insert ItsApprovedProduct;

 
Krishna SambarajuKrishna Sambaraju
Yes, if you declared the variable as "public ItsApproved_Booking__c ItsApprovedBooking {get; set;}"
Steve CairneySteve Cairney
We're definately getting somewhere. The class saved with no errors and I've added the correct field names (you were very close in your guesses!)

What I'm seeing looks like an excelent start but for some reason I can't actually input text.

Booking_Title on the ItsApprovedBooking object is a text field yet it's not possible to input text. Will the class need updating with the fields?

User-added image
Steve CairneySteve Cairney
Apologies, in the VF code the value was set for Name (which is auto filled)
Krishna SambarajuKrishna Sambaraju
Did you add the field in the visualforce page as below.

<apex:inputField value="{!ItsApprovedBooking.Booking_Title__c}"/>
Steve CairneySteve Cairney
OK, this is getting there thanks Krishna. I have created the class, the VF and added the button to the Opps page and I can cofirm it opens up the page (when the Opp is at the correct stage)

However, I'm hitting an error on Save.

First, here's the codes

Class
public class CreateIABookingController{
    public ItsApproved_Booking__c ItsApprovedBooking {get; set;} //Name the objects as a variables
    public ItsApproved_Product__c ItsApprovedProduct {get; set;}
    public ItsApproved_Design__c ItsApprovedDesign {get; set;}
    public ItsApproved_Delivery__c ItsApprovedDelivery {get; set;}
    private string OpportunityId;
    public CreateIABookingController()
    {
        opportunityId = ApexPages.currentPage().getParameters().get('oppId');
        ItsApprovedBooking = new ItsApproved_Booking__c();
        ItsApprovedProduct = new ItsApproved_Product__c();
        ItsApprovedDesign = new ItsApproved_Design__c();
        ItsApprovedDelivery = new ItsApproved_Delivery__c();
    }
    
    public PageReference Save()
    {
        ItsApprovedBooking.Opportunity__c = opportunityId;
        insert ItsApprovedBooking;
        ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id; //insert the reference field for the parent and declare variable id
        insert ItsApprovedProduct;
        ItsApprovedDesign.Product_Reference__c  = ItsApprovedProduct.Id;
        insert ItsApprovedDesign;
        ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
        insert ItsApprovedDelivery;
        
        return new PageReference ('/' + opportunityId);
    }
}

The VF page
 
<apex:page controller="CreateIABookingController">
    <apex:form >
        <apex:pageBlock title="Booking Wizard">
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Save" action="{!Save}"/>    
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1" title="ItsApproved Booking Details">
                <apex:inputField value="{!ItsApprovedBooking.Booking_Title__c}" label="Booking Title: "/>
                <apex:inputField value="{!ItsApprovedBooking.Customer_Reference__c}" label="Customer Reference: "/>
                <apex:inputField value="{!ItsApprovedBooking.Incharge_Date__c}" label="In-Charge Date: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Product Details">
                <apex:inputField value="{!ItsApprovedProduct.Product_Code__c}" label="Product Code: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Design Details">
                <apex:inputField value="{!ItsApprovedDesign.Artwork_Title__c}" label="Artwork Title: "/>
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" title="Delivery Details">
                <apex:inputField value="{!ItsApprovedDelivery.Quantity__c}" label="Quantity: "/>
                <apex:inputField value="{!ItsApprovedDelivery.Depot_Code__c}" label="Depot Code: "/>

                
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
However, as I said on Saving SF displays the following error which seems to point to insert ItsApprovedDelivery in the class...
 
System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Product]: [Product]
Error is in expression '{!Save}' in component <apex:commandButton> in page createiabookingpage: Class.CreateIABookingController.Save: line 25, column 1

Class.CreateIABookingController.Save: line 25, column 1

The only required fields I can think of for product would be the lookup for the ItsApprovedBooking.Name however, how can we add those on creation?
 
Krishna SambarajuKrishna Sambaraju
It is throwing error on line 25, when inserting the "Delivery" record. On the Delivery object there seem to be a lookup for "Product", which need to be populated.
So before "insert ItsApprovedDelivery;" statement add the following.
ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;

I hope the above field name is correct.
Steve CairneySteve Cairney
Amazing! Thanks for all your hard work. I've learnt a great deal. I can confirm that the VF page created the Booking, and all related objects

There are a couple of additional things I'd like to be able to do.

1. As I stated before, Although there is only 1 ItsApprovedBooking, that booking can have multiple products, multiple designs and multiple deliveries. Would there be a way to add addition products, designs and deliveries in the page?

2. Instead of using the related lists in the Opps, a visual force aspect would be much better. It could list the various
Steve CairneySteve Cairney
So perhaps on the VF page, there could be a button called "Add Additional Products" for example? This would enable the core data filled to be saved, but then another product could be added to the booking?
Steve CairneySteve Cairney
OK, so after a bit of research I'm going to see if I can the following VF pages

addProduct
addDesign
addDelivery

perhaps then I will be able to add an extension class to the main booking page and use a custom save action so if I needed to add more, it would redirect to the relevant addPage.

Does that sound like I'm onto the right track?
Krishna SambarajuKrishna Sambaraju
This is the simplest way to insert all the objects at one go. If you want to add multiple records at the same time, it is going to be a bit complex and needs a re-dsign of the visualforce page and the controller. You need to have a separate "Add" button for each of these objects when you click the button, call a method to add the object to a list and insert the list at once. It will not be easy to track which product belongs which booking. Then you need to save booking first and use lookup in the product section to select the booking before adding the product.

The easiest way is to use the buttons on the standard layout, if you want to add a new product to an existing booking and so on and use this page if you want to create a new booking and corresponding product. Here is a sample code for adding multiple products.
//Declare a variable for the list
List<Product__c> products;

//Instantiate the list in the constructor
public CreateBookingController()
{
   //add the following line in the constructor.
    products = new List<Product__c>();
}

//Add the method
public void AddProduct()
{
   //If Lookup for booking reference field on product section in visualforce page is added.
   products.add(product);
   product = new Product__c();
}
//change the existing save method.
public PageReference Save()
{
   //change insert product to insert products.
   insert products;
}
In the visualforce page in the product section add the lookup for the Booking Reference. Here you can only pick the bookings already created, not the current one which you filled in the booking section, because this booking is not yet created. If you want the current booking to be available in the lookup, you need to save it first, by adding a new method in the controller to save the booking.
 
<apex:pageBlockSection columns="1" title="Product Details">
                <apex:inputField value="{!product.Booking_Reference__c}" label="Booking Reference: "/>
                <apex:inputField value="{!product.ProductCode__c}" label="Product Code: "/>
                <apex:commandButton value="Add Product" action="{!AddProduct}"/>
            </apex:pageBlockSection>
Hope you have an understanding of how it works. 
Steve CairneySteve Cairney
Hi Krishna, are you saying that I should experiment by adding to the current class and VF page? (I'm nout sure if you a refering to my reply regarding addPages and extension classes
Krishna SambarajuKrishna Sambaraju
Yeah, I was saying about extending the current class and VF page. If you want to build new pages, it is entirely upto you. Instead of building separate pages, you can use the standard functionality of salesforce. To add a new booking click the "New Booking" button in the booking related list. If you want to add the product to a booking, click the booking on the opportunity to goto the booking details layout and then click the "New Product" in the product related list of the booking and so on.
Steve CairneySteve Cairney
Ah great. To be honest what you've done here is already a huge improvement on the 'traditional' method. I will spend the day tomorrow coming up with angles and will post back with a success story (hopefully!) I found this thread which, if I decide to create new pages should prove useful as it's a very similar scenario
https://developer.salesforce.com/forums/?id=906F0000000BQ0VIAW
Steve CairneySteve Cairney
Hi Krishna, I'm in the process of investigating the possibility of adding new records but I was wondering something: It would be useful to have a "Save and New" button attached to what we have here so the user can add an additional booking immediately. 

I'm looking for code online and I'm afraid it's confusing. I can't work out if I need to add a new class and adding to the currently class only fails...
Krishna SambarajuKrishna Sambaraju
Add a new button "Save & New" on the visualforce page and add a method like below in the class.
public PageReference SaveAndNew()
{
	ItsApprovedBooking.Opportunity__c = opportunityId;
	insert ItsApprovedBooking;
	ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id; //insert the reference field for the parent and declare variable id
	insert ItsApprovedProduct;
	ItsApprovedDesign.Product_Reference__c  = ItsApprovedProduct.Id;
	insert ItsApprovedDesign;
	ItsApprovedDelivery.Product_Reference__c  = ItsApprovedProduct.Id;
	insert ItsApprovedDelivery;
	
	ItsApprovedBooking = new ItsApproved_Booking__c();
	ItsApprovedProduct = new ItsApproved_Product__c();
	ItsApprovedDesign = new ItsApproved_Design__c();
	ItsApprovedDelivery = new ItsApproved_Delivery__c();
	return null;
}
Any issues let me know.
 
Steve CairneySteve Cairney
Hi Krishna, I thought I'd let you know that this has grown and is becoming more advanced. Just thought you'd like to know
https://developer.salesforce.com/forums/ForumsMain?id=906F0000000MLpmIAG