+ Start a Discussion
Steve CairneySteve Cairney 

add row custom object visualforce

Hi I have to achive something that I'm struggling with at the moment (I've posted a few threads recently sorry)

I have a requirement as follows. I have a custom controller as follows
 
public with sharing 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
        ItsApprovedProduct.Opp__c = opportunityId; //relate the Opp to the Product
        insert ItsApprovedProduct;            
        ItsApprovedDesign.Opportunity__c = opportunityId; //relate the Opp to the Design
        ItsApprovedDesign.Product_Reference__c  = ItsApprovedProduct.Id;
        ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
        insert ItsApprovedDesign;
        ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
        ItsApprovedDelivery.Opportunity__c = opportunityId; //relate the Opp to the Delivery
        insert ItsApprovedDelivery;
        
        return new PageReference ('/' + opportunityId);
    }
        
}
This allows the user to add a new Record (the Booking) with one related Product, Design and Delivery object on one page. This page is accessed via a button on the Opp record so that the Booking object is related to that OppID

Heres the VF page code
 
<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="3" 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>

What I need to do is achieve the following

1. Add row functionaility
I would like the user to be able to add additional Designs and Delivery rows related to the Product (Products can have multiple designs and multiple deliveries

2. I'd also like the user to be able to add a full row of related objects, IE. Another row with a new Product selected, along with Design and Deliveries (but that row to also have the option of adding additional Designs and Deliveries.

I've experimented with PageBlockTable, but I can't work out the List since until the VF save is pressed, nothing exisits.

I've got loads of code examples from around the web but the classes are so different to mine that I don't know where to start. I'm floundering and could really use some help.
 
Best Answer chosen by Steve Cairney
Krishna SambarajuKrishna Sambaraju
You just need to add set the product id on the delivery just before or after you are setting the design id on the delivery (line 110 in the above code).
Just add the following line.
delivery.Product__c = product.Id;

That should set the product id on the delivery.

All Answers

Steve CairneySteve Cairney
UPDATE:

I've made some progress with this by slowly trying out different methods I've found around the web. Currently I have edited the VF page and the Controller class as follows. This almost gives the result I need. However, when pressing save, only the last row on the page is saved against the relevant records. I believe I will have to edit the controller, but I'm not sure what to add in there.

I'm guessing the new !row value needs to taken into consideration around the public PageReference Save mark?

I have also successfully created a test class with 94% coverage.

VF Page
 
<apex:page controller="CreateIABookingController" docType="html-5.0">
    <apex:form id="theForm" >
        <apex:pageBlock id="thePB" title="Its Approved Booking Wizard">
            <apex:pageBlockButtons location="bottom">
                <apex:commandButton value="Save" action="{!Save}"/>
                </apex:pageBlockButtons>
            <apex:outputPanel id="bookingHead">
            <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="6" title="Add Products, Designs and Deliveries" id="thePbs" collapsible="false">
                <apex:pageBlockTable value="{!rows}" var="rid" id="tablet">
                    <apex:column headerValue="ID">
                        <apex:input type="auto" value="{!rows[rid].rowId}" />
                    </apex:column>
                    <apex:column headerValue="Product Code">
                        <apex:inputField value="{!ItsApprovedProduct.Product_Code__c}" />
                    </apex:column>
                    <apex:column headerValue="Artwork Title">
                        <apex:inputField value="{!ItsApprovedDesign.Artwork_Title__c}" label="Artwork Title: " />
                    </apex:column>
                    <apex:column headerValue="Quantity">
                        <apex:inputField value="{!ItsApprovedDelivery.Quantity__c}" label="Quantity: " />
                    </apex:column>
                    <apex:column headerValue="Delivery Depot Code">
                        <apex:inputField value="{!ItsApprovedDelivery.Depot_Code__c}" label="Depot Code: " />
                    </apex:column>
                    <apex:column headerValue="Action">
                        <apex:commandButton action="{!removeRow}" value="x" rerender="tablet">
                            <apex:param value="{!rows[rid].rowId}" name="rowIdToRemove" />
                        </apex:commandButton>
                    </apex:column>
                </apex:pageBlockTable>
                <apex:pageBlockSectionItem >
                    <apex:commandButton value="Add Row" action="{!addRow}" rerender="tablet"/>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
            </apex:outputPanel>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Controller 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 Map<Integer, Row> rows { get; set; }
    public CreateIABookingController(){
        rows = new Map<Integer, Row>();
    }
    public void addRow() {
        Integer rowId = rows.size();
        rows.put(rowId , new Row(rowId));
    }
    public void removeRow() {
        rows.remove(Integer.valueOf(ApexPages.currentPage().getParameters().get('rowIdToRemove')));
    }
    public class Row {
        public Integer rowId { get; set; }
        public Row(Integer rowId) {
            this.rowId = rowId;
        }
    }
    {
        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
        ItsApprovedProduct.Opp__c = opportunityId; //relate the Opp to the Product
        insert ItsApprovedProduct;            
        ItsApprovedDesign.Opportunity__c = opportunityId; //relate the Opp to the Design
        ItsApprovedDesign.Product_Reference__c  = ItsApprovedProduct.Id;
        ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
        insert ItsApprovedDesign;
        ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
        ItsApprovedDelivery.Opportunity__c = opportunityId; //relate the Opp to the Delivery
        insert ItsApprovedDelivery;
        
        return new PageReference ('/' + opportunityId);
    }
        
}

