“Новая” и “старая” методики контроля отрицательных остатков при проведении документов в системе 1С:Предприятие 8.3

Эта статья предназначена для внедренцев 1С – и особенно для тех, кто готовится к Аттестации на 1С:Специалист по платформе.

Сегодня мы разберем 2 методики контроля остатков – причем не только остатков на складе, но и, например, взаиморасчетов (“какова текущая задолженность клиента и можно ли отгружать ему товары”)

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

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

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

Возьмем простую конфигурацию с документами “Поступление товаров” и “Реализация товаров”:

Метаданные документов модельной конфигурации

Для учета остатков используется регистр накопления “Свободные остатки”:

Метаданные регистра Свободные остатки

При проведении документа “Поступление товаров” выполняются движения-приход:

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

КонецПроцедуры

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

Иногда контроль остатков реализуют и для документа «Поступление товаров» – чтобы при отмене проведения или перепроведении документа не образовался отрицательный остаток.

Например, на склад поступили 10 новых телевизоров LG, 6 из них было продано. Если в документе поступления 10 шт. исправить на 5 шт. – образуется отрицательный остаток «минус 1 шт.».

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

При проведении документа «Реализация товаров» необходимо организовать контроль остатков. Если товара на остатках недостаточно, документ не проводится и выдается диагностическое сообщение. В этом и состоит решаемая задача.

Мы намеренно работаем над простой задачей, когда себестоимость при списании не рассчитывается. Это позволит нам сосредоточиться именно на нюансах контроля остатков.

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

Естественно, Вы можете оптимизировать их самостоятельно, либо пройти наш курс по Ускорению и Оптимизации 1С :)

Как Вы уже поняли, решение задачи может быть выполнено двумя способами. Начнем с методики, которая применялась ещё со времен «1С:Предприятие 8.0».

Старая методика контроля остатков

Принцип старой методики контроля остатков следующий: проверяем, есть ли остаток товаров в нужном количестве. Если есть – списываем, если нет – сообщаем об ошибке.

Алгоритм в старой методике состоит из нескольких блоков:

  1. Запросом получаются остатки товаров и данные документа
  2. В цикле выполняется контроль достаточности товаров
  3. Если товаров недостаточно, то документ не проводится
  4. Если товаров достаточно – выполняются движения-расход

Вот так выглядит программный код:

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
   
    //  1. Очистка старых движений регистра
    Движения.СвободныеОстатки.Очистить();
    Движения.СвободныеОстатки.Записывать = Истина;
    Движения.Записать();
   
    //  2. Получение запросом данных документа и остатков регистра
    Запрос = Новый Запрос;
    Запрос.Текст =
        "ВЫБРАТЬ
        |   Товары.Номенклатура КАК Номенклатура,
        |   СУММА(Товары.Количество) КАК Количество
        |ПОМЕСТИТЬ Товары
        |ИЗ
        |   Документ.РеализацияТоваровУслуг.Товары КАК Товары
        |ГДЕ
        |   Товары.Ссылка = &Ссылка
        |
        |СГРУППИРОВАТЬ ПО
        |   Товары.Номенклатура
        |
        |ИНДЕКСИРОВАТЬ ПО
        |   Номенклатура
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        |   Товары.Номенклатура КАК Номенклатура,
        |   ПРЕДСТАВЛЕНИЕССЫЛКИ(Товары.Номенклатура) КАК НоменклатураПредставление,
        |   Товары.Количество КАК Количество,
        |   ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
        |ИЗ
        |   Товары КАК Товары
        |       ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки.Остатки(
        |               &МоментВремени,
        |               Номенклатура В
        |                   (ВЫБРАТЬ
        |                       Товары.Номенклатура КАК Номенклатура
        |                   ИЗ
        |                       Товары КАК Товары)) КАК Остатки
        |       ПО Товары.Номенклатура = Остатки.Номенклатура";
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    Запрос.УстановитьПараметр("МоментВремени", МоментВремени());
    РезультатЗапроса = Запрос.Выполнить();
   
    //  3. Обход результатов запроса
    ВыборкаТовары = РезультатЗапроса.Выбрать();
   
    Пока ВыборкаТовары.Следующий() Цикл
       
        //  4. Проверка на достаточность товаров
        Дефицит = ВыборкаТовары.Количество - ВыборкаТовары.Остаток;
        Если Дефицит>0 Тогда
            Отказ = Истина;
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = "Товара "+ВыборкаТовары.НоменклатураПредставление+" недостаточно в количестве "+Дефицит+" шт.";
            Сообщение.Сообщить();
        КонецЕсли;
       
        //  5. Переход в начало цикла, если были ошибки
        Если Отказ Тогда
            Продолжить;
        КонецЕсли;
       
        //  6. Выполнение движений в регистры
        Движение = Движения.СвободныеОстатки.ДобавитьРасход();
        Движение.Период = Дата;
        Движение.Номенклатура = ВыборкаТовары.Номенклатура;
        Движение.Количество = ВыборкаТовары.Количество;
    КонецЦикла;
   
    //  7. Установка флага записи движений в конце транзакции
    Движения.СвободныеОстатки.Записывать = Истина;
   
КонецПроцедуры

Прокомментируем ключевые точки алгоритма.

1. Очистка старых движений регистра

Ниже в алгоритме будет запрос к остаткам регистра.

Если текущий документ был ранее проведен, то существует вероятность получить в запросе старые движения документа – это серьезная проблема.

Когда возможна такая ситуация? Когда дата документа сдвигается вперед.

Покажем на примере, к чему это приведет:

  1. Остаток ламп настольных 10 шт.
  2. Проводится документ от 16.02.17, списываем 6 ламп
  3. В документе меняется дата на 17.02.17 (дату можно сместить хоть на 1 секунду вперед), перепроводим документ.

Если очистку движений не выполнять, то система сообщит о нехватке 2 штук. Почему? Да потому что старые движения документа списали 6 из 10 имеющихся ламп. Далее система пытается списать еще 6 штук, а на остатках есть только 4.

Проблема решается в 3 строки кода:

  • Выполняется очистка набора записей (он мог быть прочитан на форме или в предыдущих обработчиках)
  • У набора записей устанавливается флаг «Записывать»
  • Выполняется запись всех наборов, у которых установлен флаг «Записывать»

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

Строго говоря, мы можем управлять очисткой движений при проведении документов:

Настройка режима удаления движений в документах

Вариант с удалением движений при отмене проведения является рекомендуемым – мы сами управляем, когда нужно действительно удалять движения.

2. Получение запросом данных документа и остатков регистра

Запрос состоит из двух пакетов:

  • В первом получаются сгруппированные данные табличной части – создается временная таблица
  • Во втором запросе к данным документа присоединяются остатки из регистра.

На что стоит обратить внимание в этом запросе:

  1. При создании временной таблицы индексируется поле, по которому далее будет выполняться соединение – это сделано для оптимальной производительности
  2. Момент получения остатков – соответствуют положению документа на временной оси
  3. Остатков в регистре может не быть – поэтому выполняется левое соединение и для ресурса «Количество» применяется функция «ECТЬNULL» – значение NULL приводится к нулю.

