события датасета
триггер
Технические вопросы
Разработка

Триггер и скрипт датасета - кто первый? :)

3.4.0 XRM
Ситуация следующая:

При сохранении карточки (добавление\обновление) надо было создавать и обновлять, если уже было создано, заранее неизвестное количество операций (одна или две или три - в зависимости от значения поля). Я решил эти так:
сделал триггер After Update на tbl_Task, который удаляет все связанные с задачей (и "созданные автоматически" - поле в tbl_Cashflow) операции, а на AfterPost датасета задач добавил функцию добавления операций.
Все работает нормально, но есть 1-10% записей для которых операции отсутствуют. То ли не создаются, то ли удаляются - т.е. такое ощущение, что сначала отрабатывает AfterPost датасета - создает операции, а потом отрабатывает триггер (из-за каких-нибудь гипотетических тормозов сервера) - удаляет операции.
Нагрузка на сервер маленькая, если не минимальная.

Вопросы:
1) Возможна ли ситуация когда триггер After Update отрабатывает после AfterPost датасета?
2) если да, то есть ли еще варианты, как это решить, кроме как создавать доп. поля в задаче для записи ИД созданных операций или обновления\удаления записей по ним

Нравится

22 комментария

Там еще такой важный момент был, что задачу мог изменять, например, директор и тогда операции создавались от его имени и с урезанными для менеджеров правами - в результате чего менеджеры их не видели и у них создавались дубликаты этих операций. Именно поэтому я перенес логику на БД

1. Такого быть не может. Скорее всего ошибка в самом триггере (в запросе на удаление). По умолчанию триггер работает в рамках одной транзакции, т.е. или он отрабатывает полностью или не отрабатывает вообще (вываливается с ошибкой). Смотрите в тело триггера.

"Евгений Либин" написал:триггер работает в рамках одной транзакции

да, действительно, что это я :lol:

однако в триггере "ничего такого":
[sql]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[tr_tbl_Task_AU] ON [dbo].[tbl_Task]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare @TaskID uniqueidentifier
select @TaskID = [INSERTED].[ID]
from [INSERTED]

DELETE FROM [dbo].[tbl_Cashflow]
WHERE([tbl_Cashflow].[TaskID] = @TaskID AND
[tbl_Cashflow].[AutoCreated] = 1)

END
[/sql]

в карточке редактирования тоже все предельно просто:
[javascript]
function dlDataOnDatasetAfterPost(Dataset) {
CopyContactInTasksIfNeed(Dataset);
//ProcessSendMailMessageForTask(Dataset); //отключено
CreateContactsInTask(Self, Dataset);

CreateCashflowByTask(dlData.Dataset); // вот это
}
[/javascript]
Не знаю, почему там используется даталинк - наверно, просто как следствие копипаста). Вряд ли это могло вызвать эту проблему, исправил.

в самой функции, если убрать все лишнее и заполнение значений тоже все довольно просто:
[javascript]
function CreateCashflowByTask(TaskDataset) { // на апдейт задачи стоит tr_tbl_Task_AU (удаляет старые операции)
var TaskCode = GetTaskTypeCodeByID(TaskDataset.ValAsGUID('TypeID'));
var CashflowDataset = Services.GetNewItemByUSI('ds_Cashflow'); // сюда наверно лучше поставить GetSingleItemByCode()

CashflowDataset.DataFields.ItemsByName('DebtorCreditorID').IsRequired = false;
CashflowDataset.Append();
FillCashFlowDatasetValuesByTask(CashflowDataset, TaskDataset, false, TaskCode);

CashflowDataset.Post();
CashflowDataset.Close();
}
[/javascript]

В результате, по-прежнему не понимаю, почему может так происходить.
Причем воспроизвести сохранение задачи без формирования операции у меня пока не получилось. Не знаю, что и делать - не сидеть же мне и не клепать записи задач пока не получу такой случай...

А что внутри FillCashFlowDatasetValuesByTask?
Похоже что туда надо смотреть, т.е. триггер отработал а FillCashFlowDatasetValuesByTask нет.