Lastly here's how the page looks when rows have been added. It's a bit clunkly and it opens without any rows at all (you have to click Add Row) but it's better than before.

I'm wondering if I need to take into consideration the row ID here?

User-added image
Krishna SambarajuKrishna Sambaraju
I think you need to declare the following variables

public ItsApproved_Product__c ItsApprovedProduct {get; set;}
public ItsApproved_Design__c ItsApprovedDesign {get; set;}
public ItsApproved_Delivery__c ItsApprovedDelivery {get; set;}

in your inner class "Row". Your inner class "Row" should look something like this.
public class Row {
     public Integer rowId { get; set; }
     public ItsApproved_Product__c ItsApprovedProduct {get; set;} 
     public ItsApproved_Design__c ItsApprovedDesign {get; set;} 
     public ItsApproved_Delivery__c ItsApprovedDelivery {get; set;}
     public Row(Integer rowId, ItsApproved_Product__c product, ItsApproved_Design__c design, ItsApproved_Delivery__c delivery) {
           this.rowId = rowId;
           ItsApprovedProduct = product;
           ItsApprovedDesign = design;
           ItsApprovedDelivery = delivlery;
      }
}
//add row method can be changed as follows.
public void addRow() {
    Integer rowId = rows.size();
    rows.put(rowId , new Row(rowId, new ItsApproved_Product__c(), new ItsApproved_Design__c(), new ItsApproved_Delivery__c()));
}
Then you can access them through your Map "rows" you declared.
Also create a list for each of your objects in your outer class.
List<ItsApproved_Product__c> productList = new List<ItsApproved_Product__c>();

//your save method should use the "rows" map to create the list of different objects. I have added the productList as an example. Insert the list //outside the for loop as shown 
for (Row r : rows.values())
{
    ItsApproved_Product__c product = new ItsApproved_Product__c();
    product.Opp__c = opportunityId;
    product.Booking_Reference__c = ItsApprovedBooking.Id;
    product.Product_Code__c = r.ItsApprovedProduct.Product_Code__c;
    //add this product to the list
    productList.add(product);
   // and so on
}
if (productList.size() > 0) // do this for each of your objects.
{
   insert productList;
}

I have not tested any of the code I have given above. Hope it works. Let me know how it goes.
Krishna SambarajuKrishna Sambaraju
A small correction in the for loop. It can be much simple as below.

for (Row r : rows.values())
{
    r.ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id;
    //add this product to the list
    productList.add(r.ItsApprovedProduct);
   // and so on
}

And in your visualforce page you have to do the following change.

<apex:pageBlockTable value="{!rows}" var="rid" id="tablet">
    <apex:column headerValue="ID">
        <apex:input type="auto" value="{!rows[rid].rowId}" />
    </apex:column>
    <apex:column headerValue="Product Code">
        <apex:inputField value="{!rows[rid].ItsApprovedProduct.Product_Code__c}" />
    </apex:column>
<!-- Do the same change to other fields in the page block table.

Hope this works as you expect.
Steve CairneySteve Cairney
Hi Krishna, thanks for picking this back up.

I worked out that in the 'save' loop I had to add insert xxxList in there. I'm getting a strange error when hitting save...

SF is telling me 

System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Product Reference]: [Product Reference]
Error is in expression '{!Save}' in component <apex:commandButton> in page createiabookingpage: Class.CreateIABookingController.Save: line 57, column 1
Class.CreateIABookingController.Save: line 57, column 1

However, I can see the field in the code (HERE IT IS comment)

What could be the solution here?
 
public PageReference Save()
    {
        ItsApprovedBooking.Opportunity__c = opportunityId;
        insert ItsApprovedBooking;
        	for (Row r : rows.values())
            {
                r.ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id;
                r.ItsApprovedProduct.Opp__c = opportunityId;
                productList.add(r.ItsApprovedProduct);
                insert productList;
                r.ItsApprovedDesign.Opportunity__c = opportunityId;
                r.ItsApprovedDesign.Product_Reference__c = ItsApprovedProduct.Id; //HERE IT IS
                r.ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
                designList.add(r.ItsApprovedDesign);
                insert designList;
                r.ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
        		r.ItsApprovedDelivery.Opportunity__c = opportunityId;
       			deliveryList.add(r.ItsApprovedDelivery);
                insert deliveryList;
             }   
                return new PageReference ('/' + opportunityId);

 
Krishna SambarajuKrishna Sambaraju
Hi Steve,

Your requirement is a bit complex. You are referencing the ProductId (ItsApprovedProduct.Id) in the ItsApprovedDesign and so on, it is difficult to set the Product_Reference__c on the ItsApprovedDesign and ItsApproved_Design__c on ItsApprovedDelivery etc. You can only refer the id once the record is created. Remove the insert statements from the for loop as below.
for (Row r : rows.values())
{
	r.ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id;
	r.ItsApprovedProduct.Opp__c = opportunityId;
	productList.add(r.ItsApprovedProduct);
	
	r.ItsApprovedDesign.Opportunity__c = opportunityId;
	//r.ItsApprovedDesign.Product_Reference__c = ItsApprovedProduct.Id; //HERE IT IS
	designList.add(r.ItsApprovedDesign);
	
	//r.ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
	r.ItsApprovedDelivery.Opportunity__c = opportunityId;
	deliveryList.add(r.ItsApprovedDelivery)

	//r.ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
	deliveryList.add(r.ItsApprovedDelivery);
 }

You need to create some maps to identify which product belongs which design and which design belongs to which delivery. How can you identify this from the inputs of the visualforce page.

For each of the products selected in the Visualforce page, if you want to create a design and delivery record then you can do as below.
if (productList.size() > 0)
{
	insert productList;
}
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		design.Product_Reference__c = product.Id;
	}
	for (ItsApproved_Delivery__c delivery : deliveryList)
	{
		delivery.Product__c = product.Id;
	}
}
if(designList.size() > 0)
{
	insert designList;
}
for (ItsApproved_Design__c design : designList)
{
	for (ItsApproved_Delivery__c delivery : deliveryList)
	{
		delivery.ItsApproved_Design__c  = design.Id;
	}
}
if(deliveryList.size() > 0)
{
	insert deliveryList;
}
Hope this helps.



 
Steve CairneySteve Cairney
Hi Krishna, 

I suspected this was the case as if I removed everything put the Product, the Booking saved fine. I'm going to work on this code and let you know how it goes.

Is it possible to have the page open with 1 row already created btw? (currently you have to click Add Row to begin inputting)
Krishna SambarajuKrishna Sambaraju
Call the addRow method in your constructor.
public CreateIABookingController(){
	rows = new Map<Integer, Row>();
    addRow();
}
That will create a row when the page is loaded.
 