3. Обход результатов запроса

Разработанный запрос содержит сгруппированные данные документа и остатки по номенклатурным позициям.

В цикле обходим результат этого запроса.

4. Проверка на достаточность товаров

Определяем дефицит по товарам.

Если дефицит больше нуля, значит, товара не хватает:

  • Выдаем диагностическое сообщение
  • Выставляем параметр «Отказ» обработки проведения в значение «Истина»

Если «Отказ» будет равен «Истина», то результат транзакции проведения документа не будет зафиксирован. Говоря простым языком – это команда системе не проводить данный документ.

5. Переход в начало цикла, если были ошибки

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

6. Выполнение движений в регистры

Если проверка остатков прошла успешно, формируем движение-расход.

7. Установка флага записи движений в конце транзакции

Если данный флаг не установить, то движения НЕ будут записаны.

В конце транзакции проведения документа записываются только те наборы записей, у которых установлен флаг «Записывать».

Справедливости ради отметим, что установка свойства “Записывать” набора записей имеет смысл при одном условии – в свойстве документа “Запись движений при проведении” должно быть указано значение “Записывать выбранные”:

Свойство документа Запись движений при проведении - Записывать выбранные и Записывать модифицированные

Однако именно значение “Записывать выбранные” является стандартом де-факто:

  • Оно используется в типовых решениях
  • Устанавливается по-умолчанию при создании новых документов.

Другое значение свойства – “Записывать модифицированные” является устаревшим и в современных конфигурациях практически не встречается.

Новая методика контроля остатков

В новой методике используется принцип: списываем необходимые товары, далее проверяем – образовались ли отрицательные остатки по товарам документа. Если да, то нужно откатить проведение документа.

Как видите, принципиальная разница в моменте контроля остатков:

  • Старая методика – сначала проверяем остаток, потом списываем
  • Новая методика – сначала списываем, потом проверяем остаток.

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

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
   
    //  1. Получение запросом данных документа
    Запрос = Новый Запрос;
    Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
    Запрос.Текст =
        "ВЫБРАТЬ
        |   Товары.Номенклатура КАК Номенклатура,
        |   СУММА(Товары.Количество) КАК Количество
        |ПОМЕСТИТЬ Товары
        |ИЗ
        |   Документ.РеализацияТоваровУслуг.Товары КАК Товары
        |ГДЕ
        |   Товары.Ссылка = &Ссылка
        |
        |СГРУППИРОВАТЬ ПО
        |   Товары.Номенклатура
        |
        |ИНДЕКСИРОВАТЬ ПО
        |   Номенклатура
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        |   Товары.Номенклатура КАК Номенклатура,
        |   Товары.Количество КАК Количество
        |ИЗ
        |   Товары КАК Товары";
    Запрос.УстановитьПараметр("Ссылка", Ссылка);
    РезультатЗапроса = Запрос.Выполнить();
   
    //  2. Формирование движений-расход регистра
    Движения.СвободныеОстатки.Очистить();
    ВыборкаТовары = РезультатЗапроса.Выбрать();
    Пока ВыборкаТовары.Следующий() Цикл
        Движение = Движения.СвободныеОстатки.ДобавитьРасход();
        Движение.Период = Дата;
        Движение.Номенклатура = ВыборкаТовары.Номенклатура;
        Движение.Количество = ВыборкаТовары.Количество;
    КонецЦикла;
   
    //  3. Запись движений в БД
    Движения.СвободныеОстатки.Записывать = Истина;
    Движения.Записать();
   
    //  4. Запрос, получающий отрицательные остатки из регистра
    Запрос.Текст =
        "ВЫБРАТЬ
        |   Остатки.Номенклатура КАК Номенклатура,
        |   ПРЕДСТАВЛЕНИЕССЫЛКИ(Остатки.Номенклатура) КАК НоменклатураПредставление,
        |   -Остатки.КоличествоОстаток КАК Дефецит
        |ИЗ
        |   РегистрНакопления.СвободныеОстатки.Остатки(
        |           &МоментВремени,
        |           Номенклатура В
        |               (ВЫБРАТЬ
        |                   Товары.Номенклатура КАК Номенклатура
        |               ИЗ
        |                   Товары КАК Товары)) КАК Остатки
        |ГДЕ
        |   Остатки.КоличествоОстаток < 0";
   
    ГраницаКонтроля = Новый Граница(МоментВремени(), ВидГраницы.Включая);
    Запрос.УстановитьПараметр("МоментВремени", ГраницаКонтроля);
    РезультатЗапроса = Запрос.Выполнить();
   
    //  5. Вывод сообщений о недостатке товаров
    Если Не РезультатЗапроса.Пустой() Тогда
        Отказ = Истина;
        ВыборкаОшибки = РезультатЗапроса.Выбрать();
        Пока ВыборкаОшибки.Следующий() Цикл
            Сообщение = Новый СообщениеПользователю;
            Сообщение.Текст = "Товара "+ВыборкаОшибки.НоменклатураПредставление+" недостаточно в количестве "+ВыборкаОшибки.Дефецит+" шт.";
            Сообщение.Сообщить();
        КонецЦикла;
    КонецЕсли;
   
КонецПроцедуры

Разберем ключевые точки алгоритма.

1. Получение запросом данных документа

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

Дальше эти данные будут использованы для создания движений.

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

2. Формирование движений-расход регистра

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

3. Запись движений в БД

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

4. Запрос, получающий отрицательные остатки из регистра

А теперь простым запросом выбираем отрицательные остатки по товарам документа.

Именно здесь используется созданная на первом шаге временная таблица – накладывается условие на номенклатуру (для этого мы не создаем новый объект типа «Запрос», а используем созданный ранее).

Обратите внимание, как передается момент времени – используется тип данных «Граница». Остатки нужно получить на момент времени сразу ПОСЛЕ текущего документа.

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

Нет! Ведь в одной секунде может быть большое число документов. Поэтому единственный правильный вариант – использовать вид границы «Включая».

5. Вывод сообщений о недостатке товаров

Если результат запроса не пустой, значит, есть отрицательные остатки – в этом случае документ не проводится и выдаются сообщения обо всех ошибках.

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

Итак, оба алгоритма решают одну и ту же задачу.

Разница между алгоритмами видна, но преимущества не очевидны.

Поэтому давайте подчеркнем их:

  1. Нет необходимости очищать старые движения документа. По сути это операция записи в БД пустого набора движений и удаление существующих движений – это довольно ресурсоемкие операции
  2. Запрос, получающий данные по отрицательным остаткам, обращается только к одной таблице – нет необходимости делать левое соединение с данными документа и применять функцию «ЕСТЬNULL()»

Кроме этого, при нормальном течении бизнес-процессов пользователь указывает количество, не превышающее остаток на складе.

В этом случае второй запрос не вернет никаких данных и проведение документа будет максимально быстрым.

А так ли важны эти миллисекунды?

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

Кроме того, на экзамене 1С:Специалист по платформе нужно обязательно использовать новый способ контроля остатков, если это допускает конкретная задача.

