I designed a Stateful batch to some data and send it as CSV via email. And I don't know why but the attachement received in the email doesn't contains all the expected data, the CSV file il cut in the half of a line as if the String sort of overflowed.

Do you guys have any clue about this ?

Here is the code of my APEX Batch :

public without sharing class TIMESHEET_BigReport_BATCH implements Database.Batchable<SObject>, Database.Stateful {

    private TIMESHEET_Report_CTRL_CLS.TIMESHEET_ServiceParams_DTO param;
    private List<TIMESHEET_Report_CTRL_CLS.TIMESHEET_ReportLine_DTO> report;
    private String who;
    private String what;
    private String userEmail;

    private List<EFX_UserAdditionalInfo__c> users;

    public TIMESHEET_BigReport_BATCH(TIMESHEET_Report_CTRL_CLS.TIMESHEET_ServiceParams_DTO param, String userEmail) {
        this.param = param;
        this.userEmail = userEmail;
        this.report = new List<TIMESHEET_Report_CTRL_CLS.TIMESHEET_ReportLine_DTO>();

    public Database.QueryLocator start(Database.BatchableContext context) {

        String query;
        if (param.typeReport.equals(TIMESHEET_Report_Service_CLS.STAFFING)) {

            who = TIMESHEET_Report_Service_CLS.getWhoStaffing(param.whoType, param.whoActif, param.whoValue, param.startDate, param.endDate, param.teamId);

            Set<Id> setUser = new Set<Id>();
            this.users = Database.query(who);
            for (EFX_UserAdditionalInfo__c u : this.users) {

            query = 'SELECT Id,Total_TC_Dev_Train_Manage__c,Total_TC_Leaves__c,Total_TC_Project_Service__c,User__c,User_Name__c,Date__c FROM EFX_Tock_Day__cWHERE Date__c >= ' + Date.valueOf(param.startDate) + 'AND Date__c < ' + Date.valueOf(param.endDate) + 'AND User__c IN:' + setUser + ' ORDER BY User_Name__c ASC';

        } else {

            this.who = TIMESHEET_Report_Service_CLS.getWho(this.param.whoType, this.param.whoActif, this.param.whoValue);
            this.what = TIMESHEET_Report_Service_CLS.getWhat(this.param.whatType, this.param.whatActif, this.param.whatValue);

            String tockId = TIMESHEET_Report_Service_CLS.getTockID(this.param);
            query = TIMESHEET_Report_Service_CLS.buildQuery(this.who, this.what, this.param, TIMESHEET_Report_Service_CLS.RESULT_QUERY, tockId);

        return Database.getQueryLocator(query);

    public void execute(Database.BatchableContext context, List<Object> result) {
        if (param.typeReport.equals(TIMESHEET_Report_Service_CLS.STAFFING)) {
            List<EFX_Tock_Day__c> resultTockDays = (List<EFX_Tock_Day__c>) result;
            this.report.addAll(TIMESHEET_Report_Service_CLS.getStaffing(this.users, resultTockDays, this.param));
        } else {
            List<EFX_Tock__c> resultTockCells = (List<EFX_Tock__c>) result;
            if (param.focus) {
                this.report.addAll(TIMESHEET_Report_Service_CLS.getTockCell(resultTockCells, param));
            } else {
                this.report.addAll(TIMESHEET_Report_Service_CLS.getTockCellBusiness(resultTockCells, param));


    public void finish(Database.BatchableContext context) {

        String CSV = this.getCSV();

        Messaging.EmailFileAttachment csvAttachement = new Messaging.EmailFileAttachment();
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setSubject('TimeSheet Report');
        email.setToAddresses(new List<String>{
        email.setPlainTextBody('Here is the Timesheet report you ordered');
        email.setFileAttachments(new Messaging.EmailFileAttachment[]{
        Messaging.SendEmailResult [] result = Messaging.sendEmail(new Messaging.SingleEmailMessage[]{

    private String getCSV() {
        String header;
        List<String> keys;
        if (this.param.focus) { // Result type is detail by user
            header = 'Activity Full Name;Type;Length;Unit';
            keys = new List<String>{
                    'activityFullName', 'typeTock', 'length', 'unit'
        } else if (this.param.typeReport == 'Staffing') { // Result type is staffing
            header = 'User;Team;BU;Country;Office;Contract Type;Work Day;Project/Service;Leave/Holiday;Management/Developement/Training;Response Rate;Staffing Rate';
            keys = new List<String>{
                    'userName', 'teamLine', 'practiceLine', 'country', 'officeLine',
                    'contractType', 'nbDayWeek', 'totalBAProjectService', 'totalBALeaveHoliday',
                    'totalBAManDevTrai', 'ResponseRate', 'StaffingRate'
        } else if (!this.param.sum) { // Result is detail by day
            header = 'User;Team;BU;Country;Office;Contract Type;Activity Full Name;Type;Date;Quarter;Month;Week Number;Day;Creation Date;Comment;Length;Unit';
            keys = new List<String>{
                    'userName', 'teamLine', 'practiceLine', 'country', 'officeLine',
                    'contractType', 'activityFullName', 'typeTock', 'cellDate', 'quarter', 'month',
                    'weekNumber', 'dayOfWeek', 'creationDate', 'comment', 'length', 'unit'
        } else if (this.param.sumWeek) { // Result is detail by week
            header = 'User;Team;BU;Country;Office;Contract Type;Activity Full Name;Type;Quarter;Month;Week Number;Length;Unit';
            keys = new List<String>{
                    'userName', 'teamLine', 'practiceLine', 'country', 'officeLine', 'contractType',
                    'activityFullName', 'typeTock', 'quarter', 'month', 'weekNumber',
                    'length', 'unit'
        // Else result type default ( detail by user and total by day
        header = 'User;Team;BU,Country;Office;Contract Type;Activity Full Name;Type;Length;Unit';
        keys = new List<String>{
                'userName', 'teamLine', 'practiceLine', 'country',
                'officeLine', 'contractType', 'activityFullName', 'typeTock', 'length',
        // Generating CSV from report
        String reportCSV = header+ '\n';
        for (TIMESHEET_Report_CTRL_CLS.TIMESHEET_ReportLine_DTO line : report) {
            Map<String, Object> lineMap = (Map<String, Object>) JSON.deserializeUntyped(JSON.serialize(line));

            List<String> values = new List<String>();
            for (String key : keys) {

            reportCSV += (String.join(values, ';') + '\n');

        return reportCSV;


Thank you in advance,

1 Answer 1


My suspicion is that you have an issue with cell values that include embedded delimiters (looks like you are using ; or line breaks or double quotes.

Best practice is to use String class method escapeCsv


  • will escape embedded line breaks
  • will escape embedded double quotes
  • will escape embedded commas (as you seem to be using ;, you'll need to handle this specially

You must log in to answer this question.

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