Steve CairneySteve Cairney
Hi Krishna, I'm struggling to place the if statements in the right place in the code. 

I'm getting a System.ListException: Before Insert or Upsert list must not have two identically equal elements error.

It's highlighting that insert deliveryList; is the culprit.

Could this be because the the deliveryList isn't inserted until the end, perhaps it should be inserted before?

Code snippet here (it's possible that the if statements are in the wrong place)
 
public PageReference Save()
    {
        ItsApprovedBooking.Opportunity__c = opportunityId;
        insert ItsApprovedBooking;
        	for (Row r : rows.values())
			{
				r.ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id;
				r.ItsApprovedProduct.Opp__c = opportunityId;
				productList.add(r.ItsApprovedProduct);
	
				r.ItsApprovedDesign.Opportunity__c = opportunityId;
				//r.ItsApprovedDesign.Product_Reference__c = ItsApprovedProduct.Id;
				designList.add(r.ItsApprovedDesign);
	
				//r.ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
				r.ItsApprovedDelivery.Opportunity__c = opportunityId;
				deliveryList.add(r.ItsApprovedDelivery);

				//r.ItsApprovedDelivery.ItsApproved_Design__c  = ItsApprovedDesign.Id;
				deliveryList.add(r.ItsApprovedDelivery);
                
                if (productList.size() > 0)
					{
						insert productList;
					}
					for(ItsApproved_Product__c product : productList)
					{
						for (ItsApproved_Design__c design : designList)
						{
							design.Product_Reference__c = product.Id;
						}
						for (ItsApproved_Delivery__c delivery : deliveryList)
						{
							delivery.Product__c = product.Id;
						}
					}
					if(designList.size() > 0)
					{
						insert designList;
					}
					for (ItsApproved_Design__c design : designList)
					{
						for (ItsApproved_Delivery__c delivery : deliveryList)
						{
							delivery.ItsApproved_Design__c  = design.Id;
						}
					}
					if(deliveryList.size() > 0)
					{
						insert deliveryList;
					}
 			}   
        
        			
                return new PageReference ('/' + opportunityId);
            
       
    }

 
Krishna SambarajuKrishna Sambaraju
Hi Steve,

The following code should be outside the for loop.
if (productList.size() > 0)
{
	insert productList;
}
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		design.Product_Reference__c = product.Id;
	}
	for (ItsApproved_Delivery__c delivery : deliveryList)
	{
		delivery.Product__c = product.Id;
	}
}
if(designList.size() > 0)
{
	insert designList;
}
for (ItsApproved_Design__c design : designList)
{
	for (ItsApproved_Delivery__c delivery : deliveryList)
	{
		delivery.ItsApproved_Design__c  = design.Id;
	}
}
if(deliveryList.size() > 0)
{
	insert deliveryList;
}
Move above lines outside the for loop and try it.
 
Steve CairneySteve Cairney
Hmm, I tried that but the results are the same. The error persists...
Krishna SambarajuKrishna Sambaraju
Hi Steve,

I found the error. The error is in your for loop. You have the following statement
deliveryList.add(r.ItsApprovedDelivery); twice in the loop.

Remove one of them and try again. Make sure to keep the lines I mentioned in my previous post out of the for loop.
Steve CairneySteve Cairney
Great, this is nearly there. I'm going to look at the if loop as if you look at the screenshots, I created 3 products each with a design and a delivery

But although it saved, the designs and deliveries don't get related to the correct product.

I'll work more on this tomorrow and will report back

Here's what I inputted
User-added image

And here is the results on the Opp page (note how the Product Reference is the same on the designs and deliveries)

User-added image
Krishna SambarajuKrishna Sambaraju
You should create some maps, to check which design belongs which product, then use the map to assign the right product to the right design and so on. That was my question in one of my previous solutions. For example you can create a map as below.
Map<string, string> product2DesignMap = new Map<string, string>(); //add this variable before for loop rows
//add the following line in the for loop of rows.
product2DesignMap.put(r.ItsApprovedProduct.Product_Code__c, r.ItsApprovedDesign.Artwork_Title__c)

