Untitled

 avatar
unknown
plain_text
9 months ago
35 kB
5
Indexable
/**
* @author Ionut Boghean
* @date 2016
*
* @group Opportunity Object
* @group-content 
*
* @description Helper class for OpportunityHandler
*
* -----------------------------------------------------------------------------------------------------------------------------
* Ver.     Date        Who                          Description
*          2023-02-01  david.sanchez@empaua.com     Refactored code and replaced the use of a "fake" email to render the template with
*                                                   the a call to Messaging.renderStoredEmailTemplate.
* EMP-65   2023-02-02  david.sanchez@empaua.com     Modified the way in which attachments are selected for Confirmation emails. 
*                                                   Now for Sent and Change confirmations an exclusion list is use (instead of an inclusion list).
*/
public without sharing class OpportunityHelper {
    public static Boolean isRecurringExecution = false;
    
    public static void checkActivePeriods(Map<Id, Opportunity> newMap, Map<Id, Opportunity> oldMap) {
        Opportunity oldRecord;
        Integer activeBookingPeriods = Database.CountQuery('SELECT COUNT() FROM BookingPeriod__c WHERE Active__c = true');
        for(Opportunity newRecord :newMap.values()) {
            oldRecord = oldMap.get(newRecord.Id);
            if(newRecord.StageName != oldRecord.StageName && 
                (newRecord.StageName == System.Label.OpportunityTransactionStageSent ||
                 newRecord.StageName == System.Label.OpportunityTransactionStageBookingCancelledWithFee ||
                 newRecord.StageName == System.Label.OpportunityTransactionStageBookingCancelledWithFeeErgoRRV ||
                 newRecord.StageName == System.Label.OpportunityTransactionStageBookingChanged
                ) && 
                newRecord.Type == System.Label.OpportunityTransactionTypeBUC
            ) {
                newRecord.HasActiveBookingPeriod__c = (activeBookingPeriods == 1)? true : false;
            }
        }
    }
    
    /*******************************************************************************************************
    * @description Update opportunity related data when its cancelled without fee
    * @param newMap Updated Opportunity records
    * @param oldMap Old Opportunity records
    */
    public static void doCancellationUpdates(Map<Id, Opportunity> newMap, Map<Id, Opportunity> oldMap) {
        Opportunity oldRecord;
        for(Opportunity newRecord :newMap.values()) {
            oldRecord = oldMap.get(newRecord.Id);
            if (newRecord.StageName != oldRecord.StageName && 
                newRecord.Type == System.Label.OpportunityTransactionTypeBUC) {
                if (newRecord.StageName == System.Label.OpportunityTransactionStageBookingCancelledWithoutFee) {
                    newRecord.AccountsReceivableEuro__c = 0;
                    newRecord.AccountsReceivableLocalCurrency__c = 0;
                }
                else if (newRecord.StageName == System.Label.OpportunityTransactionStageBookingCancelledWithoutFeeErgoRRV) {
                    newRecord.AccountsReceivableEuro__c = 0;
                    newRecord.AccountsReceivableLocalCurrency__c = 0;
                }
            }
        }
    }
    
    /*******************************************************************************************************
    * @description Update opportunity's trustpilot invitation link
    * @param newMap Updated Opportunity records
    */
   
    public static void updateTrustpilotInvitationLink(List<Opportunity> newOpportunities) {

        List<Opportunity> validOpportunities = new List<Opportunity>();
    
        for (Opportunity oppty : newOpportunities) {
            if (!isRecurringExecution 
                && String.isBlank(oppty.Trustpilot_Invitation_Link__c) 
            ) {
                validOpportunities.add(oppty);
            }
        }
    
        if (!validOpportunities.isEmpty()) {
            System.enqueueJob(new CreateTrustpilotInvitationLink(validOpportunities));
        }
    }

    /*******************************************************************************************************
    * @description Update opportunity email from the ac
    * @param newMap Updated Opportunity records
    */
    public static void doEmailUpdates(Map<Id, Opportunity> newMap) {
        for(Opportunity opp : [select Id, Customer__c, Customer__r.PersonContactId, Customer__r.PersonEmail from Opportunity where id in :newMap.keySet()]) {
            if(opp.Customer__r.PersonEmail != null) {
                newMap.get(opp.Id).CustomerEmail__c = opp.Customer__r.PersonEmail;
            }
         }
    }

    /*******************************************************************************************************
    * @description Create booking history records
    * @param newMap New Opportunity records
    */
    public static void createBookingHistory(Map<Id, Opportunity> newMap) {
        createBookingHistory(newMap,null);
    }
    
    /*----*/
    public static void createBookingHistory(Map<Id, Opportunity> newMap, Map<Id,Date> prevRentingStartDtByOpptId) {
        List<BookingHistory__c> newHistoryRecords = new List<BookingHistory__c>();
        for(Opportunity newRecord :newMap.values()) {
            newHistoryRecords.add(new BookingHistory__c(
                OpportunityRef__c = newRecord.Id,
                IsActive__c = true,
                AccountRef__c = newRecord.AccountId,
                BookingID__c = newRecord.BookingID__c,
                CostsBasisBackageEuro__c = newRecord.CostsBasisBackageEuro__c,
                CostsAdditionalServices__c = newRecord.CostsAdditionalServices__c,
                CostsProviderOnSiteEuro__c = newRecord.CostsProviderOnSiteEuro__c,
                CostsProviderOnSite__c = newRecord.CostsProviderOnSite__c,
                BookingCountry__c = newRecord.BookingCountry__c,
                CustomerRef__c = newRecord.Customer__c,
                Model__c = newRecord.Model__c,
                NumberOfPassengers__c = newRecord.NumberOfPassengers__c,
                CostsCamperDays__c = newRecord.CostsCamperDays__c,
                PaymentMethod__c = newRecord.PaymentMethod__c,
                RentingBeginDate__c = newRecord.RentingBeginDate__c,
                RentingReturnDate__c = newRecord.RentingReturnDate__c,
                RentingReturnLocationRef__c = newRecord.RentingReturnLocation__c,
                RentingStartLocationRef__c = newRecord.RentingStartLocation__c,
                TaxCountry__c = newRecord.TaxCountry__c,
                TotalAmount__c = newRecord.TotalAmount__c,
                WireTransferBy__c = newRecord.WireTransferBy__c,
                AmountCO2OffsetAuto__c = newRecord.AmountCO2OffsetAuto__c,
                Insurance__c = newRecord.Insurance__c,                                  // CDW Insurance
                CostsInsuranceNew__c = newRecord.CostsInsuranceNew__c,
                CommissionInsuranceEUR__c = newRecord.CommissionInsuranceAuto__c,
                HasCOVInsurance__c = newRecord.HasCOVInsurance__c,                      // COV Insurance
                InsuranceCostsEuroCOV__c = newRecord.InsuranceCostsEuroCOV__c,
                CommissionInsuranceCOV__c = newRecord.CommissionInsuranceCOVAuto__c,
                HasRRVInsurance__c = newRecord.HasRRVInsurance__c,                      // RRV Insurance
                InsuranceCostsEuroRRV__c = newRecord.InsuranceCostsEuroRRV__c,
                CommissionInsuranceRRV__c = newRecord.CommissionInsuranceRRVAuto__c,
                AccountsPayableEUR__c = newRecord.AccountsReceivableEuro__c,
                AccountsPayableLocalCurrency__c = newRecord.AccountsReceivableLocalCurrency__c,
                CommissionableAmountEUR__c = newRecord.CommissionableAmount__c,
                PreliminaryMarginEUR__c = newRecord.PreliminaryMarginAuto__c,
                PreviousRentingBeginDate__c = (prevRentingStartDtByOpptId != null ? prevRentingStartDtByOpptId.get(newRecord.Id) : null),
                CustomerSaldo__c = newRecord.CustomerSaldoAuto__c,
                Payable_Amount_to_CamperDays_CFX__c = newRecord.Payable_Amount_to_CamperDays_CFX__c
            ));
        }
        try {
            if(!newHistoryRecords.isEmpty()) {
                insert newHistoryRecords;
            }
        } catch(Exception ex) {
            
        }
    }
    
    /*******************************************************************************************************
    * @description Update history when a booking is changed
    * @param opportunitiesById Changed bookings
    */
    public static void updateBookingHistory(Map<Id, Opportunity> newMap, Map<Id, Opportunity> oldMap) {
        Opportunity oldRecord;
        Set<Id> filteredOppIds = new Set<Id>();
        for(Opportunity newRecord :newMap.values()) {
            oldRecord = oldMap.get(newRecord.Id);
            if(newRecord.StageName != oldRecord.StageName && newRecord.StageName == System.Label.OpportunityTransactionStageSent) {
                filteredOppIds.add(newRecord.Id);
            }
        }
        updateBookingHistory(filteredOppIds);
    }
    
    /*******************************************************************************************************
    * @description Update history when a booking is changed
    * @param opportunitiesById Changed bookings
    */
    public static void updateBookingHistory(Set<Id> opportunityIds) {
        Map<Id, Opportunity> opportunitiesById = new Map<Id, Opportunity>();
        
        Map<Id, BookingAdditionalService__c> servicesById;
        for(Opportunity newRecord :[SELECT Id, AccountId, BookingID__c, CostsBasisBackageEuro__c, CostsAdditionalServices__c, CostsProviderOnSiteEuro__c,
                                           CostsProviderOnSite__c, BookingCountry__c, Customer__c, Model__c, NumberOfPassengers__c, CostsCamperDays__c,
                                           PaymentMethod__c, RentingBeginDate__c, RentingReturnDate__c, RentingReturnLocation__c, RentingStartLocation__c, 
                                           TaxCountry__c, TotalAmount__c, WireTransferBy__c, AccountsReceivableEuro__c, AccountsReceivableLocalCurrency__c,
                                           Insurance__c, InsuranceID__c, CostsInsuranceNew__c, CommissionInsuranceAuto__c, CustomerSaldoAuto__c,
                                           HasCOVInsurance__c, COVInsuranceID__c, CommissionInsuranceCOVAuto__c, InsuranceCostsEuroCOV__c,
                                           HasRRVInsurance__c, RRVInsuranceID__c, CommissionInsuranceRRVAuto__c, InsuranceCostsEuroRRV__c,
                                           AmountCO2OffsetAuto__c, CommissionableAmount__c, PreliminaryMarginAuto__c,Payable_Amount_to_CamperDays_CFX__c,
                                           Original_Customer_Lastname__c,Customer_Exchange_Rate__c,Customer_Currency__c
                                    FROM Opportunity WHERE Id IN :opportunityIds]) {
            opportunitiesById.put(newRecord.Id, newRecord);
        }

        List<BookingHistory__c> historyRecordsToUpdate = new List<BookingHistory__c>();
        Map<Id,Date> prevRentingStartDtByOpptId = new Map<Id,Date>();
        for(BookingHistory__c record :[SELECT Id, OpportunityRef__c, IsActive__c, RentingBeginDate__c FROM BookingHistory__c WHERE OpportunityRef__c IN :opportunitiesById.keySet() AND IsActive__c = true]) {
            record.IsActive__c = false;
            historyRecordsToUpdate.add(record);
            prevRentingStartDtByOpptId.put(record.OpportunityRef__c,record.RentingBeginDate__c);
        }
        createBookingHistory(opportunitiesById,prevRentingStartDtByOpptId);
        try {
            if(!historyRecordsToUpdate.isEmpty()) {
                update historyRecordsToUpdate;
            }
        } catch(Exception ex) {
            
        }
        servicesById = new Map<Id, BookingAdditionalService__c>([SELECT Id, BookingHistoryRef__c, IsPaidOnSite__c, LocalCurrency__c, NameEnglish__c, NameGerman__c, OpportunityRef__c, Price__c, Quantity__c, Type__c, VATNote__c FROM BookingAdditionalService__c WHERE OpportunityRef__c IN :opportunitiesById.keySet()]);
        BookingServiceHelper.createBookingHistoryCopy(servicesById);
    }
    
    /*******************************************************************************************************
    * @description Prepares data to send email in Sent and Booking Changed Stages
    * @param newMap
    * @param oldMap
    */
    
    public static void prepareEmailForBooking(Map<Id, Opportunity> newMap, Map<Id, Opportunity> oldMap) {

        Boolean isUpdate = (oldMap != null);
        Map<Id,Id> sendBookings = new Map<Id, Id>();
        Map<Id,Id> changeBookings = new Map<Id, Id>();
        Map<Id,Id> canceledBookings = new Map<Id, Id>();
        for(Opportunity newRecord :newMap.values()) {
        
            Opportunity oldRecord = (isUpdate == true) ? oldMap.get(newRecord.Id) : null;
        
            if(newRecord.StageName != oldRecord.StageName && newRecord.StageName == System.Label.OpportunityTransactionStageSent) {
                sendBookings.put(newRecord.Id, null);
            }
            if(newRecord.StageName != oldRecord.StageName && newRecord.StageName.contains(System.Label.OpportunityTransactionStageBookingCancelled)) {
                canceledBookings.put(newRecord.Id, null);
            }
            if(newRecord.WasBookingChangedClicked__c != oldRecord.WasBookingChangedClicked__c && newRecord.WasBookingChangedClicked__c == true) {
                changeBookings.put(newRecord.Id, null);
            }
        }
        
        if(!isRecurringExecution && (sendBookings.size() > 0 || changeBookings.size() > 0  || canceledBookings.size() > 0)) {
            sendEmailsForUpdatedRecords(sendBookings, changeBookings, canceledBookings);
            isRecurringExecution = true;
        }
    }
    
    @future (callout=true)
    public static void sendEmailsForUpdatedRecords(Map<Id,Id> sendBookings, Map<Id,Id> changeBookings, Map<Id,Id> canceledBookings){

        Map<Id,String> orgWideEmailAddressMap = new Map<Id,String>();
        Map<Id,String> bookingToLanguageMap = new Map<Id,String>();
        List<Id> oppIds = new List<Id>();
        oppIds.addAll(sendBookings.keySet());
        oppIds.addAll(changeBookings.keySet());
        oppIds.addAll(canceledBookings.keySet());
        if(oppIds.size() > 0) {
            for(Opportunity opp : [select Id, Customer__c, Customer__r.PersonContactId, Customer__r.PersonEmail, LanguageCodeAuto__c, WireTransferBy__c, WireTransferHelper__c from Opportunity where id in :oppIds]) {
			   
               bookingToLanguageMap.put(opp.Id,opp.LanguageCodeAuto__c);
               orgWideEmailAddressMap.put(opp.Id,System.Label.get('','BookingEmailWideAddressId_ALL',opp.LanguageCodeAuto__c));
               if(sendBookings.containsKey(opp.Id)) {
                    sendBookings.put(opp.Id, opp.Customer__r.PersonContactId);
                    generateDocuments(opp.Id, false);
                }
                if(changeBookings.containsKey(opp.Id)) {
                    changeBookings.put(opp.Id, opp.Customer__r.PersonContactId);
                    generateDocuments(opp.Id, true);
                }
                if(canceledBookings.containsKey(opp.Id)) {
                    canceledBookings.put(opp.Id, opp.Customer__r.PersonContactId);
                    generateDocument(opp.Id, System.Label.get('','CancelConfirm',opp.LanguageCodeAuto__c), 'canceled');
                }
             }
        }

        if(!isRecurringExecution || Test.isRunningTest()) {
            String bookingConfInclFiles = System.label.BookingConfirmationAttachments + '\n' + System.label.BookingConfirmationAttachments_2;
            sendEmails(bookingToLanguageMap,orgWideEmailAddressMap,sendBookings, System.label.BookingTemplateId, null, System.label.BookingConfirmationExcludeFiles);
            sendEmails(bookingToLanguageMap,orgWideEmailAddressMap,changeBookings, System.label.BookingChangeTemplateId, bookingConfInclFiles, null);
            sendEmails(bookingToLanguageMap,orgWideEmailAddressMap,canceledBookings, System.label.BookingCanceledTemplateId, System.label.BookingCanceledAttachments, null);

            //update booking history data
            updateBookingHistory(changeBookings.keySet());
            updateBookingHistory(sendBookings.keySet());
            isRecurringExecution = true;
        }
    }
    
    /*******************************************************************************************************
    * @description Sends emails
    * @param bookings 
    */
    public static void sendEmails(Map<Id,String> bookingToLanguageMap,Map<Id,String> orgWideEmailAddressMap, Map<Id, Id> bookings, String templateId, String attachmentsList, String attachmentExcludeList) {

        List<Messaging.SingleEmailMessage> emails = new List<Messaging.SingleEmailMessage>();
        for(Id oppId: bookings.keySet()) {
            Messaging.SingleEmailMessage email = prepareEmail(orgWideEmailAddressMap.get(oppId),oppId, bookings.get(oppId), templateId);
            email.setFileAttachments(prepareFileAttachments(bookingToLanguageMap.get(oppId), oppId, attachmentsList, attachmentExcludeList));
            emails.add(email);
        }
        if(!Test.isRunningTest()) { Messaging.sendEmail(emails); }
    }
    
    /*******************************************************************************************************
    * @description Prepares email for each booking
    * @param relatedId booking id
    * @param recipientId contact id
    * @param templateId template id
    */
    public static Messaging.SingleEmailMessage prepareEmail(String orgWideEmailAddress, Id relatedId, Id recipientId, String templateId) {
        
        Messaging.SingleEmailMessage emailToSend = Messaging.renderStoredEmailTemplate(templateId,recipientId,relatedId);
        emailToSend.setOrgWideEmailAddressId(OrgWideEmailAddress);
        emailToSend.setTreatTargetObjectAsRecipient(true);
        emailToSend.setSaveAsActivity(true);
        return emailToSend;
    }

    /**
     * 
     */
    public static List<Messaging.Emailfileattachment> prepareFileAttachments(String languageCode, Id bookingId, String attachmentsList, String attachmentExcludeList) {
        
        Set<String> bookingAttachments = new Set<String>(); 
        List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();
        List<String> cdIds = new List<String>();
        Map<id,ContentDocumentLink> CDLMap = New Map<id,ContentDocumentLink>([SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId =: bookingId]);
        for(ContentDocumentLink cdl :CDLMap.values()){
            cdIds.add(cdl.ContentDocumentId);
        }
        
        List<ContentVersion> Docs = [SELECT Id, Title, FileType, VersionData, isLatest, ContentDocumentId 
                                     FROM ContentVersion WHERE isLatest = true And ContentDocumentId IN : cdIds order by LastModifiedDate desc];

        for (ContentVersion document: Docs) {
            if (!bookingAttachments.contains(document.Title.replace('.pdf',''))) {
                Boolean shouldBeAttached = false;
                if (attachmentsList != null && OpportunityHelper.includeDocument(document.Title, attachmentsList)) {
                    shouldBeAttached = true;
                }
                else if (attachmentExcludeList != null && !OpportunityHelper.excludeDocument(document.Title, attachmentExcludeList)) {
                    shouldBeAttached = true;
                }
                if (shouldBeAttached) {
                    Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
                    String title = document.Title;
                    if(document.FileType == 'PDF' && !title.contains('.pdf')){
                        title += '.pdf';
                    }
                    
                    attachment.setBody(document.VersionData);
                    attachment.setFileName(title);
                    fileAttachments.add(attachment);
                    String fName = document.Title.replace('.pdf','');
                    if(fName == System.Label.get('','YourInvoice',languageCode))
                    	bookingAttachments.add(System.Label.get('','YourInvoice',languageCode));
                    if(fName == System.Label.get('','YourVoucher',languageCode))
						bookingAttachments.add(System.Label.get('','YourVoucher',languageCode));
                    
                    bookingAttachments.add(document.Title.replace('.pdf',''));
                }
            }
            
        }

        return fileAttachments;
    }

    /*******************************************************************************************************
    * @description checks if the file needs to be added to email
    * @param fileName
    * @param attachmentsList - an string containing a list document-names of the documents that should be attached to the email.
             the names should be separated by a comma, semicolon on new-line.
    */
    public static Boolean includeDocument(String fileName, String attachmentsList) {
        
        List<String> neededAttachments = attachmentsList.split('[,;\\n]');
        for(String name : neededAttachments) {
            if (fileName.replace('_',' ').startsWithIgnoreCase(name.trim().replace('_',' '))) {
                return true;
            }
        }
        return false;
    }

    /*******************************************************************************************************
    * @description checks if the file needs to be excluded and not be attached to the email
    * @param fileName - name of file to be checked
    * @param exclucionList - an string containing a list document-names of the documents that should NOT be attached to the email.
             the names should be separated by a comma, semicolon on new-line.
    */
    public static Boolean excludeDocument(String fileName, String exclucionList) {

        List<String> excludedDocs = exclucionList.split('[,;\\n]');
        for(String name : excludedDocs) {
            if (fileName.replace('_',' ').startsWithIgnoreCase(name.trim())) {
                return true;
            }
        }
        return false;
    }
    
    public static void generateDocuments(Id bookingId, Boolean isChange) {

        Opportunity opp = [Select Id, LanguageCodeAuto__c From Opportunity Where Id =: bookingId];
        String fileNameVoucher = System.Label.get('','YourVoucher',opp.LanguageCodeAuto__c);
        String fileNameInvoice = System.Label.get('','YourInvoice',opp.LanguageCodeAuto__c);        
        generateDocument(bookingId, fileNameVoucher, 'voucher');
        generateDocument(bookingId, fileNameInvoice, 'invoice' + (isChange ? 'Changed' : ''));
    }
    
    public static void generateDocument(Id bookingId, String documentName, String componentName) {

        Pagereference pdf = Page.BookingPDFs;
        pdf.getParameters().put('id', bookingId);
        pdf.getParameters().put('document', componentName);
        pdf.setRedirect(true);
        ContentVersion contentVersion = new ContentVersion();
        if (Test.IsRunningTest()) {
             contentVersion = createContentVersion(documentName + '.pdf', Blob.valueOf('UNIT.TEST'));
        } else {
             contentVersion = createContentVersion(documentName + '.pdf', pdf.getContentAsPDF());
        }
        
        insert contentVersion;
        contentVersion = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =:contentVersion.Id];
        ContentDocumentLink contentDocumentLink = createContentDocumentLink(contentVersion.ContentDocumentId, bookingId);
        insert contentDocumentLink;

    }

    public static ContentVersion createContentVersion(String name, Blob body){
        ContentVersion contentVersion = new ContentVersion();
        contentVersion.ContentLocation = 'S';
        contentVersion.PathOnClient = name;
        contentVersion.Title = name;
        contentVersion.VersionData = body;
        return contentVersion;
    }

    public static ContentDocumentLink createContentDocumentLink(Id contentDocumentId, Id parentId){
        ContentDocumentLink contentDocumentLink = new ContentDocumentLink();
        contentDocumentLink.ContentDocumentId = contentDocumentId;
        contentDocumentLink.LinkedEntityId = parentId;
        contentDocumentLink.ShareType = 'V';         
        return contentDocumentLink;
    }
    
    /*******************************************************************************************************
    * @description Sets the exchange rates based on values from KantoxRates__mdt
    *              The rate is only set if it is null or zero.
    *              Some of the Bookings are created from Webpages that provide the exchange rate.
    *              In these cases we don't want to overwrite the provided exchange rate.
    *
    *              Invoked from the before insert trigger.
    *
    * @param newList    - a list of Opportunities/Bookings being created
    */
    public static void updateExchangeRates(List<Opportunity> newList){

        // Get the Company's Currency
        List<CurrencyType> currList = [SELECT ISOCode FROM CurrencyType WHERE IsActive = true AND IsCorporate = true];
        String companyCurrency = !currList.isEmpty() ? currList[0].ISOCode : 'EUR';

        // Get the latest exchange as were uploaded from Kantox
        Map<String, KantoxRates__mdt> kRatesByKey = new Map<String, KantoxRates__mdt>();
        List<KantoxRates__mdt> kRates = [SELECT DeveloperName, BaseCurrency__c, ForeignCurrency__c, Rate__c FROM KantoxRates__mdt];
        if(!kRates.isEmpty()){
            for(KantoxRates__mdt kr : kRates){
                kRatesByKey.put(kr.DeveloperName, kr);
            }
        } else {
            System.debug('No Kantox Rates found.');
        }

        // Set Booking's exchange rates
        for(Opportunity opp : newList){
            // Set exchange rate to Provider's currency
            // -- LocalCurrency__c - provider's currency
            // -- BookingExchangeRate__c - exchange to provider's currency

            if(String.isNotBlank(opp.LocalCurrency__c) && (opp.BookingExchangeRate__c == null || opp.BookingExchangeRate__c == 0)) {
                String rateKey = companyCurrency + opp.LocalCurrency__c;
                if (kRatesByKey.containsKey(rateKey)) {
                    opp.BookingExchangeRate__c = kRatesByKey.get(rateKey).Rate__c;
                } else {
                    System.debug('Provider Exchange rate: The current Key '+rateKey+' was not found.');
                }
            } else {
                System.debug('The current Booking LocalCurrency__c (Provider Currency) '+opp.LocalCurrency__c+' was not found.');
            }

            // Set exchange rate to Customer's currency
            System.debug('---OpportunityHelper.updateExchangeRates--1- Customer_Currency: '+opp.Customer_Currency__c+' Customer_Exchange_Rate: '+opp.Customer_Exchange_Rate__c);
            if (String.isNotBlank(opp.Customer_Currency__c)) {
                if (opp.Customer_Exchange_Rate__c == null || opp.Customer_Exchange_Rate__c == 0){
                    String rateKey = companyCurrency + opp.Customer_Currency__c;
                    if (kRatesByKey.containsKey(rateKey)) {
                        opp.Customer_Exchange_Rate__c = kRatesByKey.get(rateKey).Rate__c;
                    } else {
                        System.debug('Customer Exchange rate: The current Key '+rateKey+' was not found.');
                    }
                }
            } else { // If Not Customer_Currency__c is set, assume that it is EUR and set rate to 1.
                opp.Customer_Exchange_Rate__c = 1;
                opp.Customer_Currency__c = 'EUR';
            }

            System.debug('---OpportunityHelper.updateExchangeRates--2- Customer_Currency: '+opp.Customer_Currency__c+' Customer_Exchange_Rate: '+opp.Customer_Exchange_Rate__c);

            if(String.isNotBlank(opp.CurrencyIsoCode)){
                String rateKey = companyCurrency + opp.CurrencyIsoCode;
                if (kRatesByKey.containsKey(rateKey)) {
                    //opp.BookingExchangeRate__c = kRatesByKey.get(rateKey).Rate__c;
                    // FIELD TBD
                } else {
                    //TODO: Log when no key is found
                }
            } else {
                //TODO: Log when no value on CurrencyIsoCode
            }
        }
    }

    /**
     * @description: Calculates and sets the value of AccountsReceivableEuro__c.
     *               Should be called from the before insert & update trigger.
     * 
     *               Note: confusingly the label for this field is: Accounts Payable (Euro), Why?
     * 
     * @Who:         david.sanchez@gmail.com
     * @When:        2022.07.12
     */
    public static void setAccountsReceivableEuro(List<Opportunity> newOpportunities, Map<Id,Opportunity> oldOpportunities) {

        for (Opportunity oppty : newOpportunities) {
            Decimal oldacctRecvLocal = 0;
            Decimal oldBookingExchangeRate = 0;
            if (oldOpportunities != null) {
                Opportunity oldOppty = oldOpportunities.get(oppty.Id);
                oldacctRecvLocal = oldOppty.AccountsReceivableLocalCurrency__c;
                oldBookingExchangeRate = oldOppty.BookingExchangeRate__c;
            }

            if (oppty.AccountsReceivableLocalCurrency__c != null &&
                (oppty.AccountsReceivableEuro__c == null || oppty.AccountsReceivableEuro__c == 0 || 
                 oppty.AccountsReceivableLocalCurrency__c != oldacctRecvLocal || oppty.BookingExchangeRate__c != oldBookingExchangeRate)) {
                    Decimal xrate = (oppty.BookingExchangeRate__c == null || oppty.BookingExchangeRate__c == 0) ? 1 : oppty.BookingExchangeRate__c;
                    oppty.AccountsReceivableEuro__c = oppty.AccountsReceivableLocalCurrency__c / xrate;
                }
        }
    }

    /**
     * When the stage of the Booking is set to "Sent", save the CostCamperDays__c into OriginalCostCamperDays__c.
     * This value will be needed for the Cancellation Invoice if the Booking is cancelled before any "Booking Change" is performed.
     */
    public static void onSentSaveOriginalCostCamperDays(List<Opportunity> newList, Map<Id, Opportunity> oldMap) {

        for (Opportunity booking : newList) {
            if (booking.StageName == 'Sent' && booking.StageName != oldMap.get(booking.Id).StageName) {
                booking.OriginalCostsCamperDaysAuto__c = booking.CostsCamperDays__c;
            }
        }
    }

    /**
     * Sends a confirmation emails to the customer when a booking it is fully paid.
     * A Booking is fully paid when the TotalAmountpaidbyCustomerEUR__c is greater or equal to CostsCamperDays__c.
     * The TotalAmountpaidbyCustomerEUR__c is a rollup value from the payments received for the booking.
     */
    public static void sendReceiptPaymntConfirm(List<Opportunity> newList, Map<Id, Opportunity> oldMap) {

        Set<Id> opptyIds = new Set<Id>();   // IDs of Opportunities to which emails should be sent
        for (Opportunity oppty : newList) {
            Decimal oldTotalAmountPaid = oldMap != null ? oldMap.get(oppty.Id).TotalAmountpaidbyCustomerEUR__c : 0;

            if (oppty.PaymentMethod__c == 'Invoice' && !oppty.IsPaymentConfirmation_Sent__c && 
                oppty.CostsCamperDays__c != 0 && oppty.TotalAmountpaidbyCustomerEUR__c != oldTotalAmountPaid
                && (oppty.StageName == 'Sent Phone' || oppty.StageName == 'Booking Changed' || oppty.StageName == 'Sent')) {
                if (oppty.CostsCamperDays__c <= oppty.TotalAmountpaidbyCustomerEUR__c) {
                       opptyIds.add(oppty.Id);
                }
            }
        }

        if (!opptyIds.isEmpty()) {
            sendEmailsForPaymentConfirmantion(opptyIds);
        }
    }

    @future(callout=true)
    private static void sendEmailsForPaymentConfirmantion(Set<Id> opptyIds) {
        
        if (!opptyIds.isEmpty()) {
            EmailTemplate emailTemplate =[SELECT Id FROM EmailTemplate WHERE developername = 'PaymentReceiptConfirmation'];
            List<Messaging.SingleEmailMessage> emailsList = new List<Messaging.SingleEmailMessage>();
            List<Opportunity> opportunities = new List<Opportunity>();
            for (Opportunity oppty : [SELECT Id, IsPaymentConfirmation_Sent__c, Customer__r.PersonContactId, Customer__r.PersonEmail, LanguageCodeAuto__c 
                                      FROM   Opportunity WHERE Id IN :opptyIds]) {
                                          
                Messaging.SingleEmailMessage email = preparePaymtRecptEmail(oppty.Customer__r.PersonEmail, oppty.Id, oppty.Customer__r.PersonContactId, 
                                                                            emailTemplate.Id, oppty.LanguageCodeAuto__c);
                emailsList.add(email);

                oppty.IsPaymentConfirmation_Sent__c = true;
                opportunities.add(oppty);
            }

            update opportunities;

            if(!Test.isRunningTest()) {
                 Messaging.sendEmail(emailsList); 
            }
        }
    }

    /*******************************************************************************************************
    * @description Prepares Payment Receipt Confirmation email for each booking
    * @param relatedId booking id
    * @param recipientId contact id
    * @param templateId template id
    */
    public static Messaging.SingleEmailMessage preparePaymtRecptEmail(String customerEmailAddress, Id relatedId, Id recipientId, String templateId, String languageCode) {
        
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setTemplateId(templateId);
        email.setTargetObjectID(recipientId);
        email.setWhatID(relatedId);
        email.setOrgWideEmailAddressId(System.Label.get('','BookingEmailWideAddressId_ALL',languageCode));
        return email;
    }
}
Editor is loading...
Leave a Comment