Вот пример джоба :
X++:
// GRD_R4453_InventSumInventTrans_pkoz, Разъехались остатки в InventSum и проводки в InventTrans., pkoz, 04.11.2011
static void Job791_15(Args _args)
{
#define.recIdTrans(5637151827)
InventTrans InventTrans;
InventSum InventSum;
;
try
{
ttsBegin;
try
{
InventTrans = InventTrans::findRecId(#recIdTrans, true);
InventTrans.Qty += 1.0;
InventTrans.Update();
breakpoint;
}
catch
{
info("поймали внутрений catch");
}
ttsCommit;
}
catch
{
info("поймали внешний catch");
}
info(strFMT("InventTrans.RowCount() = %1, InventTrans.Qty = %2, InventSum : %3 ",
InventTrans.RowCount(),
InventTrans.Qty,
con2str(Global::buf2Con(InventSum::find(InventTrans.ItemId, InventTrans.inventDimId)), "; ")
));
}
Использовать так :
1. Проставляем в джобе recID существующей проводки InventTrans
2. Открываем 2 клиента аксапты.
3. Запускаем джоб на 1-м клиенте, ждем когда он выпадет в отладчик на точке останова.
4. Запускаем тот же джоб на 2-м клиенте. Он повиснет.
5. Жмем F5 в отладчике на 1-м клиенте.
В итоге после выполнения джоба на обоих клиентах, получаем что InventTrans.Qty увеличилось на 1, а соответствующая колонка в InventSum увеличилась на 2.
Причина такого поведения, в том что мы просто поставили обработку catch в которую попадают исключения
Exception::UpdateConflict
и
Exception:
uplicateKeyException
которые в свою очередь не откатывают транзакцию.
Поэтому чтобы избегать в модификациях подобных ошибок, нельзя ставить обработку catch без указания вида исключения.
Ну или по крайней мере перед ней всегда делать обработку вышеупомянутых типов исключений.
Для удобства, я завел макрос Catch_DangerousException, который можно везде вставлять в блок обработки.
Текст макроса
X++:
catch (Exception::UpdateConflict)
{
if (appl.ttsLevel() == 0)
{
if (xSession::currentRetryCount() >= 5)
{
throw Exception::UpdateConflictNotRecovered;
}
else
{
retry;
}
}
else
{
throw Exception::UpdateConflict;
}
}
catch (Exception::DuplicateKeyException)
{
// info(strfmt("@SYS123267", ));
if (appl.ttsLevel() != 0)
{
throw Exception::DuplicateKeyExceptionNotRecovered;
}
}
Упомянутый в заголовке темы пример следует переписать так :
X++:
...
ttsBegin;
try
{
// здесь расположен код обновляющий InventTrans - например комплектация или резервирование
}
catch(Exception::Error)
{
// обработка
}
#catch_DangerousException
catch
{ // сюда попадает обработка конфликта обновления записи
// ничего не делаем
}
ttsCommit;
...