Моя структура данных:

Возможность -> SH_Quote__c -> SH_Quote_Line__c

Триггер Apex на уровне SH_Quote_Line__c

public without sharing class QuotationLineItemController {
  //After Update Trigger Handler
  public static void AfterUpdate (List<SH_Quote_Line__c> records){

    if(records.size() > 0){
        Boolean checkResponded = True;

        for (SH_Quote_Line__c ql : records){
            SH_Quote__c quoteObj = [SELECT Id, Status__c, OwnerId, OwnerEmail__c FROM SH_Quote__c WHERE Id = :ql.SH_Quote__c ];

            List<SH_Quote_Line__c> quoteLineObjs = new List<SH_Quote_Line__c>(
                [SELECT id, Name, Status__c FROM SH_Quote_Line__c WHERE SH_Quote__c = :quoteObj.id]);

            for(SH_Quote_Line__c child : quoteLineObjs){
                System.debug('quoteLineObjs.id =  ' +child.id);
                System.debug('quoteLineObjs.Status__c  =  ' +child.Status__c);

                if(child.Status__c == 'Notified'){
                    checkResponded &= False;
                    quoteObj.Status__c = 'Notified';
                }

                if(child.Status__c == 'In Progress') {
                    checkResponded &= False;
                    quoteObj.Status__c = 'In Progress';
                }

                if(child.Status__c == 'Responded'){
                    checkResponded &= True;
                    quoteObj.Status__c = 'In Progress';
                }

                System.debug('checkResponded =  ' +checkResponded);
            }

            System.debug('checkResponded FINAL =  ' +checkResponded);

            if (checkResponded == True){
                quoteObj.Status__c = 'Completed';

            }
            update quoteObj;

            quoteObj = [SELECT Id, Status__c, OwnerId, OwnerEmail__c FROM SH_Quote__c WHERE Id = :ql.SH_Quote__c ];

            if (quoteObj.Status__c == 'Completed'){

                List<EmailTemplate> lstEmailTemplates = [SELECT Id, Body, Subject from EmailTemplate where DeveloperName = 'Quotation_VF_Notify_Email'];

                //Pick any dummy contact
                Contact c = [select id, Email from Contact where Email != null limit 1];

                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                mail.setTemplateId(lstEmailTemplates[0].Id);
                mail.setSaveAsActivity(false);
                mail.setTargetObjectId(c.id); //Set to dummy contact
                mail.setTreatTargetObjectAsRecipient(false); //dont send to dummy contact
                mail.setToAddresses(new String[] {quoteObj.OwnerEmail__c}); //Send to Quotation Owner under additional email
                mail.setWhatId(quoteObj.id);
                System.debug('quoteObj :' + quoteObj); 

                Messaging.SendEmailResult[] resultMail = Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
                System.debug('SendEmailResult :' + resultMail); 
            }
       }
    }    
}

Чтобы кратко объяснить, что делает мой код, он находится на уровне SH_Quote_Line__c: когда я обновляю запись таким образом, что quoteObj.Status__c = 'Completed', я хочу, чтобы он отправил электронное письмо владельцу SH_Quote__c.

Эта логика триггера отлично работает как системный администратор, однако, когда я использую другой профиль (профиль менеджера по продажам) для обновления записи в SH_Quote_Line__c, я получаю следующую ошибку:

Мы не можем сохранить эту запись, потому что процесс «[SH_Quote_Line_c] Обновить логику статуса» завершился неудачно. Сообщите администратору Salesforce эти данные. Эта ошибка возникла, когда поток попытался обновить записи: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY: QuoteLineTrigger: выполнение AfterUpdate, вызванное: System.EmailException: Ошибка SendEmail. Первое исключение в строке 0; первая ошибка: INVALID_CROSS_REFERENCE_KEY, недопустимый идентификатор перекрестной ссылки: [] Class.QuotationLineItemController.AfterUpdate: строка 101, столбец 1 Trigger.QuoteLineTrigger: строка 30, столбец 1

Мой профиль менеджера по продажам имеет разрешения на чтение и редактирование всех трех объектов, поэтому я не могу понять, почему это не работает для этого конкретного профиля???

Заметка:

  1. Если я раскомментирую "mail.setWhatId(quoteObj.id);" ошибки нет, но он просто отправляет электронное письмо без каких-либо полей слияния, прикрепленных к моему шаблону электронной почты VF.
  2. Я пытался использовать правила Process Builder и Workflow для отправки электронного письма, когда Status__c = 'Completed', но я все еще получаю ту же ошибку (вот почему я думал, что вершина может помочь - кажется, это не так)
0
compski 4 Дек 2019 в 19:16
Добавьте кнопку «Общий доступ» к предложению и посмотрите, была ли эта запись доступна для менеджера по продажам (явно или неявно через правила общего доступа)
 – 
cropredy
4 Дек 2019 в 19:48
Кажется, я не могу сделать кнопку общего доступа видимой в записи сведений о цитате даже при добавлении в «Действия Salesforce Mobile и Lightning Experience».
 – 
compski
4 Дек 2019 в 20:01
Это кнопка страницы подробностей; переключитесь на классику, если вам нужно
 – 
cropredy
4 Дек 2019 в 20:51
Но почему вы рекомендуете этот метод? это должно быть распространено на 200 пользователей, я не могу просто поделиться каждой цитатой таким же образом
 – 
compski
5 Дек 2019 в 04:17
1
Я рекомендую кнопку "Поделиться" в качестве диагностического инструмента, чтобы вы могли настроить свою модель обмена.
 – 
cropredy
5 Дек 2019 в 05:59

1 ответ

Лучший ответ

В организационной иерархии, поскольку и Quote, и Quote Line были установлены как частные. Пришлось использовать управляемый общий доступ Apex для выполнения триггера «После вставки» на уровне строки цитаты, чтобы позволить пользователям, не являющимся администраторами, отправлять электронную почту, иначе «mail.setWhatId (quoteObj.id)» будет жаловаться.

public static void grantUserQuoteRecordAccessRight(List<SH_Quote_Line__c> objlist){

List <SH_Quote_Line__Share> qlObjList = new List<SH_Quote_Line__Share>();
List <SH_Quote__Share> quoteShareList = new List<SH_Quote__Share>();

for(SH_Quote_Line__c ql : objlist){
    SH_Quote__c quoteObj = [SELECT Id, Status__c, OwnerId, OwnerEmail__c FROM SH_Quote__c WHERE Id = :ql.SH_Quote__c ];

    for(User user : [Select Id, email, Name, UserRole.Name, Username from user where IsActive=true and Id != :ql.OwnerId]){
        SH_Quote_Line__Share qlObj = new SH_Quote_Line__Share();
        SH_Quote__Share qObj = new SH_Quote__Share(); 

        qlObj.ParentId = obj.Id;
        qlObj.AccessLevel = 'Edit';
        qlObj.RowCause = Schema.SH_Quote_Line__Share.RowCause.Manual;
        qlObj.UserOrGroupId = user.Id;
        qlObjList.add(qlObj);

        qObj.ParentId = obj.Id;
        qobj.AccessLevel = 'Edit';
        qobj.RowCause = Schema.SH_Quote__Share.RowCause.Manual;
        qObj.UserOrGroupId = user.Id;
        quoteShareList.add(qObj);
    }
}
try{
    Database.SaveResult[] result = Database.Insert(newobjlist, false);
    Database.SaveResult[] result1 = Database.Insert(quoteShareList, false);
}catch(Exception ex){
    System.debug(' ######## ERROR ############ ' + ex.getMessage());
    System.debug(' ######## ERROR ############ ' + ex.getStackTraceString());
} 
}
0
compski 5 Дек 2019 в 11:45