3.4.0 XRM
Ситуация следующая:
При сохранении карточки (добавление\обновление) надо было создавать и обновлять, если уже было создано, заранее неизвестное количество операций (одна или две или три - в зависимости от значения поля). Я решил эти так:
сделал триггер After Update на tbl_Task, который удаляет все связанные с задачей (и "созданные автоматически" - поле в tbl_Cashflow) операции, а на AfterPost датасета задач добавил функцию добавления операций.
Все работает нормально, но есть 1-10% записей для которых операции отсутствуют. То ли не создаются, то ли удаляются - т.е. такое ощущение, что сначала отрабатывает AfterPost датасета - создает операции, а потом отрабатывает триггер (из-за каких-нибудь гипотетических тормозов сервера) - удаляет операции.
Нагрузка на сервер маленькая, если не минимальная.
Вопросы:
1) Возможна ли ситуация когда триггер After Update отрабатывает после AfterPost датасета?
2) если да, то есть ли еще варианты, как это решить, кроме как создавать доп. поля в задаче для записи ИД созданных операций или обновления\удаления записей по ним
Нравится
Там еще такой важный момент был, что задачу мог изменять, например, директор и тогда операции создавались от его имени и с урезанными для менеджеров правами - в результате чего менеджеры их не видели и у них создавались дубликаты этих операций. Именно поэтому я перенес логику на БД
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(); ?
мне его читать не надо - есть только 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: