Методика оперативного проведения и управляемые блокировки в 1С:Предприятие 8.3
(бесплатная статья по Программированию в 1С)

Другие статьи программированию:

«Как в 1С подготовить информационную базу у клиента»

«Назначение обработчиков событий с помощью подписок на события»

«Новая методика контроля отрицательных остатков при проведении документов»

Для тех, кто готовится к Аттестации на 1С:Специалист по Платформе

Публикуем статью автора и ведущего курса по подготовке к Аттестации на 1С:Специалист по Платформе Павла Чистова

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

«Методика оперативного проведения, управляемые блокировки в 1С:Предприятие 8.3»

Введение

Несмотря на то, что у меня уже есть статья, посвященная механизму оперативного проведения, очень часто… нет не так, очень-очень-очень часто спрашивают, что это такое.

Я подумал и решил написать ну очень-очень-очень подробную статью про методику оперативного проведения и управляемые блокировки. А без управляемых блокировок, собственно, методику использовать бессмысленно.

При написании статьи, демопример я вводил на платформе 8.3.3.641. Всю задачу создавал на пустой базе.

Постановка задачи

Необходимо автоматизировать 2 операции: покупка, продажа товаров. При продаже товаров необходимо контролировать наличие товаров в остатках предприятия и рассчитывать себестоимость списания товаров по методу FIFO.

Начнем…

Структура метаданных:

Справочник: Товары

Документы: Приходная и Расходная с табличными частями «Список товаров» (товар, количество, сумма).

Регистры: ОстаткиТоваров, в регистре одно измерение: Товар, и один ресурс: Количество.

Назначение регистра ¬– быстрое принятие решения, можно ли проводить документ.

СтоимостьТоваров, в регистре два измерения: Товар, Партия, и два ресурса: Количество, Стоимость.

Назначение регистра: хранение информации о стоимости остатков товаров в разрезе партий.

Описывать проведение документа «Приходная» не буду, там все очевидно, все можно конструктором сделать.

В расходной опишем проведение по регистру ОстаткиТоваров…

Методика оперативного проведения

Под термином «Методика оперативного проведения» я понимаю следующее: при проведении документа, вместо того, чтобы, сформировать запрос и понять хватает ли товаров или нет, мы сначала сформируем движения, а затем уже проверим, не ушли ли мы в минус? Подробнее с плюсами и минусами этой технологии можно познакомиться в моей прошлой статье.

Опишем формирование движений в документе «Расходная»:

Обработка проведения

Прокомментируем текст модуля:

1. Устанавливаем маркер необходимости записи движений. Это необходимо делать в том случае, если у документа установлено свойство «Записывать выбранные».

Запись движений при проведении

Что дает нам это свойство?

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

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

К чему это приведет?

Правильно, к уменьшению блокировок таблиц и к сокращению времени транзакций при проведении.

2. Очищаем движения. Зачем? Все дело в еще одном новом свойстве документов «Удалять движения».

Удаление движений

Для увеличения нажмите на изображение.

При установленном свойстве «Удалять автоматически» в самом начале транзакции проведения система автоматически записывает пустые наборы записей в регистры, тем самым очищая старые движения. Это физически происходит запись со всеми вытекающими транзакциями и блокировками.

Для облегчения нагрузки на таблицы баз данных теперь есть возможность не очищать автоматом старые движения…

И тут обычно я слышу: «Так и всегда можно было «Не очищать автоматически»!

Ага, можно было, но в каждом документе приходилось описывать очистку каждого набора записей при отмене проведения… Удобно…

Короче, 1С пошла на поводу у здравого смысла и сделала новое свойство «Удалять автоматически при отмене проведения». Это гарантирует нам, что все движения документа будут очищены в том случае, если у документа отработает событие «ОтменаПроведения».

Теперь вопрос – а зачем мы в модуле написали «Очистить()»?

Тут все дело в том, что теперь у нас движения автоматом не очищаются… НО! При работе с управляемыми формами копия объекта БД может не загрузить старые движения, к примеру, зависит это и от свойства данных формы «Использовать всегда».

Свойства формы документа

Для увеличения нажмите на изображение.

К примеру, в зависимости от этого свойства, движения документа будут прочитаны (если галка стоит) при открытии формы или нет. А если даже галка не установлена, и в форме отображаются движения, то движения будут прочитаны при открытии формы.

В обычных формах движения будут однозначно прочитаны при открытии формы.

А если движения прочитаны, то наборы записей в свойстве документа «Движения» не пустые, и добавление при проведении новых движений, как правило, приводит к дублям движений.

