Всем привет!
Столкнулся с логической задачей, совсем не оригинальной, уверен, что многим знакома, но не могу кратко ее описать в двух словах (поэтому название темы получилось таким загадочным), так что, как говорится в индийском кино "долго объяснять - давайте станцую":
Есть три связанных объекта, образующих три уровня, причем нижние уровни не являются чайлд записями верхних объектов, просто релайтед записи через обычный лукап.
Каждый объект имеет флаг-чекбокс "Завершено", который выставляется в тру с помощью тригера в случае если:
(1) требуемые поля на объекте заполнены и
(2) все связанные записи низлежащего уровня тоже "Завершены" (там аналогичный чек-бокс)
И вот в чем штука: тригеры работаю только с записями самого объекта. И в чем же проблема?
А вот в чем: у записи были не заполнены требуемые поля, и вот юзер их заполняет. Сохраняет. Но запись все еще не "Завершена", потому что одна из низлежащих записей все еще не "Завершена". Ну да не беда, идем ниже и "завершаем" ту запись. И казалось бы вот и все: все рилейтед записи готовы, у верхней записи тоже все поля заполнены. Но ведь нет: чекбокс на верхней записи все еще в фальсе, показывает что запись еще не "Завершена"... Почему? Да потому что когда мы Завершили низлежащую запись, ее апдейт не вызвал апдейт верхней записи, и там все еще по-старому...
Т.е. в тригере низлежащего объекта нужно провоцировать тригер вышележащего объекта, чтоб тот сделал свое дело, и так по цепочке вверх.
Ну и вопрос, вы не сталкивались с такой ситуацией, может есть какие светлые идеи, бэст практис на тему?
Всем привет! Столкнулся с логической задачей, совсем не оригинальной, уверен, что многим знакома, но не могу кратко ее описать в двух словах (поэтому название темы получилось таким загадочным), так что, как говорится в индийском кино "долго объяснять - давайте станцую": Есть три связанных объекта, образующих три уровня, причем нижние уровни не являются чайлд записями верхних объектов, просто релайтед записи через обычный лукап. Каждый объект имеет флаг-чекбокс "Завершено", который выставляется в тру с помощью тригера в случае если: (1) требуемые поля на объекте заполнены и (2) все связанные записи низлежащего уровня тоже "Завершены" (там аналогичный чек-бокс) И вот в чем штука: тригеры работаю только с записями самого объекта. И в чем же проблема? А вот в чем: у записи были не заполнены требуемые поля, и вот юзер их заполняет. Сохраняет. Но запись все еще не "Завершена", потому что одна из низлежащих записей все еще не "Завершена". Ну да не беда, идем ниже и "завершаем" ту запись. И казалось бы вот и все: все рилейтед записи готовы, у верхней записи тоже все поля заполнены. Но ведь нет: чекбокс на верхней записи все еще в фальсе, показывает что запись еще не "Завершена"... Почему? Да потому что когда мы Завершили низлежащую запись, ее апдейт не вызвал апдейт верхней записи, и там все еще по-старому... Т.е. в тригере низлежащего объекта нужно провоцировать тригер вышележащего объекта, чтоб тот сделал свое дело, и так по цепочке вверх. Ну и вопрос, вы не сталкивались с такой ситуацией, может есть какие светлые идеи, бэст практис на тему?
Из того что ты написал, ты сам ответил на свой вопрос. Да нужно каскадное срабатывание триггеров, но проблема в этом что все эти срабатывания могут зайти в цикл. Поэтому нужно очень аккуратно проверять условия срабатывания триггеров и фильтровать требуемые записи.
Из того что ты написал, ты сам ответил на свой вопрос. Да нужно каскадное срабатывание триггеров, но проблема в этом что все эти срабатывания могут зайти в цикл. Поэтому нужно очень аккуратно проверять условия срабатывания триггеров и фильтровать требуемые записи.
Через future или scheduler такие моменты придется решать.
Или разруливать последовательность срабатывания триггера через глобальные переменные.
Вот в твое случае так как триггер сперва сработал на паренте, выставил все нужные поля и еще вдобавок обновил детей, то логичнее было бы запретить срабатываение триггера на детях, так как ты и так уже все выставляешь как нужно в триггере на родителе.
Через future или scheduler такие моменты придется решать. Или разруливать последовательность срабатывания триггера через глобальные переменные. Вот в твое случае так как триггер сперва сработал на паренте, выставил все нужные поля и еще вдобавок обновил детей, то логичнее было бы запретить срабатываение триггера на детях, так как ты и так уже все выставляешь как нужно в триггере на родителе.
Можно через организовать класс в котором будет хэндлится твой кейс.
типа такого:
public with sharing class TriggerStateMachine {
public static Boolean TRIGGER_A_DISABLED = false;
public static Boolean TRIGGER_B_DISABLED = false;
public static Boolean TRIGGER_C_DISABLED = false;// Only 1 constructor should be used.
public TriggerStateMachine(Sobject p_object) {
if(p_object type of ARecord__c) {
handleA(p_object);
} else if(p_object type of BRecord__c) {
handleB(p_object);
} else if(p_objec type of CRecord__c) {
handleC(p_object);
}
}private void handleA(Sobject p_object) {
if (TRIGGER_A_DISABLED) return;//some stuff
// просто вырубить рекурсию
TRIGGER_A_DISABLED = true;
handleB(p_object);
}private void handleB(Sobject p_object) {
if (TRIGGER_B_DISABLED) return;// просто вырубить рекурсию
TRIGGER_B_DISABLED = true;
handleC(p_object);
}private void handleC(Sobject p_object) {
if (TRIGGER_C_DISABLED) return;// просто вырубить рекурсию
TRIGGER_C_DISABLED = true;
handleA(p_object);
}
}
ps: класс гавно, я знаю, есть более крутые решения
Можно через организовать класс в котором будет хэндлится твой кейс. типа такого: [code] public with sharing class TriggerStateMachine { public static Boolean TRIGGER_A_DISABLED = false; public static Boolean TRIGGER_B_DISABLED = false; public static Boolean TRIGGER_C_DISABLED = false; // Only 1 constructor should be used. public TriggerStateMachine(Sobject p_object) { if(p_object type of ARecord__c) { handleA(p_object); } else if(p_object type of BRecord__c) { handleB(p_object); } else if(p_objec type of CRecord__c) { handleC(p_object); } } private void handleA(Sobject p_object) { if (TRIGGER_A_DISABLED) return; //some stuff // просто вырубить рекурсию TRIGGER_A_DISABLED = true; handleB(p_object); } private void handleB(Sobject p_object) { if (TRIGGER_B_DISABLED) return; // просто вырубить рекурсию TRIGGER_B_DISABLED = true; handleC(p_object); } private void handleC(Sobject p_object) { if (TRIGGER_C_DISABLED) return; // просто вырубить рекурсию TRIGGER_C_DISABLED = true; handleA(p_object); } } [/code] ps: класс гавно, я знаю, есть более крутые решения
В принципе не должно быть проблем реализовать каскадное обновление снизу вверх, если только у вас нет или не будет петли обновления сверху вниз. Я бы логику выставления флага реализовал непосредственно в триггерах и из нижних объектов просто дёргал апдейт верхних, а триггер вверху всё выполнит сам.
В принципе не должно быть проблем реализовать каскадное обновление снизу вверх, если только у вас нет или не будет петли обновления сверху вниз. Я бы логику выставления флага реализовал непосредственно в триггерах и из нижних объектов просто дёргал апдейт верхних, а триггер вверху всё выполнит сам.