and check the Artwork_Title__c before adding to list.
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		if (product2DesignMap.get(product.Product_Code__c) == design.Artwork_Title__c)
			design.Product_Reference__c = product.Id;
	}
	for (ItsApproved_Delivery__c delivery : deliveryList)
	{
		delivery.Product__c = product.Id;
	}
}
You have to do a similar checks for the delivery.
Hope this helps.
 
Steve CairneySteve Cairney
Hi Krishna, I'm interested in why you chose those fields for the new map, as Artwork title isn't really related. Although it works!

Once I know you're thinking behind this I'll be able to apply the same technique to the delivery aspect.
Krishna SambarajuKrishna Sambaraju
Hi Steve,

It doesn't have to be Artwork Title, it can be some other field. I gave it as an example.

The productList, designList and deliveryList are prepared in a for loop "rows". It is not a good practice to use DML statements (like insert, update etc) in a for loop. So we are inserting the lists outside this for loop. As the lists are prepared before insert, we don't have the reference fields (for example we can't refer the Product Id in the Design object's Product_Reference__c field). So there should be some data other than the Id that matches the right product to the right design. It could be any field (text or number) that can say this design belongs to this product.

Hope you understand what I was trying to say.
 
Steve CairneySteve Cairney
That does make sense.

What I guess I'm confused about is the following

if (product2DesignMap.get(product.Product_Code__c) == design.Artwork_Title__c)

I don't understand how they can be equal.

Here's how I'm reading it logically. If there is a product in the product2 map, get the product code and match the same product it with the artwork title. But that doesn't make sence to me.

Also, no matter what I try, I can't apply the same technique to the Delivery object. All that happens in the last product and design row is added twice.
Steve CairneySteve Cairney
Also, thinking about this if the artwork title was used, and the name of the artwork was the same for two products it would confuse the code.

I wonder if we can use the rowID? 
Krishna SambarajuKrishna Sambaraju
A map is a key- value pair. In the example I gave you, I created a map using 
product2DesignMap.put(r.ItsApprovedProduct.Product_Code__c, r.ItsApprovedDesign.Artwork_Title__c) where the first value r.ItsApprovedProduct.Product_Code__c is the key and the second value r.ItsApprovedDesign.Artwork_Title__c is the value. This map is created in the for loop "rows" as the Product Code is different on each row, it creates a map with product codes as key and Artwork_Title__c as the value in this case.

And when I use this map in IF condition product2DesignMap.get(product.Product_Code__c), I am passing the keyproduct.Product_Code__c which will return the value Artwork_Title__c which I have set earlier.

So if this value is equal to design.Artwork_Title__c then I set the Product_Reference__c using the Id field.

I hope it is clear now.
Steve CairneySteve Cairney
Right, so in regards to the Delivery object. I should map the Artwork Title the the Depot Code? If it's only pairs, I can't add the Product code also... My think here is within 1 row there will only be 1 depot code, however this might not work if the same depot code is used on another row?

Something like

product2DeliveryMap.put(r.ItsApprovedDesign.Artwork_Title__c, r.ItsApprovedDelivery.Depot_Code__c)?

I'll have to add instruction to the user so they don't use the same artwork titles for different products, if we can't reference the integer rowId somehow.
Krishna SambarajuKrishna Sambaraju
I don't think you can use the RowId, as this is not a field on the actual object "ItsApproved_Product__c". When I am checking the map in the IF condition I was passing the Product_Code__c from the inserted productList, where there is no RowId.
Krishna SambarajuKrishna Sambaraju
In the solution I gave you, you cannot have same product code twice as product code is the key in the map, but for different product codes, you can have same Artwork Titles.
Steve CairneySteve Cairney
That's actually good because there shouldn't be 2 of the same products on the same booking.

Now, for the delivery I'm pairing the artwork title and the depot code (decimal) so I've got this outside the for loop

Map<string,decimal> product2DeliveryMap = new Map<string,decimal>();

I've got this inside the for loop
product2DeliveryMap.put(r.ItsApprovedDesign.Artwork_Title__c,r.ItsApprovedDelivery.Depot_Code__c);