Так вот, чтобы не зависеть от обстоятельств, мы на всякий случай удаляем то, что возможно может находиться в коллекции движений по регистру «ОстаткиТоваров». Обращаю внимание, что запись данных в базу тут не производится, просто в памяти очищается набор записей.

Кстати, если свойство конфигурации «Основной режим запуска» будет установлено в значение «Обычное приложение», то такую строку «Движения.ИмяРегистра.Очистить()» будет писать и сам конструктор формирования движений.

Формирование движений в обычном приложении

3. Итак, движения сформировали, самое время их записать. Тут есть два варианта: либо «Движения.Записать()», либо «Движения.ОстаткиТоваров.Записать()». В чем разница и что выбрать?

Начнем с последнего: «Движения.ОстаткиТоваров.Записать()»; Этот способ безусловно запишет данные в регистр накопления. Но при этом флаг «Записывать» у набора записей снят не будет.

Но это ерунда, главное тут то, что при большом количестве наборов записей у документа нам придется самостоятельно контролировать, что в каком порядке в базу пишется, это может (да что там «может», точно скажется) негативно сказаться на проблеме взаимных блокировок (DeadLock), когда одна транзакция заблокирует таблицу «А » и будет ждать освобождения таблицы «Б «, а другая транзакция будет вести себя строго наоборот.

Теперь посмотрим, как работает метод «Движения.Записать()»; Метод записывает только те движения документа, у которых установлен флаг «Записывать», при этом флаг в итоге снимается, что не приводит к повторной записи движений по окончании транзакции проведения.

И главное, «Движения.Записать()» всегда записывают движения в том порядке, в котором таблицы указаны в дереве метаданных, что на порядок уменьшает шансы взаимных блокировок, ведь все транзакции в одинаковом порядке блокируют таблицы.

Теперь, надеюсь, очевидно, выбираем метод «Движения.Записать()»;

Запишем движения и проверим остатки в регистре.

Записать движения
Установить параметр

Комментарии к модулю:

4. Привязываем к запросу менеджер временных таблиц. Это позволит использовать временные таблицы позже для проведения по регистру с партиями товаров.

В запросе выбираем из табличной части документа товары и количество, группируем все и индексируем по полю Товар. Это, в общем случае, благоприятно скажется на быстродействии запроса.

5. «ТочкаИтогов», здесь мы используем объект «Граница». Так как передав в запрос просто «МоментВремени()», мы получили бы остатки на начало проведения документа, то есть не включили бы в расчет итогов только что сформированные движения документа. А нам нужно понять, что произошло с итогами после проведения документа.

В целом все.

Мы используем новую методику проведения документа. Сначала записали данные в регистр, а потом через кэш транзакции смотрим не ушли ли мы в минус. Если ушли – транзакцию отказываем.
Но это далеко не все. Назревает вопрос о «грязном чтении».

Грязное чтение

Что это такое?

Я не буду вдаваться в теорию (в отличии от вас, вам я советую в теорию копнуть, лишним не будет), поясню на примере:

Проводим две расходных. И в одной и в другой продаем 5 ложек. А в остатках всего 6. Так получилось, что продавать ложки мы умудрились в один и тот-же момент времени.

Что произойдет?

Первая накладная сформирует движения и начнет читать запрос по остаткам через кэш собственной транзакции, что, собственно, будет делать и вторая накладная. Но ложек-то всего 6, а в сумме две накладные продадут 10… Потому что не знают о том, что данные в таблицах уже не актуальны…

Что делать?

Надо заблокировать данные от параллельного чтения.

В нашем примере это сделать очень просто. У набора записей есть свойство «БлокироватьДляИзменения». Это, как и свойство «Записывать», лишь маркер указывающий, что необходимо установить блокировку на те записи, которые были сформированы в наборе записей.

Когда устанавливать свойство «БлокироватьДляИзменения»? Не важно, главное до самой записи данных.

Когда произойдет блокировка записей? В момент записи данных.

Что означает эта блокировка?

Никто параллельно с нами читать данные из регистра по тем товарам, движения по которым были сформированы, не сможет.

Когда блокировка будет снята?

При окончании транзакции, в которой она началась, в нашем примере – при завершении проведения документа.
Установим блокировку:

Блокировать для изменения

 

Казалось бы, все. А давайте проверим какие свойства нужно установить у объектов, для того, чтобы такая блокировка не просто работала, а еще и блокировала только те записи, по которым были сформированы движения…

А. Свойство конфигурации «Режим управления блокировкой данных в транзакции» – если, «Управляемый», то все хорошо.

