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,


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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.