если прочитаете, то вот ))
если коротко - там просто заполняются значения, все действия происходят вне функции
[javascript]
function FillCashFlowDatasetValuesByTask(CashflowDataset, TaskDataset, HeadPhones, TaskCode) {
var ClauseID = GetCashflowClauseIDByCode(TaskCode);
var ExpenseTypeID;
if ((TaskCode == 'Bus') /*&& IsEmptyGUID(TaskDataset.ValAsGUID('OpportunityID'))*/) {
ExpenseTypeID = GetCashflowExpenseTypeIDByCode(TaskCode);
} else
/*if (TaskDataset.ValAsBool('OrderByGuide')){
ExpenseTypeID = GetCashflowExpenseTypeIDByCode('drugoe');
} else*/ {
ExpenseTypeID = GetCashflowExpenseTypeIDByCode('Tour');
}
if (TaskDataset.ValAsBool('IsCash') || TaskCode == 'Bus' || HeadPhones) {
CashflowDataset.ValAsGUID('CashAccountID') = '{4FAFDE39-D5EB-4309-BAFD-9C3DA1FCF0EB}'; // наличная касса
} else {
CashflowDataset.ValAsGUID('CashAccountID') = '{ADEC1E41-17EE-4B02-B04E-D677DEA48D39}'; // расчетный счет
}

if (TaskDataset.ValAsGUID('OpportunityID')) {
CashflowDataset.ValAsGUID('OpportunityID') = TaskDataset.ValAsGUID('OpportunityID');
}CashflowDataset.ValAsGUID('StatusID') = '{FDEA47BE-53FE-4730-BF4F-4F44C3B5D61A}'; // выполнена
if (!HeadPhones) {
CashflowDataset.ValAsStr('Subject') = TaskDataset.ValAsStr('Title');
} else {
CashflowDataset.ValAsStr('Subject') = TaskDataset.ValAsStr('Title') + ' наушники';
}
//var IsTop = GetIsUserInGroup(Connector.CurrentUser.Name, 'УЧРЕДИТЕЛИ');
CashflowDataset.ValAsBool('UseAsCashflow') = false;
CashflowDataset.ValAsBool('UseAsPandL') = true;
CashflowDataset.ValAsBool('AutoCreated') = true;
CashflowDataset.ValAsBool('ForFlow') = true;
CashflowDataset.ValAsGUID('ClauseID') = ClauseID;
CashflowDataset.ValAsGUID('ExpenseTypeID') = ExpenseTypeID; // группы
CashflowDataset.ValAsGUID('CurrencyID') = TaskDataset.ValAsGUID('CurrencyID');
if (!HeadPhones) {
CashflowDataset.ValAsFloat('Amount') = TaskDataset.ValAsFloat('FullAmount');
} else {
CashflowDataset.ValAsFloat('Amount') = TaskDataset.ValAsFloat('HeadphonesAmount');
}
CashflowDataset.ValAsGUID('PayerID') = Connector.CurrentUser.AccountID;
CashflowDataset.ValAsFloat('AutocalcAmount') = true;
if (!HeadPhones) {
CashflowDataset.Values('RecipientID') //= CashflowDataset.Values('DebtorCreditorID')
= TaskDataset.Values('SupplierID');
} else {
CashflowDataset.Values('RecipientID') //= CashflowDataset.Values('DebtorCreditorID')
= TaskDataset.Values('Supplier2ID');
}
//SetBasicPriceInDataset(CashflowDataset, 'BasicAmount', 'Amount', 'CurrencyID', 'CurrencyRate');
CashflowDataset.ValAsGUID('TaskID') = TaskDataset.ValAsGUID('ID');
CashflowDataset.DisableEvents();
CashflowDataset.ValAsDateTime('ActualDate') = CashflowDataset.ValAsDateTime('EstimatedDate') = TaskDataset.ValAsDateTime('StartDate');
CashflowDataset.EnableEvents();
return CashflowDataset;
}
[/javascript]

на всякий случай вот полный текст функций
createcashflowbytask.txt

На первый взгляд всё ОК.
Проверьте 2 пункта
1. Посмотрите напрямую из базы [sql] Select * from tbl_CashFlow Where TaskID = 'Нужный ID задачи'

