1

I have a map called getMonthAndYear() where it creates values from the startDate and EndDate of an opp. I am also adding two other values so the map looks like this.

Product | Description | startMonth | ... | ... | endMonth.

I am using that Map to populate my pageBlockTable using apex:repeat. Under each columns, I need an inputfield to be binded to some objects fields as below.

Product => Schedule_Product__c.Product_Name__c
Description => Schedule_Product__c.Description__c
all Months => Shedule_Product_Month__c.Amount__c 

I need to create a new record for each month that has an amount into it. If I have ex: Product | test | 1000$ | 2000$ it will than create 2 records

  1. Product test 1000$
  2. Product test 2000$

Right now, I have my VF Page displaying the header correctly but I am not able to bind the inputtext to the right Object and Field. Any help will be appreciated. See code below:

APEX:

<apex:page standardController="opportunity" extensions="ScheduleTable">
    <apex:form >      
        <apex:pageBlock id="pb" title="Invoicing Schedule" >
            
            <apex:pageBlockTable id="table1" value="{!columnContent}" var="cc">
                
                <apex:repeat value="{!MonthAndYear}" var="myheader">
                    <apex:column >
                        <apex:facet name="header">
                            {! myheader }
                        </apex:facet>
                        <input id=??IncrementalID?? type="text" name="theTextInput"/>
                    </apex:column>
                </apex:repeat>
            </apex:pageBlockTable>
            
        </apex:pageBlock> 
    </apex:form>
</apex:page>

Class:

public with sharing class ScheduleTable {

    public Opportunity opp{get;set;}
    public product_Schedule__c ps{get;set;}
    
    
    public ScheduleTable(ApexPages.StandardController stdController) {
        this.opp = (Opportunity)stdController.getRecord();
        ps = new Product_Schedule__c();
    }

    public Set<String> getColumnContent() {
        Set<String> columnContent = new Set<String>();
        columnContent.add('<input id="theTextInput" type="text" name="theTextInput" />');
        return columnContent;
    }

    public Set<String> getMonthAndYear() {
        
        opp = [SELECT Id, Name, Start_Date__c, End_Date__c FROM Opportunity WHERE Id = : opp.Id];
        
        Date d1 = opp.Start_Date__c;
        Date d2 = opp.End_Date__c;
        Map<Integer,String> monthNameMap=new Map<Integer, String>{1=>'Janvier', 2=>'Février', 3=>'Mars', 4=>'Avril', 5=>'Mai',
                                                                    6=>'Juin', 7=>'Juillet', 8=>'Aout', 9=>'Septembre',10=>'Octobre',
                                                                    11=>'Novembre', 12=>'Decembre'};

        Set<String> monthYearSet = new Set<String>();
        
        monthYearSet.add('Produits');
        monthYearSet.add('Description');
            
        while(d1 < d2)
            {
                monthYearSet.add(monthNameMap.get(d1.Month()) + ' ' + d1.Year());
                d1 = d1.AddMonths(1);
            }
        return monthYearSet;
    }
    
    public PageReference save() {
        update opp;
        ps.opportunity__c = opp.Id;
        insert ps;
        return null;
    }
}

Credits for the code: How to get custom table row headers and values on a Apex Page Table?

Thanks for your help!

0

1 Answer 1

0

Ok, I've drafted a possible solution:

Visualforce page:

<apex:page standardController="Opportunity" extensions="ScheduleTable">
    <apex:form >      
        <apex:pageBlock id="pb" title="Invoicing Schedule" >
            <apex:pageBlockTable id="table1" value="{!scheduleRows}" var="scheduleRow">
                <apex:repeat value="{!fieldNames}" var="fieldName">
                    <apex:column headerValue="{!fieldName}">
                        <apex:inputText value="{!scheduleRow[fieldName]}" />
                    </apex:column>
                </apex:repeat>
            </apex:pageBlockTable>
            <apex:pageblockButtons>
                 <apex:commandButton value="Save" action="{!save}"/>
            </apex:pageblockButtons>  
        </apex:pageBlock> 
    </apex:form>
</apex:page>

Apex class:

public with sharing class ScheduleTable {
    public Opportunity opp { get; set; }
    public List<ScheduleRow> scheduleRows { get; set; }
    public List<String> fieldNames { get; set; }
    public Set<String> monthsAndYears;
    
    public ScheduleTable(ApexPages.StandardController stdController) {
        this.opp = (Opportunity) stdController.getRecord();
        this.monthsAndYears = getMonthsAndYears();
        this.fieldNames = new List<String>{'product', 'description'};
        this.fieldNames.addAll(this.monthsAndYears);
        this.scheduleRows = new List<ScheduleRow>{new ScheduleRow()}; // TODO: probably initialize with more rows, or have an add row button
    }
    
    public Set<String> getMonthsAndYears() {
        opp = [
            SELECT Id, Name, Start_Date__c, End_Date__c
            FROM Opportunity
            WHERE Id = :opp.Id
        ];
        
        Date d1 = opp.Start_Date__c;
        Date d2 = opp.End_Date__c;
        
        Map<Integer, String> monthNameMap = new Map<Integer, String>{
            1 => 'jan',
                2 => 'feb',
                3 => 'mar',
                4 => 'apr',
                5 => 'may',
                6 => 'jun',
                7 => 'jul',
                8 => 'aug',
                9 => 'sep',
                10 => 'oct',
                11 => 'nom',
                12 => 'dec'
                };
                    
        Set<String> monthYearSet = new Set<String>();
        
        while (d1 < d2) {
            monthYearSet.add(monthNameMap.get(d1.Month()) + d1.Year());
            d1 = d1.addMonths(1);
        }
        
        System.debug(monthYearSet);
        return monthYearSet;
    }
    
    public PageReference save() {
        for (ScheduleRow row : scheduleRows) {
            Map<String, Object> rowJSON = (Map<String, Object>)JSON.deserializeUntyped(JSON.serialize(row));
            for (String field : this.monthsAndYears) {
                if (rowJSON.get(field) != null) {
                    // TODO: Create an instance of your schedule record and add it to a list, to insert the whole list later
                    // TODO: store row.product and row.description in the record
                }
            }
        }
        // TODO: insert schedule records list
        return null;
    }
    
    public class ScheduleRow {
        public String product { get; set; }
        public String description { get; set; }
        public String dec2020 { get; set; }
        public Decimal jan2021 { get; set; }
        public Decimal feb2021 { get; set; }
        // TODO: create more fields for possible months
    }
}

Apex has not many reflection capabilities so it's hard to work with dynamic references, specially with generic classes. I tried this with an opportunity that starts in dec2020 and ends in feb2021 and it worked.

Note that to have column headers in french, you can create a map that maps column field names with the name in french, and evaluate that with headerValue="{!headerNames[fieldName]}.

You'll have to complete the code, but it's a start. Hope that helps.

3
  • Thanks Alba, the class ScheduleRow, is there a way to prevent having to hard code this there? It means that I will need to add more than 120 lines for the next 10 years?
    – Eric
    Feb 5, 2021 at 19:47
  • Not sure... as apex pageblocktable needs to be bound to a class, even if you use the json deserialize / serialize trick...
    – Alba Rivas
    Feb 5, 2021 at 22:07
  • You can always create a custom table
    – Alba Rivas
    Feb 5, 2021 at 22:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .