База знаний Одина — Одинэсника › Форумы › ODIN — Форум по 1С Предприятию › Почему дважды устанавливается признак Записывать в ОбработкаПроведения?
- В этой теме 1 ответ, 2 участника, последнее обновление 19 минут назад сделано
Odin — Одинэсник.
-
АвторЗаписи
-
-
17 октября 2026 в 16:48 #38852
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ОстаткиНоменклатуры.Записывать = Истина;
Движения.Записать();Движения.ОстаткиНоменклатуры.Записывать = Истина;
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(«РегистрНакопления.ОстаткиНоменклатуры»);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение(«Склад», Склад);
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Номенклатура»,»Номенклатура»);
Блокировка.Заблокировать();//{{КОНСТРУКТОР_ЗАПРОСА_С_ОБРАБОТКОЙ_РЕЗУЛЬТАТА
// Данный фрагмент построен конструктором.
// При повторном использовании конструктора, внесенные вручную изменения будут утеряны!!!Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество
|ПОМЕСТИТЬ втТЧТовары
|ИЗ
| Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
|ГДЕ
| РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втТЧТовары.Номенклатура КАК Номенклатура,
| втТЧТовары.Номенклатура.Представление КАК НоменклатураПредставление,
| втТЧТовары.Количество КАК Количество,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток
|ИЗ
| втТЧТовары КАК втТЧТовары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| втТЧТовары.Номенклатура КАК Номенклатура
| ИЗ
| втТЧТовары КАК втТЧТовары)
| И Склад = &Склад) КАК ОстаткиНоменклатурыОстатки
| ПО втТЧТовары.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура»;Запрос.УстановитьПараметр(«МоментВремени», МоментВремени());
Запрос.УстановитьПараметр(«Склад», Склад);
Запрос.УстановитьПараметр(«Ссылка», Ссылка);РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Если Выборка.Количество > Выборка.КоличествоОстаток Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = «Не хватает товара «
+ Выборка.НоменклатураПредставление
+ «. Требуется: «
+ Строка(Выборка.Количество)
+ «, остаток: «
+ Строка(Выборка.КоличествоОстаток);
Сообщение.Сообщить();Отказ = Истина;
Продолжить;
КонецЕсли;Движение = Движения.ОстаткиНоменклатуры.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = Выборка.Номенклатура;
Движение.Склад = Склад;
Движение.Количество = Выборка.Количество;
КонецЦикла;КонецПроцедуры
Зачем здесь в начале 2 раза записываются движения? И что конкретно делает строка Движения.Записать();? Зачем мы записываем движения, которые по идее должны заменить новыми при перезаписи документа?
-
17 октября 2026 в 16:48 #38853
Здесь движения записываются не два раза. Фактически метод записи вызывается только один раз:
1С (Код)1234Движения.ОстаткиНоменклатуры.Записывать = Истина;Движения.Записать();Движения.ОстаткиНоменклатуры.Записывать = Истина;Первая и третья строки только устанавливают признак
Записывать.## Что делает
Движения.Записать()Движенияпредставляет собой коллекцию наборов записей по регистрам, которые связаны с текущим документом.Строка:
1С (Код)1Движения.Записать();записывает в регистры все наборы движений, у которых установлено:
1С (Код)1Движения.ОстаткиНоменклатуры.Записывать = Истина;В данном случае в регистр накопления
ОстаткиНоменклатурызаписывается текущее состояние набора движений документа.Если набор движений на этот момент пустой, то старые движения документа по этому регистру удаляются.
Именно это здесь и используется:
1С (Код)12Движения.ОстаткиНоменклатуры.Записывать = Истина;Движения.Записать();Перед началом проведения удаляются старые движения текущего документа.
## Зачем удалять старые движения перед проверкой остатков
Рассмотрим ситуацию.
Документ был проведен ранее:
| Документ | Номенклатура | Движение |
|—|—|—:|
| Расходная накладная №1 | Товар А | Расход 10 |Затем пользователь изменил документ, например:
1С (Код)12Было: Товар А, количество 10Стало: Товар А, количество 15При повторном проведении нужно проверить наличие 15 единиц, а не учитывать старый расход 10 единиц как уже существующее движение этого же документа.
Если старое движение документа не удалить перед выполнением запроса остатков, запрос может увидеть остаток с учетом старого расхода текущего документа. В результате получится примерно такая логика:
1С (Код)1234Реальный остаток до проведения: 100Старый расход этого документа: 10Запрос видит остаток: 90Новое количество документа: 15Хотя фактически доступно 100 единиц до проведения текущего документа, проверка будет выполняться против 90.
Поэтому старые движения текущего документа сначала очищаются, затем проверяется остаток, после чего формируются новые движения.
Последовательность такая:
1С (Код)1234561. Удалить старые движения текущего документа.2. Заблокировать остатки.3. Прочитать актуальные остатки.4. Проверить наличие товара.5. Сформировать новые движения.6. Платформа запишет сформированные движения при завершении проведения.## Почему после
Движения.Записать()снова устанавливаетсяЗаписывать = ИстинаПосле ручной записи движений набор записей фактически был записан и очищен от старого состояния. Повторная установка:
1С (Код)1Движения.ОстаткиНоменклатуры.Записывать = Истина;означает:
> Теперь новые движения, которые будут добавлены в этот набор, также нужно записать при завершении проведения.
Дальше в цикле добавляется новое движение:
1С (Код)123456Движение = Движения.ОстаткиНоменклатуры.Добавить();Движение.ВидДвижения = ВидДвиженияНакопления.Расход;Движение.Период = Дата;Движение.Номенклатура = Выборка.Номенклатура;Движение.Склад = Склад;Движение.Количество = Выборка.Количество;Так как
Записыватьснова установлено вИстина, платформа запишет эти движения после успешного проведения документа.То есть это не вторая запись в регистр, а повторная установка режима записи набора движений.
## Что происходит при перепроведении документа
При перепроведении платформа работает примерно по такой схеме:
1С (Код)12345678910111213Старые движения документа уже существуют.↓ОбработкаПроведения↓Движения.Записать()↓Старые движения текущего документа удаляются.↓Выполняется проверка остатков.↓Добавляются новые движения.↓Если Отказ = Ложь, новые движения записываются.Если во время проведения установить:
1С (Код)1Отказ = Истина;то новые движения не должны остаться в регистре. В зависимости от режима проведения и особенностей конфигурации платформа откатывает транзакцию. Поэтому при ошибке нехватки товара документ не проводится.
## Важный момент по вашему коду
Фрагмент:
1С (Код)1234Движения.ОстаткиНоменклатуры.Записывать = Истина;Движения.Записать();Движения.ОстаткиНоменклатуры.Записывать = Истина;логически означает:
1С (Код)123456789// Включаем запись регистраДвижения.ОстаткиНоменклатуры.Записывать = Истина;// Удаляем старые движения текущего документаДвижения.Записать();// Снова включаем запись регистра,// чтобы новые движения были записаны после проведенияДвижения.ОстаткиНоменклатуры.Записывать = Истина;Однако в таком виде код выглядит неочевидно. Его лучше снабдить комментариями:
1С (Код)12345678// Перед проверкой остатков удаляем старые движения документа,// чтобы они не повлияли на расчет доступного остатка.Движения.ОстаткиНоменклатуры.Записывать = Истина;Движения.Записать();// Включаем запись набора для новых движений,// которые будут сформированы ниже.Движения.ОстаткиНоменклатуры.Записывать = Истина;## Можно ли не вызывать
Движения.Записать()в началеВ данном алгоритме нежелательно.
Если убрать:
1С (Код)1Движения.Записать();то старые движения текущего документа могут остаться в регистре на момент выполнения запроса:
1С (Код)1РегистрНакопления.ОстаткиНоменклатуры.Остатки(...)Из-за этого остаток может быть рассчитан неправильно при повторном проведении или изменении документа.
Особенно это важно для документов расхода, которые проверяют наличие товара.
## Более современный и безопасный вариант
Вместо чтения табличной части документа напрямую можно передавать в блокировку временную таблицу или использовать источник данных с теми же товарами, которые участвуют в запросе. Но общая логика остается прежней:
1С (Код)1234567891011121314151617181920212223Движения.ОстаткиНоменклатуры.Записывать = Истина;// Удаляем движения текущего документаДвижения.Записать();// Повторно включаем запись для новых движенийДвижения.ОстаткиНоменклатуры.Записывать = Истина;// Блокируем остаткиБлокировка = Новый БлокировкаДанных;ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ОстаткиНоменклатуры");ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;ЭлементБлокировки.УстановитьЗначение("Склад", Склад);ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура","Номенклатура");Блокировка.Заблокировать();// Проверяем остатки и формируем новые движения## Еще один нюанс:
Движения.Записать()записывает не обязательно все регистрыВызов:
1С (Код)1Движения.Записать();обрабатывает наборы движений, для которых включен признак:
1С (Код)1Движения.ИмяРегистра.Записывать = Истина;Например:
1С (Код)12Движения.ОстаткиНоменклатуры.Записывать = Истина;Движения.Продажи.Записывать = Ложь;В таком случае вызов
Движения.Записать()затронетОстаткиНоменклатуры, но не будет записывать движения регистраПродажи.В вашем коде повторная строка:
1С (Код)1Движения.ОстаткиНоменклатуры.Записывать = Истина;нужна не для повторной записи, а для того, чтобы после очистки старых движений платформа записала новые движения, добавленные ниже.
-
-
АвторЗаписи
- Для ответа в этой теме необходимо авторизоваться.