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

База знаний Одина — Одинэсника › Форумы › ODIN — Форум по 1С Предприятию › Почему дважды устанавливается признак Записывать в ОбработкаПроведения?

Просмотр 1 ветки ответов
  • Автор
    Записи
    • #38852
      Фото аватараOdineski
      Участник

        Процедура ОбработкаПроведения(Отказ, Режим)

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

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

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

        //{{КОНСТРУКТОР_ЗАПРОСА_С_ОБРАБОТКОЙ_РЕЗУЛЬТАТА
        // Данный фрагмент построен конструктором.
        // При повторном использовании конструктора, внесенные вручную изменения будут утеряны!!!

        Запрос = Новый Запрос;
        Запрос.Текст =
        «ВЫБРАТЬ
        | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура,
        | СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество
        |ПОМЕСТИТЬ втТЧТовары
        |ИЗ
        | Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
        |ГДЕ
        | РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
        |
        |СГРУППИРОВАТЬ ПО
        | РасходнаяНакладнаяСписокНоменклатуры.Номенклатура
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        | втТЧТовары.Номенклатура КАК Номенклатура,
        | втТЧТовары.Номенклатура.Представление КАК НоменклатураПредставление,
        | втТЧТовары.Количество КАК Количество,
        | ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток
        |ИЗ
        | втТЧТовары КАК втТЧТовары
        | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
        | &МоментВремени,
        | Номенклатура В
        | (ВЫБРАТЬ
        | втТЧТовары.Номенклатура КАК Номенклатура
        | ИЗ
        | втТЧТовары КАК втТЧТовары)
        | И Склад = &Склад) КАК ОстаткиНоменклатурыОстатки
        | ПО втТЧТовары.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура»;

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

        РезультатЗапроса = Запрос.Выполнить();

        Выборка = РезультатЗапроса.Выбрать();

        Пока Выборка.Следующий() Цикл
        Если Выборка.Количество > Выборка.КоличествоОстаток Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = «Не хватает товара «
        + Выборка.НоменклатураПредставление
        + «. Требуется: «
        + Строка(Выборка.Количество)
        + «, остаток: «
        + Строка(Выборка.КоличествоОстаток);
        Сообщение.Сообщить();

        Отказ = Истина;
        Продолжить;
        КонецЕсли;

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

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

        Зачем здесь в начале 2 раза записываются движения? И что конкретно делает строка Движения.Записать();? Зачем мы записываем движения, которые по идее должны заменить новыми при перезаписи документа?

      • #38853
        bot_avaOdin — Одинэсник
        Участник

          Здесь движения записываются не два раза. Фактически метод записи вызывается только один раз:

          Первая и третья строки только устанавливают признак Записывать.

          ## Что делает Движения.Записать()

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

          Строка:

          записывает в регистры все наборы движений, у которых установлено:

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

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

          Именно это здесь и используется:

          Перед началом проведения удаляются старые движения текущего документа.

          ## Зачем удалять старые движения перед проверкой остатков

          Рассмотрим ситуацию.

          Документ был проведен ранее:

          | Документ | Номенклатура | Движение |
          |—|—|—:|
          | Расходная накладная №1 | Товар А | Расход 10 |

          Затем пользователь изменил документ, например:

          При повторном проведении нужно проверить наличие 15 единиц, а не учитывать старый расход 10 единиц как уже существующее движение этого же документа.

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

          Хотя фактически доступно 100 единиц до проведения текущего документа, проверка будет выполняться против 90.

          Поэтому старые движения текущего документа сначала очищаются, затем проверяется остаток, после чего формируются новые движения.

          Последовательность такая:

          ## Почему после Движения.Записать() снова устанавливается Записывать = Истина

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

          означает:

          > Теперь новые движения, которые будут добавлены в этот набор, также нужно записать при завершении проведения.

          Дальше в цикле добавляется новое движение:

          Так как Записывать снова установлено в Истина, платформа запишет эти движения после успешного проведения документа.

          То есть это не вторая запись в регистр, а повторная установка режима записи набора движений.

          ## Что происходит при перепроведении документа

          При перепроведении платформа работает примерно по такой схеме:

          Если во время проведения установить:

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

          ## Важный момент по вашему коду

          Фрагмент:

          логически означает:

          Однако в таком виде код выглядит неочевидно. Его лучше снабдить комментариями:

          ## Можно ли не вызывать Движения.Записать() в начале

          В данном алгоритме нежелательно.

          Если убрать:

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

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

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

          ## Более современный и безопасный вариант

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

          ## Еще один нюанс: Движения.Записать() записывает не обязательно все регистры

          Вызов:

          обрабатывает наборы движений, для которых включен признак:

          Например:

          В таком случае вызов Движения.Записать() затронет ОстаткиНоменклатуры, но не будет записывать движения регистра Продажи.

          В вашем коде повторная строка:

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

      Просмотр 1 ветки ответов
      • Для ответа в этой теме необходимо авторизоваться.
      База знаний 1С