[/sql]
2. Если вышеуказанный запрос вернет данные, то посмотрите на sq_CashFlow возможно где-то inner join или в where доп. условия.

"Евгений Либин" написал:Проверьте 2 пункта

1. нет
2. нет(

А где CashflowDataset.Open(); ?

"Евгений Либин" написал:А где CashflowDataset.Open(); ?

мне его читать не надо - есть только Append() в CreateCashflowByTask
"SQK" написал:Если набор данных не находится в состоянии добавления записи (значение свойства IDataset::State не равно "dstInsert"), и у текущего пользователя есть права на добавление записи (значение свойства IDataset::CanInsert равно "True"), то вызывает событие IDatasetEvents::OnDatasetBeforeAppend. Иначе метод завершает работу.

Я бы рекомендовал всегда перед добавление делать открытие датасета, тем более есть такая строка [sql]
CashflowDataset.DataFields.ItemsByName('DebtorCreditorID').IsRequired = false;
[/sql].
Для ускорения можно делать фильтр по любому левому ID [javascript]
ApplyDatasetFilter(CashflowDataset, 'ID', GUID_NULL, true);
[/javascript]
Обращения по ID моментальное, набор данных будет пустой и датасет будет проинициализирован.

Спасибо, Евгений, воспользуюсь Вашими советами, хотя и не понимаю, для чего мне открывать датасет (на Before\After Open ничего нет)

Тут смысл не в отработке событий, а в инициализации самого датасета.

Здравствуйте, Дмитрий!
Помогли ли советы Евгения по открытию DataSet'а?
В данном случае также возможны следующие вариант, что операции создаются, но не привязываются к записям.
Попробуйте включить логирование и при возникновении проблемы проверьте, добавлялись ли записи вообще.

"Андрей Каспаревич" написал:Помогли ли советы Евгения по открытию DataSet'а?

Пока не знаю. Я дописал открытие с фильтрацией. Через какое-то время посмотрим, будет ли повторяться ситуация.

"Андрей Каспаревич" написал:операции создаются, но не привязываются к записям

почему? Не вижу причин, почему ИД задач и продаж могли бы не подставляться в скрипте. Кроме того я бы видел разницу между результатами запросов в SQL и в Террасофт

"Андрей Каспаревич" написал:
Попробуйте включить логирование и при возникновении проблемы проверьте, добавлялись ли записи вообще.

Так и сделал))

Через 2 недели - месяц отпишусь, что все ок, либо раньше - что нет

А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?

"Борисов Михаил Евгеньевич" написал:

А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?


триггеру можно)

"Андросов Дмитрий" написал:
Борисов Михаил Евгеньевич пишет:

А делать DELETE FROM [dbo].[tbl_Cashflow] можно под всеми пользователями?

триггеру можно)


Не понял, как? Тригер срабатывает в рамках транзакции где пошел update. Он будет обращаться к таблицам от имени того пользователя от которого пошел update.

"Борисов Михаил Евгеньевич" написал:Он будет обращаться к таблицам от имени того пользователя от которого пошел update

вот только пользователи работают с представлениями (и их триггерами), а я сделал на таблицу

"Андросов Дмитрий" написал:
Борисов Михаил Евгеньевич пишет:

Он будет обращаться к таблицам от имени того пользователя от которого пошел update

вот только пользователи работают с представлениями (и их триггерами), а я сделал на таблицу


У меня только один вопрос: от имени какого пользователя открыта транзакция в тригере таблицы? Что возращает suser_name(): supervisor, vasay или еще что то? Я подозреваю, что vasay и прав у него ровно столько сколько дали через grant.

На сколько я понял - проблем с удалением записей нет. Вопрос только с созданием новых. Если речь идет о контексте выполнения триггера, то нужно для начала уточнить версию СУБД. Разные СУБД и даже разные версии СУБД по разному используют (не используют) контекст.

Проблема была в том, что пользователи использовали массовое редактирование записей (делается через датасет), а создание операций повешено на скрипт карточки....

зато заглавный вопрос прояснил :wink:

Показать все комментарии