Если «Автоматический», то системе глубоко фиолетово на то, что мы с вами написали. В нашем частном случае, блокировка в режиме «Автоматический» не будет установлена.

Если «и то, и другое», то важно проверить настройки объектов. Помните, что вид транзакции наследуется всеми вложенными транзакциями. То есть если документ записывается в автоматической транзакции, а движения делает по регистрам с управляемой… То система немного расстроится и вывалится с ошибкой.

Б. Так как блокируем мы не весь регистр, за что нам отдельное спасибо, а только те данные, по которым мы сформировали движения, за что спасибо ребятам из 1С, то важно установить свойство регистра «Разрешить разделение итогов».

Регистр - Разрешить разделение итогов

Если разделение итогов не будет включено и в режиме пользователя, то в большей части случаев блокировка будет наложена на весь регистр с его таблицами.
Вот теперь точно с регистром «ОстаткиТоваров» все. Займемся себестоимостью.

Партионное списание

При списании партий из регистра «СтоимостьТоваров» использовать методику оперативного проведения мы не можем.

Почему?

А для того, чтобы списать партии нужно узнать – какие конкретно, то есть нам нужно сначала прочитать данные из регистра, а потом уже формировать движения.

Запрос - УстановитьПараметр
РезультатЗапроса

 

Комментарии к модулю:

7. После проверки будем ли мы проводить документ, описываем новый текст запроса. Новый объект запрос создавать нет смысла, будем использовать уже существующий. Кроме всего, к нему уже подключен менеджер временных таблиц, в котором содержится проиндексированная таблица «ДокТЧ «.

8. Устанавливаем «МоментВремени()», как точку расчета итогов таблицы остатков. Как было описано выше, по умолчанию эта точка (то бишь старые движения, которые мог бы сформировать этот документ) в расчет итогов не попадут. Но тут есть одна хитрость, к которой мы вернемся немного позже.

9. На всякий случай очистим движения документа, вдруг они были прочитаны.

10. Опишем списание по партиям.

11. Установим маркер необходимости записи данных по регистру «СтоимостьТоваров».

Когда транзакция проведения будет завершена, система самостоятельно запишет движения в базу.

Почти все, остались блокировки.

Управляемые блокировки

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

Поэтому, для установки блокировки, будем использовать объект «БлокировкаДанных».

Объект «БлокировкаДанных» представляет таблицу, каждая строка которой описывает, что и где нужно заблокировать.

ЭлементБлокировки

 

Перед нашим вторым текстом запроса опишем блокировку.

12. Создаем объект.

13. Добавляем в объект новую строку с описанием блокируемой таблицы.

14. Для того чтобы не блокировать весь регистр, установим фильтр по товарам, данные о том по каким товарам необходимо заблокировать таблицы хранятся у нас в результате первого запроса, из пакета результатов достаем последний. Можно было и саму табличную часть документа привязать к источнику данных, но тогда система еще раз строила бы запрос к табличной части документа, а так мы все данные уже получили, сгруппировали.

15. Так как в результате запроса поле содержащее список с товарами может называться не так, как поле в регистре – описываем соответствие.

Установили блокировку. Теперь параллельно с нами никто не прочитает остатки по указанным товарам из регистра «СтоимостьТоваров». Обращаю внимание, так как мы не знаем заранее какие партии будут выбраны запросом, то фильтр по ним установить мы не можем и блокируем все партии. Чем меньше фильтров описано, тем больше данных блокируется.

И вот тут казалось бы все, а нет.

Проблема оперативного проведения

Если сейчас попробовать провести документ оперативно 2 раза, указывая последние товары в регистре, то система вылетит с ошибкой.

Почему?

При оперативном проведении, несмотря на то, что мы указали в параметре второго запроса «МоментВремени()» и он, вроде бы, не должен включить старые движения документа – система рассчитает остатки на оперативную отметку времени, то есть старые движения будут включены в расчет итогов. Да, да… Вот такая «фишка».

Что делать?

Отследим оперативное проведение и очистим старые движения.

Перед описание второго запроса вставим такой код:

Режим - Движения
 

16. Проверяем оперативно ли проводится документ.

17. Устанавливаем свойство «БлокироватьДляИзменения». Это позволит заблокировать от чтения те данные, которые сейчас будут удалены из регистра. Вдруг транзакция будет отменена, не хотим, чтобы кто-либо вместе с нами работал с этими данными.

Подобное действие не помешает и при смене даты или времени документа, а не только при оперативном проведении.