and I've for this in the for loop 
 
for (ItsApproved_Design__c design : designList)
        {
            for (ItsApproved_Delivery__c delivery : deliveryList)
            {
					if (product2DeliveryMap.get(design.Artwork_Title__c) == delivery.Depot_Code__c)//added for new map
                	delivery.ItsApproved_Design__c  = design.Id;
            }
        }

However, The deliveries still don't match the designs. Could I be placing the code in the wrong place?
Krishna SambarajuKrishna Sambaraju
As the product codes are unique, why don't you use the Product Code to create this delivery map as well.
product2DeliveryMap.put(r.ItsApprovedProduct.Product_Code__c,r.ItsApprovedDelivery.Depot_Code__c);

and later check using 
if (product2DeliveryMap.get(design.ItsApproved_Product__r.Product_Code__c) == delivery.Depot_Code__c)

Hope this works.
Steve CairneySteve Cairney
OK, I've managed to relate the multiple Products with the Delivery, but not the design. by using the following code (I changed the name of the maps to MultiX )
 
for (ItsApproved_Delivery__c delivery : deliveryList)
            {
                if (MultiDeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)//this relates the Product with the delivery
                delivery.Product__c = product.Id;
            }
You can see the results on the Opp page here

User-added image

So now I need think I need to relate the Product and the Design with the Delivery.

The question is do I need to create a new Map for that, or can I reuse 
(MultiDeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)

by just placing it in the code again.

I'll do some tests!
Krishna SambarajuKrishna Sambaraju
Try this for your deliverList
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		for (ItsApproved_Delivery__c delivery : deliveryList)
		{
			boolean designAssigned = false;
			if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
			{
				delivery.ItsApproved_Design__c  = design.Id;
				designAssigned = true;
				break;
			}
			if (designAssigned)	
				break;
		}
	}
}
Hope this works.
 
Steve CairneySteve Cairney
OK, I added that in there (I received an error but this was solved by allowing reparenting on ItsApproved_Design__c on the Delivery object)

However! Still no change. The delivery assumes the last design regardless of product code.
Krishna SambarajuKrishna Sambaraju
Sorry, there was a mistake in the code. Try this.
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		boolean designAssigned = false;
		for (ItsApproved_Delivery__c delivery : deliveryList)
		{
			if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
			{
				delivery.ItsApproved_Design__c  = design.Id;
				designAssigned = true;
				break;
			}
			if (designAssigned)	
				break;
		}
	}
}
The boolean field was declared in wrong loop.
 
Steve CairneySteve Cairney
Hmm, afraid no change. 

What's that code trying to do Krishna?
Krishna SambarajuKrishna Sambaraju
This bit might need a different logic. I will check it and update the script.
Krishna SambarajuKrishna Sambaraju
Sorry the second break statement was in wrong loop. Try this.
for(ItsApproved_Product__c product : productList)
{
	for (ItsApproved_Design__c design : designList)
	{
		boolean designAssigned = false;
		for (ItsApproved_Delivery__c delivery : deliveryList)
		{
			if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
			{
				delivery.ItsApproved_Design__c  = design.Id;
				designAssigned = true;
				break;
			}
		}
		if (designAssigned)	
			break;
	}
}
It should work this time.
 
Krishna SambarajuKrishna Sambaraju
Ignore it. This won't work.This logic always assigns the first design to all the deliveries. I will post the new solution in a bit.
Krishna SambarajuKrishna Sambaraju
Here is the code, I think should work.
//prepare a map between product code and design id. As there is a lookup for product on the design object, you //should be able to do the following query.
List<ItsApproved_Design__c> designs = [select Id, Product_Reference__r.Product_Code__c from ItsApproved_Design__c 
                                        where Id IN designList];
Map<string, Id> productCode2DesignId = new Map<string, Id>();
for (ItsApproved_Design__c design : designs)
{
    productCode2DesignId.put(design.Product_Reference__r.Product_Code__c, design.Id);
}