Ok, значит, нужно всегда использовать новую методику, верно?

Нет, это не так!

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

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

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

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

К слову сказать, в типовой «1С:Управление торговлей 11» реализован контроль остатков по новой методике, а в «1С:Бухгалтерии 8» – по старой методике.

Но это ещё не все!

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

Блокировкам для обоих методик контроля остатков посвящена отдельная статья. Также в данной статье мы решаем более сложную задачу – кроме контроля остатков выполняем расчет себестоимости списываемой номенклатуры. Рекомендуем её вдумчиво изучить.

А для «затравки» лишь скажем, что установка блокировки в новой методике делается очень просто – и это еще одно преимущество нового способа контроля остатков.

Итоги

Подведем краткие итоги.

Мы рассмотрели две методики контроля остатков, каждая из которых применяется в современных типовых конфигурациях.

Ключевое различие между методиками в моменте контроля остатков:

  • Старая методика – контроль до записи движений в регистры
  • Новая методика – контроль после записи движений в регистры

В общем случае новая методика является более эффективной, но применима она не всегда.

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

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

И в завершение примеры из типовых конфигураций:

  • В УТ 11 есть 2 основных регистра для учета номенклатуры: Свободные остатки (количество) и Себестоимость товаров (данные о себестоимости) – используется новая методика
  • В БП 3.0 данные о себестоимости и остатках хранятся в одном регистре бухгалтерии – используется старая методика контроля остатков.

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

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

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


Статья в PDF-форматеСтатья в PDF-формате
Вы можете скачать эту статью в формате PDF по следующей ссылке: Ссылка доступна для зарегистрированных пользователей)
ИБ с новой методикой контроля остатков:
Ссылка доступна для зарегистрированных пользователей)
ИБ со старой методикой контроля остатков:
Ссылка доступна для зарегистрированных пользователей)

ИБ разрабатывались на платформе 1С:Предприятие 8.3.9.1850.