Вот теперь все. Полный текст модуля документа «Расходная»:

ОбработкаПроведения
МенеджерВременныхТаблиц
УстановитьПараметр
БлокировкаДанных
РежимПроведенияДокумента
РезультатЗапроса

 

Надеюсь после этой статьи большая часть вопросов о «новой» методике проведения и о том, почему нельзя повсеместно писать «БлокироватьДляИзменения = Истина», будет закрыта.

PDF-версия статьи для участников группы ВКонтакте

Мы ведем группу ВКонтакте — http://vk.com/kursypo1c.

Если Вы еще не вступили в группу — сделайте это сейчас и в блоке ниже (на этой странице) появятся ссылка на скачивание материалов.

[sociallocker id=»136011″]
Статья по программированию - в PDF-формате

Статья в PDF-формате

Вы можете скачать эту статью в формате PDF по следующей ссылке: Курсы-по-1С.рф — Методика оперативного проведения и управляемые блокировки.pdf

[/sociallocker]
Если Вы уже участник группы — нужно просто повторно авторизоваться в ВКонтакте, чтобы скрипт Вас узнал. В случае проблем решение стандартное: очистить кеш браузера или подписаться через другой браузер.

Комментарии / обсуждение (64):

  1. alexpodv

    Хорошо написал Viktor, но что-то никто не ответил.

    Первая расходная уменьшит текущие остатки в регистре остатков, а вторая будет ждать первую (чтобы поставить X-блокировку). Дождавшись окончания транзакции первой расходной, вторая расходная сама поставит X-блокировку и отработает
    UPDATE _AccumRgTXXXXX
    set _FldXXXYY = _FldXXXYY(остаток с учетом первой расходной) — @P2. Поэтому проверяя после этого UPDATE поле _FldXXXYY будем видеть ушли в минус или нет. Никаких других механизмов не надо придумывать.

    Viktor (18.07.2015)
    Первая транзакция Т1 исполняет SQL оператор 1) UPDATE _AccumRgTXXXXX set _FldXXXYY = _FldXXXYY — @P1, где Р1 – количество расхода по первой расходной накладной. Вторая транзакция Т2 ждет завершения первой транзакции, чтобы исполнить оператор 2) UPDATE _AccumRgTXXXXX set _FldXXXYY = _FldXXXYY — @P2, где Р2 – количество расхода во второй расходной накладной. Поскольку второй оператор update исполнится только после commit Т1, то в поле _FldXXXYY ,будет записано значение P0 – P1 – P2, где P0 – значение остатка в поле _FldXXXYY на момент старта транзакции Т1. Таким образом, потери информации не произойдёт

  2. Александр

    Грязное чтение
    ……..
    Первая накладная сформирует движения и начнет читать запрос по остаткам через кэш собственной транзакции, что, собственно, будет делать и вторая накладная. Но ложек-то всего 6, а в сумме две накладные продадут 10… Потому что не знают о том, что данные в таблицах уже не актуальны…

    Скажите пожалуйста,в какой момент происходит считывание данных движения в транзакционный кэш,во время их записи?

  3. mizantrop

    Прокомментируйте пункт №7 в необходимости применять отбор по товару на виртуальную таблицу Остатки, если все равно происходит соединение таблиц по этому полю?

    РегистрНакопления.СтоимостьТоваров.Остатки(&ТочкаИтоговДляСебестоимости, Товар В (……..Накладывается фильтр и потом повторяется в условии соединения.

    • GROOVY

      Ну либо все 100500 остатков сначала рассчитаются а потом по условию соединения 100495 будут отброшены, либо сначала расчет остатков 5 товаров произойдет и потом соединение. Мне кажется второй вариант несколько быстрее.

  4. Александр

    Здравствуйте,в не совсем понятно,когда в транзакционный кэш заносятся данные о итогах регистра в данном примере?

  5. Марат

    А можно вместо первых двух строк написать следующее:

    Движения.ОстаткиТоваров.Записывать = Истина;
    Движения.ОстаткиНоменклатуры.Записать();

    БлокировкаДанных = Новый БлокировкаДанных;

    ЭлементБлокировки = БлокировкаДанных.Добавить(«РегистрНакопления.ОстаткиТоваров»);
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Номенклатура»,»Номенклатура»);
    БлокировкаДанных.Заблокировать();

    Или моя блокировка данных является излишней?

  6. Александр

    Здравствуйте.
    У меня следующий вопрос.
    Есть регистр ОстаткиНоменклатуры. Для конфигурации и для регистра включен управляемый режим блокировок. Для регистра включены разделения итогов. Конфигурация Управляемое приложение.
    Для ПРИХОДНОЙ накладной при помощи конструктора сформировал процедуру проведения.
    Нужно ли (имеет ли смысл) добавлять в модуль проведения следующие строки:

    Движения.ОстаткиНоменклатуры.Очистить();
    Движения.ОстаткиНоменклатуры.БлокироватьДляИзменения = Истина;

    • GROOVY

      Нет. У нас в приходной нет конкуренции за получение данных. Блокировки нужны исключительно при обусловленном проведении.

  7. Дмитрий

    Павел здравствуйте, возник вопрос по блокам кода 16 и 17. Как я понимаю если у документа выставлено свойство «Записывать выбранные», то маркер «Записывать» будет выставлен перед началом проведения в значение «Ложь», то есть, если раньше блока 16, не выставить маркер у требуемого набора записей (в этом случае «Стоимость товаров») признак в значение «Истина», то Движения.Записать не даст нужного результа, то есть старые движения мы не очистим, для этого нужно либо явно устанавливать маркер, либо удостовериться что у документа установлено свойство «Записывать модифицированные»?  Можете пояснить этот момент, а то специалиста я сдал, а этот вопрос и от экзаменатора и от самого себя утаил. А истину узнать охота.)

      • Andy

        Здравствуйте, Павел. А почему в блоке 17 Вы использовали конструкцию Движения.СтоимостьТовара.Записать(), вместо Движения.Записать() как в п. 3.? Вы ранее писали что это более удачный способ позволяющий избежать лишних записей в таблицу БД.

          • Andy

            Спасибо за ответ и за статью, можно сказать что учусь сейчас по ней )). Я понимаю что это один набор, но вопрос какую конструкцию лучше использовать, ведь можно сделать так:
            Движения.СтоимостьТоваров.Записывать = Истина;
            Движения.Записать()
            у других наборов маркеров на запись не стоит и в итоге запишется один набор и судя по Вашему описанию это лучше для платформы, т.к. уменьшается шанс взаимных блокировок. Или я чего-то не до понял?

            Так же Павел ещё один вопрос возникает, по поводу п.5 и п.8, а зачем нам в запросах вообще применять эти параметры, как понимаю без них мы получим актуальные данные из таблицы итогов на 01/11/3999 г.? В книге Радченко запрос по остаткам без них строится.

            • GROOVY

              Если набор пишется один, то тут без разницы как его писать :)

              А если документ проводится неоперативно? Нам нужно сначала подучить остатки с учетом движения документа, а в другом случае без. Потому так и пишем.

              • Andy

                1. Почему без разницы? Вы же сами писали что «Движение.Записать() не приводит к повторной записи движений по окончании транзакции проведения». Понято что можно такой мелочью пренебречь но просто пока учусь хотелось бы «привыкнуть к правильному коду» :)

                2. А вот здесь не догнал. Извиняюсь если глупый вопрос от начинающего. Читая Радченко (Практическое пособие разработчика) у меня в голове засело что при неоперативном проведении остатки проверять не надо. У него прямо в коде документа «Оказания услуг» стоит Если Режим = РежимПроведенияДокумента.Оперативный Тогда — проверяем отрицательные остатки. Где тут правда? Это же официальная книга от 1с.

                3. Решая задачу из сборника 1.1. по описанной Вами методике, у меня вышло 3 регистра: первые два аналогичны описанным, а третий оборотный Продажи, где храню обороты по количеству, стоимости, выручке. Можно конечно выручку и в регистр СтоимостьТоваров добавить, и ограничится 2-мя регистрами но ИМХО это не совсем будет верно получать обороты из регистра остатков. Вопрос. На экзамене по спецу обращают внимание на количество регистров? Мол зачем ты ещё один сделал если можно было бы двумя обойтись получив тот же самый отчет?

                • GROOVY

                  1. Без разницы. При чем тут повторная запись набора, если мы маркер ему не установили? Перечитайте еще раз статью.
                  2. Не проверяйте. Потом будете объяснять клиентам, что им это не надо. Я как бы не настаиваю.
                  3. На экзамене проверяют. Неоптимальное получение показателей — 3 балла. Выручку в стоимость товаров добавить… Ну так не сдать экзамен точно.

  8. Andy

    «Движения.Записать() всегда записывают движения в том порядке, в котором таблицы указаны в дереве метаданных, что на порядок уменьшает шансы взаимных блокировок, ведь все транзакции в одинаковом порядке блокируют таблицы»

    Не могу догнать эту фразу. А почему на порядок уменьшается шанс взаимных блокировок если движения записываются в порядке указания в дереве метаданных? В чём разница если я наборы данных будут записывать не по порядку дерева? Поясните пожалуйста, а то мозг уже сломал ))

    • GROOVY

      Если писать данные хаотично, то велика вероятность попасть на взаимные блокировки, когда объект а блокирует таблицу объекта Б, а тот в свою очередь блокирует А. Если порядок одинаков, то шансы резко уменьшаются.

  9. Alexsasha

    Для регистра СтоимостьТоваров при оперативном проведении мы очищаем старые движения (Очистить(), Записать()).
    Почему мы так не делаем для регистра ОстаткиТоваров?

  10. aaal

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

    • GROOVY

      Минусы: Блокируем ВСЕ партии. Не позволяем параллельное проведение приходных. Несколько раз записываем данные в регистр, то есть Мучаем блокировки по нескольку раз за транзакцию.

      • aaal

        Хм, так ведь и в исходном алгоритме также блокируем все партии но номенклатуре, только немного позже. И с параллельным проведением приходных та же ситуация, ведь вначале блокируем регистр «Остатки номенклатуры».
        Я не против 2-х регистров, если такая политика 1С, просто хочется прояснить, в чем их преимущество по сравнению с моим предложением.
        Особенно в примерах со списанием по средней и указанием конкретной партии списания в расходной — можно вначале просто не заполнять себестоимость, записать, проверить остатки и далее все по тексту, все блокировки те же. Согласитесь, что при списании по средней или при указании партии списания в расходной на одном регистре оптимальнее?

        • GROOVY

          Смысл методики оперативного проведения, на примитивном регистре БЫСТРО принять решение о необходимости дальнейших действий, не блокируя более сложные регистры «на всякий случай».
          Себестоимость партий — это демопример. Но и в нем можно предположить, что сама себестоимость будет списываться фоновым заданием, и с контекстом клиента вообще не будет связана.

          • aaal

            Понятно, спасибо за ответ. А на экзамене на специалиста в задачах со списанием по средней или указанием конкретной партии списания в расходной накладной тоже посоветуете делать 2 регистра?

  11. Евгений

    я сделал границу так:
    Граница(МоментВремени(),ВидГраницы.Исключая) и тогда 16,17 не понадобилось, все работает…

    • GROOVY

      При оперативном проведении время документа будет сдвигаться, старые движения попадут в расчет остатков.

  12. Евгений

    Простите, а как организовать списание партий, если в документе допускаются ДУБЛИ (по ТМЦ) строк с разной ценой? Более того, в строке может быть указана конкретная ПАРТИЯ, которая должна быть списана в первую очередь.
    В этом случае я так понимаю запрос в 7 не применим…

  13. xdd

    Здравствуйте новичок в этой теме подскажите пожалуйста, правильно ли я понимаю, что в файловом режиме работы блокировка всегда накладывается на всю таблицу (регистр), чтобы ты не прописывал?

  14. Bartezz

    По-моему проблему оперативного проведения можно решить не прибегая к излишним блокировкам и излишним записям в БД. Проще объявить в модуле объекта переменную МоментВремениДокумента, значение которой определять в процедуре ПередЗаписью:
    Если Не ЭтоНовый() Тогда
    МоментВремениДокумента = МоментВремени();
    КонецЕсли;
    И уже на этапе передачи параметра момента времени в запрос определять какую из двух брать:
    Запрос.УстановитьПараметр(«МоментВремени», ?(МоментВремениДокумента = Неопределено, МоментВремени(), МоментВремениДокумента));

    В таком случае проблема оперативного проведения автоматически снимается. Поправьте если не прав

    • GROOVY

      Новый документ может проводится не оперативно.
      Старый документ может перепроводится оперативно.

  15. Владимир Качковский

    Здравствуйте.
    Очень удивила запись о том, что если у регистра накопления не стоит разделение итогов, то есть большая вероятность, что будет заблокирован весь регистр. Это только в рамках текущей задачи или …? Насколько я понимаю, режим разделения итогов позволяет одновременно записывать данные нескольким транзакциям по одинаковому набору измерений (в случае если нет контроля остатков, либо в случае контроля остатков — при грамотном использовании — БлокироватьДляИзменения). Каким образом его отсутствие может привести к блокировке всей таблицы — мне не понятно.
    С уважением, Владимир.

    • GROOVY

      Возможность использования разделения итогов у регистра позволяет обновлять (не блокировать) таблицу при параллельной записи итогов по одинаковому набору значений измерений. Если разделение не включено, то транзакция которая пытается обновить итоги встает в очередь и ждет пока записи будут освобождены прошлой транзакцией.
      Разделение итогов не влияет на поведение файловой версии, в которой в любом случае будет заблокированы и таблица движений и таблица остатков целиком. Так же это свойство без описания управляемых блокировок никак не повлияет на работу базы под управлением не MSSQL сервера, и даже с его помощью предугадать как будут заблокированы записи в автоматическом режиме никто не возьмется.

  16. unkas

    так и не дошло, зачем в первом запросе нужен последний запрос пакета:
    |ВЫБРАТЬ
    | ДокТЧ.Товар
    |ИЗ
    | ДокТЧ КАК ДокТЧ

  17. Игорь

    Здравствуйте. Конечно же я новичок. Учитель задал разобраться, почему в модуле объекта, строку «Движения.РегистрНакопления.Записывать = Истина» нужно писать в самом начале, а не непосредственно перед движениями. Объясните или дайте ссылку на объяснение. Спасибо.

    • GROOVY

      Эту строку можно писать где угодно, главное чтобы до записи коллекции движений.

  18. Максим

    Есть две транзакции T1 и Т2. Обе обращаются к одним и тем же данным одного регистра. Т1 и Т2 параллельно считывают данные из регистра. При этом Т1 первая добирается до записи. Т1 блокирует для изменения записи регистра. Т2 доходит до записи, видит, что записи заблокированы, ждёт. При этом Т1 записывает данные и успешно завершает транзакцию. Т2 видит, что записи освободились, тоже записывает свои данные (без повторного чтения) и затирает изменения Т1. Не понимаю, как система разрешает эту коллизию. Где ошибка в моём примере?

    • Василий Ханевич

      Похоже, это вопрос в рамках курса по запросам. Если так, то просьба перенести Ваш комментарий туда, чтобы другие участники его увидели.
      Если описанный пример рассматривается в конфигурации с автоматическими блокировками, то возникнет взаимоблокировка, т.к. блокировки снимаются после завершении транзакции, и Т1 не сможет добраться до записи, т.к. ей мешает S-блокировка, наложенная при чтении другой транзакцией. Аналогично транзакция Т2.
      При управляемых блокировках уровень изоляции транзакций ниже, блокировки снимаются после выполнения запроса, поэтому мы можем списать остатки дважды и уйти «в минус». Чтобы такого не произошло, разработчику в программном коде необходимо установить исключительную блокировку.

    • Viktor

      Вторая транзакция ждет завершения первой транзакции, чтобы исполнить SQL оператор UPDATE _AccumRgTXXXXX set _FldYYYYY = _FldYYYY — @P1;
      где парам

    • Viktor

      Первая транзакция Т1 исполняет SQL оператор 1) UPDATE _AccumRgTXXXXX set _FldXXXYY = _FldXXXYY — @P1, где Р1 – количество расхода по первой расходной накладной. Вторая транзакция Т2 ждет завершения первой транзакции, чтобы исполнить оператор 2) UPDATE _AccumRgTXXXXX set _FldXXXYY = _FldXXXYY — @P2, где Р2 – количество расхода во второй расходной накладной. Поскольку второй оператор update исполнится только после commit Т1, то в поле _FldXXXYY ,будет записано значение P0 – P1 – P2, где P0 – значение остатка в поле _FldXXXYY на момент старта транзакции Т1. Таким образом, потери информации не произойдёт

  19. Olenevod

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

    Если мы в любом случае очищаем движения

    // 16
    Движения.Стоимость.Товаров.Очистить();

    и
    // 9
    Движения.Стоимость.Товаров.Очистить();

    То не правильней ли было бы удалить строку с комментарием // 9
    а в // 16 написать

    Движения.Стоимость.Товаров.Очистить();
    Если Режим = РежимПроведенияДокумента.Оперативный Тогда
    Движения.Стоимость.Товаров.БлокироватьДляИзменения = Истина;
    Движения.Стоимость.Товаров.Записать();
    КонецЕсли;

    Заранее спасибо за ответ.

  20. Серг

    Спасибо за материал,безусловно полезен для подготовки к специалисту,чем и занимаюсь.Еще вопрос а если использовать в запросе конструкцию для изменения,так не проще «Новый БлокировкаДанных»?И вот еще,как я понял что если для проведения нет всех данных,и они берутся запросом,то мы используем вариант как со стоимостью.А что вот не совсем понятно так с этими механизмами очистки и получения остатков с движениями,постоянно какая то двусмысленность идет,нет такого четкого алгоритма,именно места где могут прочитаться,могут не очиститься,могут не прочитаться,а если еще учитывая настройки «удалять автоматически» и «автоматически при отмене проведения»,то очень смутно понятно.

    • GROOVY

      ДляИзменения — работает в автоматических блокировках. И только.
      По всему остальному — рекомендую, не спеша, прочитать статью еще раз.

  21. snark

    Маленькое уточнение по коду: в //10 ОсталосьСписать не уменьшается по ходу цикла на количество уже списанного?

  22. Аскер

    Прошу прошения, но хотелось бы окончательно разобраться:
    разве в //17 не происходит повторная блокировка данных, которые были заблокированы в //12-//15?
    Спасибо.

    //12
    Блокировка = Новый БлокировкаДанных;
    //13
    ЭлементБлокировки =
    Блокировка.Добавить(«РегистрНакопления.СтоимостьТоваров»);
    ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    //14
    ЭлементБлокировки.ИсточникДанных = ПакетРезультатов[2] ;
    //15
    ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Товар», «Товар»);
    Блокировка.Заблокировать();

    //16
    Если Режим = РежимПроведенияДокумента.Оперативный Тогда
    Движения.СтоимостьТоваров.Очистить();
    //17
    Движения.СтоимостьТоваров.БлокироватьДляИзменения = Истина;
    Движения.СтоимостьТоваров.Записать();
    КонецЕсли;

      • Александр

        Здравствуйте.
        У меня возник аналогичный вопрос. Объясните, пожалуйста, почему не будет происходить повторной блокировки в описанном выше коде?

        Могу предположить следующий варианты развития событий:

        1) Если оперативно проводится ранее не проведенный документ. тогда в 17 мы ни чего ни заблокируем. Нечего блокировать.

        2) Если оперативно перепроводим документ, в котором были удалены строки участвовавшие в предыдущем движении. Тогда в 12-15 эти удаленные строки не попадут и следовательно мы их должны заблокировать в 17. Т.е. 17 пункт необходим.

        3) Если те же строки остались или добавились новые. Тогда 17 пункт будет лишний?

        Может быть следовало бы выполнить дополнительные проверки и выполнять 17 пункт только для второго случая, когда происходит удаление строк? На сколько вообще критична для производительности данная блокировка? Очень хотелось бы получить развернутый ответ.

        Заранее спасибо.

        • Татьяна Гужавина

          Доброго дня, Александр!
          1. Да, все верно.
          2. Да, все верно.
          3. Менеджер блокировок 1С устроен таким образом, что если 2 раза попытаться наложить блокировку на те же значения, то блокировка второй раз накладываться просто не будет. Для производительности попытка наложить повторную блокировку вообще не критична.

  23. Поддержка курса по EDT

    Основательно. Однако не хватает сравнения. Т.е. как это делать — из статьи понятно, а зачем? Не очень. Хотя я по ходу просто не в теме. Так сказать — что в итоге получим в цифрах. Для баз где работает «овер 9000» человек, там понятно, экономия при проведение, по сути — безболезненное перепроведение.

    Но как вы считаете, имеет ли смысл использовать это например в таком случае — есть 200 магазинов, все они закрываются в 8 вечера, и каждый администратор, считает, что если он не закроет кассу ровно в 8:00, то за ним придет дьявол и откусит ему ухо. В итоге — 200человек неистово жмакают закрытие кассы и проведение отчета, ну и ясное дело — попадают в блок (это я говорю про УТ10).

    Так вот, если включить разделение данных, и поставить блок на товар и на склад, то по идее — у них вообще никогда не будет взаимоблокировок? За исключением перемещений.

    И еще такой вопрос, как работа этих механизмов зависит от СУБД? Т.е. в СУБД же блок идет или строк, или таблиц, в зависимости от СУБД. А тут как? Или блок идет только для режима «Авто»?

    Заранее спасибо за ответ. Тема интересная, но в ней не профи, хочется разобраться.

    • GROOVY

      Управляемые блокировки устанавливаются сервером 1С:Предприятие, а не СУБД.

    • gudun-ku

      В данном случае, лучше сделать отложенное проведение документов, чтобы у администратора не было доступа к проведению документа отчета о розничных продажах, а производилась только запись документа в базу. А проведение документов выполнять регламентным заданием, ночью, когда все документы уже записаны и на кнопки никто не нажимает :)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

 Ознакомлен с условиями Публичной оферты и Пользовательского соглашения
 Согласен на обработку персональных данных (Политика обработки ПДн)