//loop through the deliverList and productList and assign the design Id
for (ItsApproved_Delivery__c delivery : deliveryList)
{
    for(ItsApproved_Product__c product : productList)
    {
        if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
        {
            delivery.ItsApproved_Design__c  = productCode2DesignId.get(product.Product_Code__c);
            break;
        }
    }
}
Hope this works.
 
Steve CairneySteve Cairney
Thanks Krishna, I'll work on this now. Should I revert the code on the deliveryList back to how it was before?
Steve CairneySteve Cairney
It's possible I've got the code in the wrong loops as the results are unchanged.

I have declared the following, perhaps in the wrong place?

This is at line 10 after the other lists
List<ItsApproved_Design__c> designs = [select Id, Product_Reference__r.Product_Code__c from ItsApproved_Design__c where id IN : designList];

This is outside the for loop
 
Map<string,Id> productCode2DesignId = new Map<string,Id>();

This is inside the for loop
 
product2DeliveryMap.put(r.ItsApprovedProduct.Product_Code__c,r.ItsApprovedDelivery.Depot_Code__c);

And the loop through is at the end (after insert designList)
if(deliveryList.size() > 0)
        {
            insert deliveryList;
        }
        for(ItsApproved_Delivery__c delivery : deliveryList)
        {
            for (ItsApproved_Product__c product : productList)
            {
                if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
                {
                    delivery.ItsApproved_Design__c  = productCode2DesignId.get(product.Product_Code__c);
                    break;
                    
                }
                
            }
        }
My thinking is there's no inset for the new list we created back in line 10 "designs" so the code is ignoring it?
 
Steve CairneySteve Cairney
Opps, I forgot to add the new for. I've put it here but no change
 
if(deliveryList.size() > 0)
        {
            insert deliveryList;
        }
        
        for (ItsApproved_Design__c design : designs)
        {
            productCode2DesignId.put(design.Product_Reference__r.Product_Code__c, design.Id);
        }
        for(ItsApproved_Delivery__c delivery : deliveryList)
        {
            for (ItsApproved_Product__c product : productList)
            {
                if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
                {
                    delivery.ItsApproved_Design__c  = productCode2DesignId.get(product.Product_Code__c);
                    break;
                    
                }
                
            }
        }

 
Krishna SambarajuKrishna Sambaraju
Did you add the following query before the for loop for designs. I don't see it in your above post.

List<ItsApproved_Design__c> designs = [select Id, Product_Reference__r.Product_Code__c from ItsApproved_Design__c                                          where Id IN designList];
 
Krishna SambarajuKrishna Sambaraju
You should add the entire code after your "insert designList" statement and before the "insert deliverList". This should work.
 
Steve CairneySteve Cairney
I've got List<ItsApproved_Design__c>  right at the beginning of the code where variables are declared. Also I've added everything else AFTER the insert deliveryList.

I'll make the changes!               
Steve CairneySteve Cairney
We are sooooo close!

I had some left overs from previous attempts so I removed them and added your all your code exactly where you said.

Now the Designs are being added into the correct deliveries but the Products aren't.

So I think we are one list and one loop away from success.

I'm posting the code in it's entirety here so you can see the whole picture.
 
public class CreateIABookingController{
    