89 комментариев к ““Новая” и “старая” методики контроля отрицательных остатков при проведении документов в системе 1С:Предприятие 8.3

  1. Alex2016 сказал:

    Добрый день!
    Зачем в старом методе контроля остатков (п.3 алгоритма) в обходе выборки делаются и движения. Ведь может получиться так, что у 100 первых товаров в выборке дефицита нет, а у 101-го товара дефицит есть. Тогда мы выполняем лишние строки кода.
    Не лучше ли вынести движения из этого цикла в отдельный цикл, который будет выполняться, если дефицита нет, и Отказ остался = Ложь?

    • Вячеслав Вязигин сказал:

      День добрый!

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

  2. Alex2016 сказал:

    Добрый день!
    Зачем в запросе в старом методе в п.2. в тексте запроса выбирается 2 поля Товары.Номенклатура и ПредставлениеСсылки(Товары.Номенклатура).
    Разве недостаточно ПредставленияСсылки?

    • Вячеслав Вязигин сказал:

      День добрый!

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

  3. Яковенко Илья сказал:

    Насколько правильно делать в старой методике проведения вот так:

    //Снимаем галку разделение итогов, чтобы заблокировать записи по “Удаляемым” данным. Чтобы при отказе транзакции другой пользователь между контролем остатков и отказом транзакции не списал освободившуюся номенклатуру
    Движения.ОстаткиНоменклатуры.БлокироватьДляИзменения = Истина;
    //Если работаем с обычными формами
    Движения.ОстаткиНоменклатуры.Очистить();
    //Если режим записи не оперативный, то мы будем использовать в дальнейшем выбора остатки с МоментВремени() который не включает движения документа
    //Если режим записи оперативный, то мы очищаем движения, так как при чтении остатков использование МоментВремени() все равно будет брать остатки влючая движения данного документа.
    Если Режим = РежимПроведенияДокумента.Оперативный Тогда
    //Пишем пустой набор записей, флаг ОстаткиНоменклатуры.Записывать = Истина не снимается
    Движения.ОстаткиНоменклатуры.Записать();
    КонецЕсли;

    Интересует нужно ли снимать разделение итогов. И реально ли при оперативном проведении перепроведение приведет к тому что будут взяты остатки которые включают движения данного документа?

    • Яковенко Илья сказал:

      Обсуждение данного вопроса на форуме Чистова
      http://forum.chistov.pro/index.php?topic=1999.30

      пост 1

      Допустим: в уже имеющемся проведённом документе “Реализация” продан товар “стул” в количестве одна штука, при этом на складе их ноль штук.
      Затем пользователь заходит в документ и меняет, “стул” на “кресло”. Далее нажимает кнопку провести, и запускается обработка проведения.
      В момент выполнения строки: Записать() движение в регистре с позицией “стул” очищается и запишется в базу данных, в результате на остатке на складе появится одна позиция “стул”.
      Далее внутри тойже обработки проведения наложится блокировка на “кресло” и начнётся транзакция.
      При этом, по скольку на складе появился один незаблокированный “стул” на остатке, другой пользователь в это время возьмёт и побыстрому проведёт, этот “стул” в другом документе “реализация” и на складе станет ноль позицый “стул”.
      Затем уже в нашем документе, при попытке списать заменённый стул на кресло, произошла какая-то ошибка, и мы решили откатить транзакцию. т.е. “стул” возвращается на место.
      И в результате таких действий, стул будет в количестве “минус один” на складе.
      Вот для того, чтобы такого не произошло, нужно заблокировать, не только новые позиции, но и старые которые были в документе, до момента окончания текущей транзакции записи, до тех пор, пока мы не будем точно уверены, что не планируется её откат.

      пост 2

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

      Старая методика проведения:
      Движения.ОстаткиТоваров.БлокироватьДляИзменения=Истина;
      Движения.ОстаткиТоваров.Записать();

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

      Перепроводим существующий документ.
      Такс устанавливаем блокировку… Блокируем Что? Регистр. По какому набору записей??? ФИГ знает толи по пустому толи по старому, а истина где то там—————>…… НО в народе говорят что лучше написать, баллы на экзамене не лишние)). Например: регистр остатки товаров пустой и есть расходная (номенклатура:стол, количество: 1). Заходит пользователь и меняет стол на стул жмет Окей и ждет пока закончится транзакция , в этот момент второй юзер тем же документом расходная видит ага появился стол, щас спишу и списывает. Обидно но транзакция не увенчалась успехом и мы ее отменили. В итоге столы полетели в минуса , вместе с баллами на экзамене. Чтоб такого не случилось пишем Движения.ОстаткиТоваров.БлокироватьДляИзменения=Истина;, тем самым блокируем регистр по старому набору записей.(<————-здесь я вообще не уверен блин!!! ХЭЛП!). В итоге второй юзер вместе со своей расходной попадает в очередь, а потом получает отказ. Счастлив экзаменатор, сдающий, целы баллы).

      Второй случай когда надо использовать Движения.ОстаткиТоваров.БлокироватьДляИзменения=Истина;

      Новая методика проведения , у регистра накопления стоит свойство (в значение истина), разделять итоги. В таблицу итогов попадает разделитель который позволяет писать данные нескольким документам у которых одинаковый набор записей (например приходная накладная не должна стоять в очереди), НО гадкий разделитель попадает в блокировку при внесении записей списания. Например проводим одновременно: расходная 1 (блокируем Номенклатура: стол, склад:основной, разделитель1), расходная 2(блокируем Номенклатура: стол, склад:основной, разделитель2). Потом начинаем читать и тут :o. Расходная 1 заблокировала данные позиции и расходная 2 тоже, только по разным разделителям, а читаем остатки мы по всем позициям в целом, в итоге ловим deallock.
      Чтоб такого не получилось пишем злополучное Движения.ОстаткиТоваров.БлокироватьДляИзменения=Истина; отключаем разделитель и вторая расходная попадает в очередь, таким образом первая спокойно все читает и мы радуемся.

      Статья от самого Чистова

      http://1c.chistov.pro/2013/07/blog-post_25.html

      Грязное чтение.
      Что это такое?
      Я не буду вдаваться в теорию (в отличии от вас, вам я советую в теорию копнуть, лишним не будет), поясню на примере:
      Проводим две расходных. И в одной и в другой продаем 5 ложек. А в остатках всего 6. Так получилось, что продавать ложки мы умудрились в один и тот-же момент времени. Что произойдет?
      Первая накладная сформирует движения и начнет читать запрос по остаткам через кэш собственной транзакции, что, собственно, будет делать и вторая накладная. Но ложек-то всего 6, а в сумме две накладные продадут 10… Потому-что не знают о том, что данные в таблицах уже не актуальны…
      Что делать?
      Надо заблокировать данные от параллельного чтения.
      В нашем примере это сделать очень просто. У набора записей есть свойство "БлокироватьДляИзменения". Это, как и свойство "Записывать", лишь маркер указывающий, что необходимо установить блокировку на те записи, которые были сформированы в наборе записей.
      Когда устанавливать свойство "БлокироватьДляИзменения"? Не важно, главное до самой записи данных.
      Когда произойдет блокировка записей? В момент записи данных.
      Что означает эта блокировка?
      Никто параллельно с нами читать данные из регистра по тем товарам, движения по которым были сформированы, не сможет.
      Когда блокировка будет снята?
      При окончании транзакции в которой она началась, в нашем примере, при завершении проведения документа.

    • Евгений Гилев (Мастер-тренер) сказал:

      В этом нет никакой необходимости, БлокироватьДляИзменения роли не сыграет (то есть абсолютно ничего не изменит).

      • Илья сказал:

        Т.е. Вы хотите сказать что данная ситуация не может возникнуть?

        “Например: регистр остатки товаров пустой и есть расходная (номенклатура:стол, количество: 1). Заходит пользователь и меняет стол на стул жмет Окей и ждет пока закончится транзакция , в этот момент второй юзер тем же документом расходная видит ага появился стол, щас спишу и списывает. Обидно но транзакция не увенчалась успехом и мы ее отменили. В итоге столы полетели в минуса”

  4. Дионис сказал:

    Добрый день!
    Подскажите как сделать, чтобы проверка происходила еще и по складам?
    Допустим есть скдлад1 и склад2, в документах приходов и расходов эти позиции указываются.

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

    • Вячеслав Вязигин сказал:

      День добрый!

      Для обоих методик проведения необходимо выполнить общие действия:

      1. В регистр накопления «Свободные остатки» добавляется измерение «Склад».
      2. При проведении документа «Поступление товаров» выполняются движения-приход с учетом склада.

      Движение.Склад = Склад;

      Старая методика контроля остатков

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

      Склад = &Склад

      Запрос.УстановитьПараметр("Склад", Склад);

      2. Выполнение движений в регистры также выполняется с учетом склада.

      Движение.Склад = Склад;

      Новая методика контроля остатков

      При формировании движений-расход регистра учитываем склад.

      Движение.Склад = Склад;

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

      Склад = &Склад

      Запрос.УстановитьПараметр("Склад", Склад);
  5. Федя сказал:

    А подскажите по новой методике ,я правильно понимаю, что если Отрицательные остатки по какой то номенклатуре были до момента проведения нашего документа , то Мы получим ошибку отрицательных остатков и по нашему документу?

    • Вячеслав Вязигин сказал:

      День добрый!

      Совершенно верно, неважно, что было до проведения документа. Суть новой методики именно в контроле отрицательных остатков после формирования движений документа.

      При правильной архитектуре системы и корректном учете наличие отрицательных остатков маловероятно)

  6. I-am-a-programmer.ru сказал:

    Евгений, возник ещё один вопрос, требующий экспертной рецензии:
    а нельзя ли реализовать новую методику на более легком регистре накопления оборотов?
    Сейчас реализую схему: Заказано покупателем -> Закуплено под заказ -> Отгружено. Как мне видится, идеально было бы сделать оборотный регистр Заказы (Изм: Номенклатура, Заказ; Ресурсы: Заказано, Закуплено, Отгружено) и по нему вести учет, сравнивая обороты при проведении (что бы контролировать: Отгружено <= Закуплено <= Заказано).
    Свойство БлокироватьПриИзменении при проведении блокирует в регистре необходимые сочетания Заказ – Номенклатура из табличной части документов закупки и отгрузки (как я понимаю это свойство), что так же способствует реализации этого механизма.

    Прокомментируйте, пожалуйста, такой подход.

    • Евгений Гилев (Мастер-тренер) сказал:

      В общем случае такой подход не является правильным – когда вместо регистра остатков мы используем регистр оборотов с двумя ресурсами, в которых хранится приход и расход.

      Теперь по Вашей ситуации.
      Скорее всего Вам будет требоваться не только обороты, но и остатки – в отчетах по заказам, поставке под заказ и т.д.
      Получается, что остаток у вас будет вычисляться.

      Далее, чтобы получить данные по остатку (в общем случае) Вам нужно будет рассчитывать обороты за большой период, что может быть не оптимальным.

  7. SegaZX сказал:

    Здравствуйте. Спасибо за полезную статью. Я вот только один момент не понял:
    1. Если у нас есть 2 регистра накопления:
    -ОстаткиНоменклатуры (Измерение:Номенклатура и ресурс “Количество”) Тут мы храним остатки.
    -СтоимостьНоменклатуры (Измерения: Номенклатура и ресурс “Количество” и “Сумма”). а тут мы храним стоимость.
    то все понятно, мы используем новую методику, остатки проверяем из регистра “ОстаткиНоменклатуры”, Записываем в него движения документа, затем проверяем не ушли ли мы в минус.
    2. А вот если у нас для хранения остатков используется регистр “ОстаткиНоменклатуры”, где Измерения “Номенклатура”, “Партия” и ресурс “Количество”?? или где два ресурса “Количество” и “Сумма”?
    в этом случае мы ведь не можем записать движения в регистр, т.к. мы еще не знаем значение “Партия”. или для того, чтобы в общем проверить ИТОГО (по всем партиям) хватит количества или нет, можно записать движения с пустой партией? и если хватает количество, то уже формировать алгоритм по списанию стоимости по партиям?
    спасибо. Или же нет?

    • Евгений Гилев (Мастер-тренер) сказал:

      Добрый день!

      Для использования новой методики нужно использовать 2 регистра.

      Вообще, нужно понимать, что в общем случае 2 регистра (остатки + себестоимость) оптимальнее одного.

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

      Поэтому оптимально данные по остаткам на складе хранить в отдельном регистре с небольшим количеством измерений.

      • SegaZX сказал:

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

        • Евгений Гилев (Мастер-тренер) сказал:

          Да, новую методику имеет смысл применять, если остатки и себестоимость хранятся в отдельных регистрах.

          Это, например, слабо применимо для регистра бухгалтерии, где обычно и остатки и себестоимость хранятся на одном счете БУ.
          Поэтому для бух. задач зачастую нужно использовать старую методику (если контроль остатков там вообще требуется).

  8. Николай сказал:

    Полезная статья, спасибо!
    Единственный вопрос остался: можно ли (нужно ли для экзамена?) сообщение о нехватке номенклатуры выводить с привязкой к ТЧ как в старой методике?

    • Евгений Гилев (Мастер-тренер) сказал:

      Точных сведений о том, что за это снижают оценку у нас нет.
      Но имеет смысл реализовать такой функционал. Много времени это не займет :)

  9. Варвар2 сказал:

    Есть новое предложение: В контроль остатков документе не писать, а написать его в самом регистре. Используя модуль набора записей и процедуры ПередЗаписью(Отказ, Замещение) и
    ПриЗаписи(Отказ, Замещение)

    Для одного регистра я уже написал, вроде работает
    Для второго пишу сейчас, вроде получается.

    Пытался выслать код, но не получилось.

    Выгода этого метода очевидно, ибо документов желающих записать что то, в регистр много, и написаны они разными людьми. И не во всех документах правильно написана защита.

    А если защита написана в самом регистре, то не важно что там написано в самом документе!

    • Евгений Гилев (Мастер-тренер) сказал:

      Добрый день!

      Ваш код прошел, он в комментарии ниже.

      Описанный Ваши подход имеет право на существование. Более того, в ряде случае в типовых решениях применяется подобный подход.

      Но всё-таки в подавляющем большинстве документов контроль выполняется в модуле объекта (а точнее в методах общих модулей, которые вызываются из модуля объекта.
      Это связано с тем, что обычно проверка при проведении документа выполняется не по одному регистру, а по нескольким. При использовании Вашего подхода, проверки бы выполнялись в модулях наборов записей этих регистров.

      А это значит, что посылалось бы несколько запросов в БД. Что приведет к снижению производительности системы на больших объемах данных.

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

  10. Варвар2 сказал:

    А если контроль остатков повесить на сам регистр?
    Примерно так:
    Есть регистр накопления СБК_СодержаниеДрагметалловВНоменкре
    В модуле набора записей регистра написать следующее:

    Процедура ПередЗаписью(Отказ, Замещение)

    Документ=ЭтотОбъект.Отбор.регистратор.Значение; //Выясним какой документ вносит изменения?

    Если ЭтотОбъект.Количество()=0 Тогда
    ПриОтменеПроведения=Новый Запрос;
    ПриОтменеПроведения.Текст=”ВЫБРАТЬ
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Организация,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Подразделение,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Номенклатура,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Драгметалл,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.ПартияДМ,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Склад,
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Сотрудник,
    |ЕСТЬNULL(ОстаткиАктуальные.КоличествоОстаток, 0) – СБК_СодержаниеДрагметалловВНоменклатуреОбороты.КоличествоПриход + СБК_СодержаниеДрагметалловВНоменклатуреОбороты.КоличествоРасход КАК Остаток
    |ИЗ
    |РегистрНакопления.СБК_СодержаниеДрагметалловВНоменклатуре.Обороты(&Dat0, &Dat0, Регистратор, ) КАК СБК_СодержаниеДрагметалловВНоменклатуреОбороты
    |ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СБК_СодержаниеДрагметалловВНоменклатуре.Остатки КАК ОстаткиАктуальные
    |ПО СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Организация = ОстаткиАктуальные.Организация
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Подразделение = ОстаткиАктуальные.Подразделение
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Номенклатура = ОстаткиАктуальные.Номенклатура
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Драгметалл = ОстаткиАктуальные.Драгметалл
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.ПартияДМ = ОстаткиАктуальные.ПартияДМ
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Склад = ОстаткиАктуальные.Склад
    |И СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Сотрудник = ОстаткиАктуальные.Сотрудник
    |ГДЕ
    |СБК_СодержаниеДрагметалловВНоменклатуреОбороты.Регистратор = &Регистратор
    |
    |УПОРЯДОЧИТЬ ПО
    | Остаток”;
    ПриОтменеПроведения.УстановитьПараметр(“Dat0”,Документ.Дата);
    ПриОтменеПроведения.УстановитьПараметр(“Регистратор”,Документ);
    ТЗ=ПриОтменеПроведения.Выполнить().Выгрузить();
    Для Каждого ё Из ТЗ Цикл //Проверяем актуальные остатки
    Если ё.Остаток<0 Тогда
    Тхт="СБК_СодержаниеДрагметалловВНоменкре.ПередЗаписью() /Подразделение="+ё.Подразделение+"/Номнклатура="+ё.Номенклатура+"/Драгметалл="+ё.Драгметалл+"/ПартияДМ="+ё.ПартияДМ+"/Склад="+ё.Склад+"/Сотрудник="+ё.Сотрудник+"//Остаток="+ё.Остаток;
    Сообщить(Тхт);
    Отказ=Истина;
    КонецЕсли;

    КонецЦикла;

    КонецЕсли; //ЭтотОбъект.Количество()=0

    КонецЕсли; //Константы.КонтрольИсточниковФинансирования.Получить()
    КонецПроцедуры

    Процедура ПриЗаписи(Отказ, Замещение) //00 Бобейко 25.05.2017
    Если Константы.КонтрольИсточниковФинансирования.Получить() Тогда
    //Проверим Остатки
    ТЗ = ЭтотОбъект.Выгрузить();
    ТЗ.Свернуть("Период,ВидДвижения,Организация,Подразделение,Номенклатура,Драгметалл,ПартияДМ,Склад,Сотрудник","Количество");

    Для Каждого ё из ТЗ Цикл
    Если ё.ВидДвижения=ВидДвиженияНакопления.Расход Тогда
    ё.Количество=-ё.Количество;
    КонецЕсли;
    КонецЦикла;
    Запрос=Новый Запрос;
    Запрос.Текст="ВЫБРАТЬ
    | ТЗ.Организация,
    | ТЗ.Подразделение,
    | ТЗ.Номенклатура,
    | ТЗ.Драгметалл,
    | ТЗ.ПартияДМ,
    | ТЗ.Склад,
    | ТЗ.Сотрудник,
    | ТЗ.Количество
    |ПОМЕСТИТЬ Движения
    |ИЗ
    | &ТЗ КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |ЕСТЬNULL(СБК_СодержаниеДрагметалловВНоменклатуреОстатки.КоличествоОстаток, 0) + Движения.Количество КАК Остаток,
    | Движения.Организация,
    | Движения.Подразделение,
    | Движения.Номенклатура,
    | Движения.Драгметалл,
    | Движения.ПартияДМ,
    | Движения.Склад,
    | Движения.Сотрудник
    |ИЗ
    | Движения КАК Движения
    | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СБК_СодержаниеДрагметалловВНоменклатуре.Остатки(&Dat, ) КАК СБК_СодержаниеДрагметалловВНоменклатуреОстатки
    | ПО Движения.Организация = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Организация
    | И Движения.Подразделение = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Подразделение
    | И Движения.Номенклатура = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Номенклатура
    | И Движения.Драгметалл = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Драгметалл
    | И Движения.ПартияДМ = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.ПартияДМ
    | И Движения.Склад = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Склад
    | И Движения.Сотрудник = СБК_СодержаниеДрагметалловВНоменклатуреОстатки.Сотрудник
    |
    |УПОРЯДОЧИТЬ ПО
    | Остаток";

    Запрос.УстановитьПараметр("ТЗ",ТЗ);
    Запрос.УстановитьПараметр("Dat",ЭтотОбъект.Отбор.регистратор.Значение.Дата);
    ё=Запрос.Выполнить().Выбрать();
    Пока ё.Следующий() Цикл //Проверяем остатки на дату документа
    если ё.Остаток<0 Тогда
    Тхт="СБК_СодержаниеДрагметалловВНоменкре.ПриЗаписи() /Подразделение="+ё.Подразделение+"/Номнклатура="+ё.Номенклатура+"/Драгметалл="+ё.Драгметалл+"/ПартияДМ="+ё.ПартияДМ+"/Склад="+ё.Склад+"/Сотрудник="+ё.Сотрудник+"//Остаток="+ё.Остаток;
    Сообщить(Тхт);
    Отказ=Истина;
    КонецЕсли;

    КонецЦикла;

    КонецЕсли
    КонецПроцедуры

    В этом случае во всех документах работающих с регистром (а их 12 штук) можно ничего не писать, ибо регистр сам следит за своими остатками.
    Есть правда недостатки например
    // 1Документ 15.05.2017 Приход 15 шт Х
    // 2Документ 20.05.2017 Списание 8 шт Х
    // 3Документ 27.25.2017 Приход 20 шт Х
    То теперь можно отменить проведение 1Документа

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

  11. Александр сказал:

    При параллельном проведении документа получаю ошибку:

    по причине:
    Ошибка выполнения запроса
    по причине:
    Конфликт блокировок при выполнении транзакции:
    Microsoft SQL Server Native Client 11.0: Превышено время ожидания запроса на блокировку.
    HRESULT=80040E31, SQLSrvr: SQLSTATE=HYT00, state=33, Severity=10, native=1222, line=1

    В запросе выбираются документы проводимого типа.
    При проведении документа блокируется вся таблица документа? И ее нельзя прочитать в соседней транзакции?

    • Евгений Гилев (Мастер-тренер) сказал:

      Совершенно непонятно о какой конфигурации и каких документах идет речь.
      Как выполняется блокировка, какие комбинации измерений блокируются, есть ли пересечения в блокируемых изменениях в Ваших документах – это те вопросы, на которые Вы должны ответить, чтобы понять источник проблемы.

      А ведь есть еще режим блокировок – автоматический или управляемый.

      При проведении документа блокируется вся таблица документа?

      Нет, не блокируется.

      • Александр сказал:

        Конфигурация нетиповая. Как воспроизводится. В процедуре ОбработкаПроведения есть сокращенно такой запрос “Выбрать Документы.Ссылка из Документы.РегистрацияПотребленияУслугПоОбъектамПотреблениеУслугПоОбъектам Как документы”. Провожу документ1, в процедуре ОбработкаПроведения ставлю точку останова. В соседней сессии провожу другой документ, и ошибка выходит на выполнении этого запроса, из чего предположил что блокируется таблица документов. Что интересно, открываю 3 сессию и там выполняю этото запрос в консоли, он выполняется.

        • Евгений Гилев (Мастер-тренер) сказал:

          Что-то конкретное по Вашей ситуации подсказать не удастся – мало данных.
          Рекомендую пройти курс по Ускорению и Оптимизации 1С, чтобы самостоятельно решить проблему: http://курсы-по-1с.рф/1c-v8/optimization/

          Документы должны проводится параллельно, если у в них нет пересекающихся блокируемых наборов данных.

          P.S.

          Имя документ “РегистрацияПотребленияУслугПоОбъектамПотреблениеУслугПоОбъектам” тянет на рекорд :)

  12. Никита сказал:

    Можно ли в старой методике не очищать предварительно движения, а получать данные об остатках на момент времени с видом границы “исключая”?

    • Евгений Гилев (Мастер-тренер) сказал:

      Нет, это не спасет.
      Проблема будет, когда дата документа сдвинута вперед.
      Например, документ проведен в 9:00:00, через час в этот документ возвращается и меняют время на 10:00:00 (или просто перепроводят оперативно).

      • Артур сказал:

        Правильно ли я понимаю, что платформа при оперативном проведении, несмотря на явное указание в установке параметров запроса границы на которую ей необходимо читать остатки, в целях оптимизации прочитает их на 31.12.3999, иными словами обратится напрямую к таблице итогов без соединения последней с таблицей движения.
        Таким образом, во избежание излишних блокировок при записи следует анализировать режим проведения и в случае его оперативности записывать пустой набор записей перед чтением остатков.
        Пример кода:
        Запрос.УстановитьПараметр(“МоментВремени”,МоментВремени());
        Если Режим = РежимПроведенияДокумента.Оперативный Тогда
        Движения.СтоимостьНоменклатуры.Записать();
        КонецЕсли;

        Ну и сразу вытекающий запрос, а где же работает такая подмена
        воли и как давно?

        Раньше насколько я понимаю было актуально писать следующее:
        МоментВремени = ?(Режим = РежимПроведенияДокумента.Оперативный, Неопределено, МоментВремени());
        Запрос.УстановитьПараметр(“МоментВремени”,МоментВремени());

        • Вячеслав Вязигин сказал:

          День добрый!

          Вы неправильно понимаете. Платформа оперирует текстом запроса, который написал разработчик, независимо от оперативности проведения документа. Год 3999 подставляется в параметр запроса автоматически только в том случае, когда запрос не имеет параметра даты на условие виртуальной таблицы остатков.

          Записывать пустой набор записей или нет зависит от методики проведения (старая или новая) документа. Не ищите никаких скрытых смыслов и «подмены воли», на все воля разработчика :)

  13. ShootNICK сказал:

    Запрос.Текст =
    “ВЫБРАТЬ
    | Остатки.Номенклатура КАК Номенклатура,
    | -Остатки.КоличествоОстаток КАК Дефецит

    дефИцит, извините )

  14. nvv1970 сказал:

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

    • Евгений Гилев (Мастер-тренер) сказал:

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

  15. Яков сказал:

    Всегда было интересно, зачем данные табличной части конкретного документа получать отдельным запросом, если они уже есть в прочитанном объекте. Ну вот не понимаю. Документ-объект уже в кэше, идёт объектная блокировка, нафига ещё таблицу БД дёргать? В сеансовые данные вывалили, таблицей значений дубли скрутили, параметром в другие запросы сунули. Где я неправ?

    • Евгений Гилев (Мастер-тренер) сказал:

      Можно пойти предложенным путем.

      Но какого-либо заметного ускорения Вы не получите.

      И вот почему. Вам всё равно ведь придется дергать СУБД чтобы создать временную таблицу.
      Да не надо будет обращаться к табличной части документа, но это обращение выполняется с условием по ссылке, а по ней всегда есть индекс – это условие отрабатывает очень быстро.

      • Яков сказал:

        Понял.
        В ряде случаев можно и без временной таблицы обойтись. Если я задам условие на вхождение в список, и его передам как параметр в запрос, это ведь не временная. А учитывая нынешнюю архитектуру, когда, например, “Номенклатура+Характеристика+Серия” = “Аналитика”, сделаю отбор по Аналитика В (&СписокИзТабЧасти) и всё.

        • Евгений Гилев (Мастер-тренер) сказал:

          Да, в ряде случаев можно обойтись без временной таблицы.

  16. topasha сказал:

    Спасибо за статью, очень интересно!
    Попробовал на живых людях ;)
    В самописной конфе есть подходящий регистр (два измерения, один ресурс). Использовался “старый” способ контроля остатков. Работало вполне сносно. Попробовал на нём “новый” способ контроля остатков, переписал обработку проведения. Чисто субъективно, по скорости работы никакой разницы не заметил. Регистр остатков используется интенсивно, работает более 60 пользователей одновременно. Только строк кода добавилось. Стоило ли заморачиваться с апгрейдом?

    • Евгений Гилев (Мастер-тренер) сказал:

      Реальное применение знаний – это всегда правильно.

      Разницу при собственном тестировании одним пользователем Вы вряд ли заметите.
      Даже, если запускаете клиент-серверный вариант.

      Поэтому имеет смысл еще раз проверить алгоритм и запустить в продакшн – посмотреть результат. Если сильно заморочиться, можно и сравнить производительность обоих способов :)
      Но вполне возможно результат будет виден на глаз, если при старом способе когда-нибудь возникли сложности, связанные с проведением этого документа.

      Можно пойти и другим путем – поставить 1С:ТестЦентр и сэмулировать интенсивную работу нескольких пользователей и оценить результаты :)

  17. Павел сказал:

    Всегда интересовало как реализовано ФИФО в рамках новых типовых. По старой методике? Ведь там нужно обращение у данным регистра

    • Евгений Гилев (Мастер-тренер) сказал:

      В 1С:Бухгалтерии – по старой методике.

      В УТ 11 – по новой.
      Фокус УТ 11 в том, что списание партий происходит НЕ в момент проведения выбытия товаров, а в момент закрытия месяца.

      • Яков сказал:

        Это как? Тогда получается, что данные партионного учёта неактуальны вплоть до закрытия месяца? И нормальной себестоимости ожидать не приходится?

        • Евгений Гилев (Мастер-тренер) сказал:

          Закрытие месяца не обязательно выполнять 1 раз в месяц.
          Настраивается регламентное задание и выбирается требуемая периодичность – раз в день, каждый час или чаще.
          Конечно это создает определенную нагрузку на сервер, поэтому приемлемое значение выбирается по двум критериям – требуемая актуальность данных и допустимой нагрузки на кластер серверов.

  18. Nikolay сказал:

    Здравствуйте!
    Спасибо за статью!

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

    • Евгений Гилев (Мастер-тренер) сказал:

      Добрый день!

      Видимо речь о старой методики контроля остатков.
      Кроме контроля остатков в выборке мы производим формирование движений. Дельту можно было бы посчитать и в запросе, но принципиальных изменений это не даст.

      Если же Вы предлагаете создать еще один запрос, который возвращает ошибочные записи, то такой подход в общем случае будет хуже – из-за “накладных” расходов.

  19. Четверг сказал:

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

    • Четверг сказал:

      3) ?(Режим = РежимПроведенияДокумента.Оперативный,
      Неопределено,…). Это вроде бы тоже на экзаменах не пишут, а передают либо Момент либо Границу. Не уверен. Вроде как советы от экзаменаторов. Могу ошибаться.

      • Евгений Гилев (Мастер-тренер) сказал:

        Даже, если это на экзамене не обязательно – в реальной жизни это желательное действие.
        Поскольку в этом случае остатки для оперативно проводимых документов будут получаться максимально быстро.

    • Евгений Гилев (Мастер-тренер) сказал:

      1. Это желательно делать.
      Набор записей может быть предварительно прочитан. Например, в событии ПередЗаписью или на форме документа.
      2. Метод Движения.Записать() записывает только те наборы записей, у которых свойство Записывать равно Истина.
      Более того, после записи этот метод сбрасывает свойство Записывать в Ложь.
      Поэтому свойство это нужно устанавливать дважды, если речь идет о старой методике контроля остатков.

      • Елена сказал:

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

        • Евгений Гилев (Мастер-тренер) сказал:

          А каким образом Вы выполняете запись в регистр?

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

          • Четверг сказал:

            почему править так – Движения.Записать()? Почему не рекомендуете очищать таким образом – Движения.ИмяРегистра.Записать(), а записывать Движения.ИмяРегистра.Записывать = Истина;?

            • Четверг сказал:

              почему это хуже?:
              -Движения.ИмяРегистра.Очистить();
              -Движения.ИмяРегистра.Записать();
              -Движения.ИмяРегистра.Записывать = Истина;

            • Евгений Гилев (Мастер-тренер) сказал:

              Потому что при использовании записи не через Движения.Записать() есть опасность взаимоблокировки.
              Посмотрите описание области кода “1. Очистка старых движений регистра” в этой статье.

              Кроме того, предлагаемые Вами метод может быть признан ошибкой на экзамене (со снижением оценки).

              • Четверг сказал:

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

                • Евгений Гилев (Мастер-тренер) сказал:

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

                  А если быть более точным, то накладываются две блокировки – исключительная управляемая блокировка (в кластере серверов 1С) и X блокировка СУБД.

              • Asta0000 сказал:

                “Потому что при использовании записи не через Движения.Записать() есть опасность взаимоблокировки.” Источник: ©Курсы-по-1С.рф

                Т.е. если использовать Движения.Записать() взаимоблокировки не будет? Например в таком коде:
                Первый документ
                Движения.СвободныеОстатки.Записывать = Истина;
                Движения.Записать();

                Движения.Себестоимость.Записывать = Истина;
                Движения.Записать();

                Второй документ Движения.Себестоимость.Записывать = Истина;
                Движения.Записать();

                Движения.СвободныеОстатки.Записывать = Истина;
                Движения.Записать();

                Если не будет – то почему, а если будет, то надо либо подробнее об этом либо не упоминать

          • Елена сказал:

            Спасибо за разъяснение, использовала Движения.ИмяРегистра.Записать().

  20. polax сказал:

    Спасибо за статью. Возник технический вопрос. А если дата документа смещается не вперед, а назад? Очистка регистра не нужна?

    • Евгений Гилев (Мастер-тренер) сказал:

      Не нужна, поскольку будущие движения не повлияют на прошлые остатки.

  21. Василий сказал:

    Вопрос снимаю, так как блокировки рассматриваются в следующей статье. Но, думаю, надо сказать это.

    • Евгений Гилев (Мастер-тренер) сказал:

      Вроде как об этом сказано в специальном разделе “Но это ещё не все!”.

  22. Сергей сказал:

    Есть вопрос по старой методике проведения. Зачем чистить движения документа раз это такая тяжелая операция? Что нам мешает в запросе остатков получить движения нашего документа и вычесть их из остатков, проверив период движений. Добавится одна выборка из регистра с отбором по регистратору (очень быстрая операция, регистратор индексирован), одно соединение/объединение с остатками(обычно строк документа гораздо меньше чем записей в регистре и соединить таблицы это легкая операция)

    • Евгений Гилев (Мастер-тренер) сказал:

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

  23. gosn1ck сказал:

    Добрый день.
    спасибо за статью!
    слишком строгая получилось фраза “Поэтому единственный правильный вариант – использовать вид границы «Включая»”, ведь момент времени можно и не указывать вовсе.
    на одном из чудных проектов пришлось делать вообще 2 проверки и на момент времени и без момента … всё как всегда зависит от задачи

    • Евгений Гилев (Мастер-тренер) сказал:

      Добрый день!

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

      А на проектах ситуации бывают действительно разные :)

  24. Alex_grem сказал:

    Т.е. в УТ 11 контроль остатков идет по регистру “Свободные остатки” новой методикой или по регистру “Себестоимость товаров” тоже идет контроль?

  25. kalamber сказал:

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

    Критерий применимости — если для формирования движений нет необходимости обращаться к данным контролируемого регистра Источник: ©Курсы-по-1С.рф

    Или имеется ввиду отсутствие левого соединения?

    • Евгений Гилев (Мастер-тренер) сказал:

      Добрый день!

      Цитата:

      Критерий применимости — если для формирования движений нет необходимости обращаться к данным контролируемого регистра.

      Ключевой момент выделил жирным.

      В новой методике прежде, чем сформировать движения по регистру “Свободные остатки” нет необходимости обращаться к этому регистру.

  26. Василий сказал:

    В “Сообщение.Текст = “Товара “+ВыборкаТовары.Номенклатура+” недос”
    вы выводите ссылку, а это неправильно. Получается запрос в цыкле. Надо использовать представление. Или я ошибаюсь?

    • Евгений Гилев (Мастер-тренер) сказал:

      С одной стороны – да, это неплохо.
      С другой стороны, Вы удивитесь, но в типовой УТ 11/ERP/КА выводимая в сообщении об ошибке номенклатура получается так – СокрЛП(Выборка.Номенклатура), где Выборка.Номенклатура – это ссылка.

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

      • Алексей сказал:

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

        • Евгений Гилев (Мастер-тренер) сказал:

          Посмотрите на комментарий ниже, от palsergeich.

    • palsergeich сказал:

      Получение основного представления на сервере ничтожная по затратам операция, ибо у ссылочных типов отдельный индекс на это, ибо даже на высоконагруженной базе при выводе нескольких сотен тысяч представлений по ссылке в ТабДок, сколько нибудь криминального замедления замечено не было, при сравнении с выводом сразу представления. А уж тем более в диагностике контроля остатков, где возникновение такой ситуации считается маловероятным событием, тратить время на то что бы вспомнить, как называется это поле в запросе – не оправдано что ли, да и более того, основное представление может не входить в используемый индекс и привести к key lookup или скану.
      Конкретно в этом примере для того что бы вычислить ПРЕДСТАВЛЕНИЕССЫЛКИ, будет соединение с таблицей номенклатуры, потому что ссылка на номенклатуру, ничего не знает о представлении, оно хранится в своей таблице.
      Это скорее как рекомендация всегда индексировать поля соединения временных таблиц, сколько не тестировал – лучший результат – совпадение по времени с отсутствием индексирования, в остальных случаях – увеличение времени работы запроса. Все рекомендации которые дает 1с – это общая практика, но не всегда это правильно.

      Текст запроса в профайлере без представления
      exec sp_executesql N’SELECT
      T1.Fld58RRef,
      T1.Fld59Balance_
      FROM (SELECT
      T2._Fld58RRef AS Fld58RRef,
      T2._Fld59 AS Fld59Balance_
      FROM dbo._AccumRgT60 T2 WITH(NOLOCK)
      WHERE T2._Period = @P1 AND (T2._Fld59 @P2) AND (T2._Fld59 @P3)) T1′,N’@P1 datetime2(3),@P2 numeric(10),@P3 numeric(10)’,’5999-11-01 00:00:00′,0,0

      Текст запроса в профайлере с представлением
      exec sp_executesql N’SELECT
      T1.Fld58RRef,
      T1.Fld58RRef,
      T3._Description,
      T1.Fld59Balance_
      FROM (SELECT
      T2._Fld58RRef AS Fld58RRef,
      T2._Fld59 AS Fld59Balance_
      FROM dbo._AccumRgT60 T2 WITH(NOLOCK)
      WHERE T2._Period = @P1 AND (T2._Fld59 @P2) AND (T2._Fld59 @P3)) T1
      LEFT OUTER JOIN dbo._Reference12 T3 WITH(NOLOCK)
      ON T1.Fld58RRef = T3._IDRRef’,N’@P1 datetime2(3),@P2 numeric(10),@P3 numeric(10)’,’5999-11-01 00:00:00′,0,0

      Выводы делайте сами.

  27. Дмитрий сказал:

    Очень интересная и полезная статья,
    но

    //  7. Установка флага записи движений в конце транзакции
         
    Движения.СвободныеОстатки.Записывать = Истина;

    необходим только если у документа выбрано свойство
    Запись движений при проведении:Записывать выбранные.
    В случае если определено:Записывать модифицированные
    он не обязателен

    • Евгений Гилев (Мастер-тренер) сказал:

      Не хотелось перегружать статью деталями, учитывая, что:
      1. В типовых этот флаг у документов установлен в “Записывать выбранные”
      2. В новой ИБ по умолчанию у документов указывается свойство “Записывать выбранные”.

      • Дмитрий сказал:

        Солидарен с вами, но когда у меня принимали экзамен, товарищ экзаменатор плотно прошелся по этому вопросу.

        • Евгений Гилев (Мастер-тренер) сказал:

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

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

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

Мы используем файлы cookies, чтобы сделать сайт удобнее.
Продолжая просмотр сайта, Вы соглашаетесь с их использованием.
Подробнее