    public ItsApproved_Booking__c ItsApprovedBooking {get; set;}
    public ItsApproved_Product__c ItsApprovedProduct {get; set;}
    public ItsApproved_Design__c ItsApprovedDesign {get; set;}
    public ItsApproved_Delivery__c ItsApprovedDelivery {get; set;}
    List<ItsApproved_Product__c> productList = new List<ItsApproved_Product__c>();
    List<ItsApproved_Design__c> designList = new List<ItsApproved_Design__c>();
    List<ItsApproved_Delivery__c> deliveryList = new List<ItsApproved_Delivery__c>();
    private string OpportunityId;
    public Map<Integer, Row> rows { get; set; }
    public CreateIABookingController(){
        rows = new Map<Integer, Row>();
        addRow();
    }
    public void addRow() {
        Integer rowId = rows.size();
        rows.put(rowId , new Row(rowId, new ItsApproved_Product__c(), new ItsApproved_Design__c(), new ItsApproved_Delivery__c()));
    }
    public void removeRow() {
        rows.remove(Integer.valueOf(ApexPages.currentPage().getParameters().get('rowIdToRemove')));
    }
    public class Row {
        public Integer rowId { get; set; }
        public ItsApproved_Product__c ItsApprovedProduct {get; set;} 
        public ItsApproved_Design__c ItsApprovedDesign {get; set;} 
        public ItsApproved_Delivery__c ItsApprovedDelivery {get; set;}
        public Row(Integer rowId, ItsApproved_Product__c product, ItsApproved_Design__c design, ItsApproved_Delivery__c delivery) {
            this.rowId = rowId;
            ItsApprovedProduct = product;
            ItsApprovedDesign = design;
            ItsApprovedDelivery = delivery;
        }
    }
    {
        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;
        Map<string,string> product2DesignMap = new Map<string,string>();//map for designs
        Map<string,decimal> product2DeliveryMap = new Map<string,decimal>();//
        
        for (Row r : rows.values())
        {
            
            product2DesignMap.put(r.ItsApprovedProduct.Product_Code__c,r.ItsApprovedDesign.Artwork_Title__c);//maps product and
            //artwork title (both must be unique)
            product2DeliveryMap.put(r.ItsApprovedProduct.Product_Code__c,r.ItsApprovedDelivery.Depot_Code__c);//
            
            r.ItsApprovedProduct.Booking_Reference__c = ItsApprovedBooking.Id;
            r.ItsApprovedProduct.Opp__c = opportunityId;
            productList.add(r.ItsApprovedProduct);
            
            r.ItsApprovedDesign.Opportunity__c = opportunityId;
            r.ItsApprovedDesign.Product_Reference__c = ItsApprovedProduct.Id;
            designList.add(r.ItsApprovedDesign);
            
            r.ItsApprovedDelivery.Product__c = ItsApprovedProduct.Id;
            r.ItsApprovedDelivery.Opportunity__c = opportunityId;
            deliveryList.add(r.ItsApprovedDelivery);
            
        }
        
        if (productList.size() > 0)
        {
            insert productList;
        }
        for(ItsApproved_Product__c product : productList)
        {
            for (ItsApproved_Design__c design : designList)
            {
                if (product2DesignMap.get(product.Product_Code__c) == design.Artwork_Title__c)//adds additional product and design
                    design.Product_Reference__c = product.Id;
                
            }
            for (ItsApproved_Delivery__c delivery : deliveryList)
            {
                delivery.Product__c = product.Id;
            }
        }
        if(designList.size() > 0)
        {
            insert designList;
        }
        
        //Krishna loop
        List<ItsApproved_Design__c> designs = [select Id, Product_Reference__r.Product_Code__c from ItsApproved_Design__c 
                                               where Id IN : designList];
        Map<string, Id> productCode2DesignId = new Map<string, Id>();
        for (ItsApproved_Design__c design : designs)
        {
            productCode2DesignId.put(design.Product_Reference__r.Product_Code__c, design.Id);
        }
        
        //loop through the deliverList and productList and assign the design Id
        for (ItsApproved_Delivery__c delivery : deliveryList)
        {
            for(ItsApproved_Product__c product : productList)
            {
                if (product2DeliveryMap.get(product.Product_Code__c) == delivery.Depot_Code__c)
                {
                    delivery.ItsApproved_Design__c  = productCode2DesignId.get(product.Product_Code__c);
                    break;
                }
            }
        }
        //end Krishna loop
        
        if(deliveryList.size() > 0)
        {
            insert deliveryList;
        }
        
        return new PageReference ('/' + opportunityId);
        
    }
    
}

 
Krishna SambarajuKrishna Sambaraju
You just need to add set the product id on the delivery just before or after you are setting the design id on the delivery (line 110 in the above code).
Just add the following line.
delivery.Product__c = product.Id;

That should set the product id on the delivery.
This was selected as the best answer
Steve CairneySteve Cairney
Bravo! Thanks so much for your help over the last few days Krishna, I have learnt a great deal and I can't thank you enough for your patience and work.