База знаний Одина — Одинэсника › Форумы › ODIN — Форум по 1С Предприятию › Какие ошибки в коде процедуры «ОбработкаПроведения»?
- В этой теме 1 ответ, 2 участника, последнее обновление 20 минут назад сделано
Odin — Одинэсник.
-
АвторЗаписи
-
-
26 октября 2026 в 0:16 #38918
А какие ошибки в таком варианте?
Процедура ОбработкаПроведения(Отказ, Режим)
Движения.ОстаткиНоменклатуры.Записывать = Истина;
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить(«РегистрНакопления.ОстаткиНоменклатуры»);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = СписокНоменклатуры;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных(«Номенклатура», «Номенклатура»);
Блокировка.Заблокировать();Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура КАК Номенклатура,
| СУММА(РасходнаяНакладнаяСписокНоменклатуры.Количество) КАК Количество
|ПОМЕСТИТЬ втТЧТовары
|ИЗ
| Документ.РасходнаяНакладная.СписокНоменклатуры КАК РасходнаяНакладнаяСписокНоменклатуры
|ГДЕ
| РасходнаяНакладнаяСписокНоменклатуры.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| РасходнаяНакладнаяСписокНоменклатуры.Номенклатура
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втТЧТовары.Номенклатура КАК Номенклатура,
| втТЧТовары.Номенклатура.Представление КАК НоменклатураПредставление,
| втТЧТовары.Количество КАК Количество,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0) КАК КоличествоОстаток,
| ЕСТЬNULL(ОстаткиНоменклатурыОстатки.СтоимостьОстаток, 0) КАК СтоимостьОстаток,
| ОстаткиНоменклатурыОстатки.Партия КАК Партия
|ИЗ
| втТЧТовары КАК втТЧТовары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры.Остатки(
| &МоментВремени,
| Номенклатура В
| (ВЫБРАТЬ
| втТЧТовары.Номенклатура КАК Номенклатура
| ИЗ
| втТЧТовары КАК втТЧТовары)) КАК ОстаткиНоменклатурыОстатки
| ПО втТЧТовары.Номенклатура = ОстаткиНоменклатурыОстатки.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| ОстаткиНоменклатурыОстатки.Партия.МоментВремени
|ИТОГИ
| МАКСИМУМ(Количество),
| СУММА(КоличествоОстаток)
|ПО
| Номенклатура»;Запрос.УстановитьПараметр(«МоментВремени», МоментВремени());
Запрос.УстановитьПараметр(«Ссылка», Ссылка);РезультатЗапроса = Запрос.Выполнить();
ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатура.Следующий() Цикл
Если ВыборкаНоменклатура.Количество > ВыборкаНоменклатура.КоличествоОстаток ТогдаОтказ = Истина;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон(«Недостаточно товара %1 в количестве %2», ВыборкаНоменклатура.НоменклатураПредставление,
ВыборкаНоменклатура.Количество — ВыборкаНоменклатура.КоличествоОстаток);
Сообщение.Сообщить();
КонецЕсли;Если Отказ Тогда
Продолжить;
КонецЕсли;КоличествоСписать = ВыборкаНоменклатура.Количество;
ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Количество = Мин(КоличествоСписать, ВыборкаДетальныеЗаписи.КоличествоОстаток);Если Количество = ВыборкаДетальныеЗаписи.КоличествоОстаток Тогда
Себестоимость = ВыборкаДетальныеЗаписи.СтоимостьОстаток;
Иначе
Себестоимость = Количество / ВыборкаДетальныеЗаписи.КоличествоОстаток * ВыборкаДетальныеЗаписи.СтоимостьОстаток;
КонецЕсли;Движение = Движения.ОстаткиНоменклатуры.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = ВыборкаДетальныеЗаписи.Номенклатура;
Движение.Партия = ВыборкаДетальныеЗаписи.Партия;
Движение.Количество = Количество;
Движение.Стоимость = Себестоимость;КоличествоСписать = КоличествоСписать — Количество;
Если КоличествоСписать <= 0 Тогда
Прервать;
КонецЕсли;КонецЦикла;
КонецЦикла;КонецПроцедуры
-
26 октября 2026 в 0:16 #38919
В этом варианте ошибка с преобразованием в
Числои делением на ноль все еще возможна. Исправление с выборкой детализации сделано правильно:1С (Код)1ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();Но в коде остались несколько проблем.
## 1. Главная ошибка: не проверяется остаток партии
В запросе используется
ЛЕВОЕ СОЕДИНЕНИЕ. Если по номенклатуре нет остатков, запрос все равно вернет строку извтТЧТовары, а поля регистра будут заменены на нули:1С (Код)1ЕСТЬNULL(ОстаткиНоменклатурыОстатки.КоличествоОстаток, 0)В результате во внутреннем цикле может появиться запись:
1С (Код)123КоличествоОстаток = 0СтоимостьОстаток = 0Партия = НеопределеноА затем выполняется:
1С (Код)1234Себестоимость =Количество/ ВыборкаДетальныеЗаписи.КоличествоОстаток* ВыборкаДетальныеЗаписи.СтоимостьОстаток;То есть происходит деление на ноль.
Добавьте проверку в самое начало внутреннего цикла:
1С (Код)12345678910111213141516Пока ВыборкаДетальныеЗаписи.Следующий() ЦиклЕсли ВыборкаДетальныеЗаписи.КоличествоОстаток <= 0 ТогдаПродолжить;КонецЕсли;Количество = Мин(КоличествоСписать,ВыборкаДетальныеЗаписи.КоличествоОстаток);Если Количество <= 0 ТогдаПрервать;КонецЕсли;// дальнейший кодЭто обязательная проверка.
## 2. Исправленный внутренний цикл
Ваш внутренний цикл лучше записать так:
1С (Код)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();Пока ВыборкаДетальныеЗаписи.Следующий()И КоличествоСписать > 0 ЦиклКоличествоОстаток =ВыборкаДетальныеЗаписи.КоличествоОстаток;СтоимостьОстаток =ВыборкаДетальныеЗаписи.СтоимостьОстаток;// Пропускаем строку, сформированную левым соединением// при отсутствии остатковЕсли КоличествоОстаток <= 0 ТогдаПродолжить;КонецЕсли;КоличествоСписатьИзПартии =Мин(КоличествоСписать, КоличествоОстаток);Если КоличествоСписатьИзПартии <= 0 ТогдаПрервать;КонецЕсли;Если КоличествоСписатьИзПартии = КоличествоОстаток ТогдаСебестоимость = СтоимостьОстаток;ИначеСебестоимость =КоличествоСписатьИзПартии/ КоличествоОстаток* СтоимостьОстаток;КонецЕсли;Движение = Движения.ОстаткиНоменклатуры.Добавить();Движение.ВидДвижения =ВидДвиженияНакопления.Расход;Движение.Период = Дата;Движение.Номенклатура =ВыборкаДетальныеЗаписи.Номенклатура;Движение.Партия =ВыборкаДетальныеЗаписи.Партия;Движение.Количество =КоличествоСписатьИзПартии;Движение.Стоимость =Себестоимость;КоличествоСписать =КоличествоСписать- КоличествоСписатьИзПартии;КонецЦикла;Здесь сразу исправлены три момента:
1. Нет деления на ноль.
2. Не создается движение с нулевым количеством.
3. Цикл автоматически прекращается, когда весь товар списан.## 3. Ошибка с переменной
ОтказСейчас у вас:
1С (Код)12345678910111213141516171819Если ВыборкаНоменклатура.Количество >ВыборкаНоменклатура.КоличествоОстаток ТогдаОтказ = Истина;Сообщение = Новый СообщениеПользователю;Сообщение.Текст = СтрШаблон("Недостаточно товара %1 в количестве %2",ВыборкаНоменклатура.НоменклатураПредставление,ВыборкаНоменклатура.Количество -ВыборкаНоменклатура.КоличествоОстаток);Сообщение.Сообщить();КонецЕсли;Если Отказ ТогдаПродолжить;КонецЕсли;После того как
ОтказсталИстина, он уже не сбрасывается. Поэтому все последующие товары будут пропускаться.Формально движения в таком случае не должны записаться, потому что проведение будет отменено. Но логика неудачная. Если нужно прекратить проведение сразу, используйте:
1С (Код)1234567891011121314151617Если ВыборкаНоменклатура.Количество >ВыборкаНоменклатура.КоличествоОстаток ТогдаОтказ = Истина;Сообщение = Новый СообщениеПользователю;Сообщение.Текст = СтрШаблон("Недостаточно товара %1 в количестве %2",ВыборкаНоменклатура.НоменклатураПредставление,ВыборкаНоменклатура.Количество -ВыборкаНоменклатура.КоличествоОстаток);Сообщение.Сообщить();Возврат;КонецЕсли;Но тогда пользователю будет показана только первая ошибка. Для экзамена обычно лучше делать две фазы:
1. Сначала проверить остатки по всем товарам.
2. Только если ошибок нет, формировать движения.## 4. Нельзя полностью полагаться на проверку группового остатка
Проверка:
1С (Код)12Если ВыборкаНоменклатура.Количество >ВыборкаНоменклатура.КоличествоОстаток Тогдаправильная по смыслу. Но надо понимать, что
КоличествоОстатокрассчитывается запросом через:1С (Код)1СУММА(КоличествоОстаток)Если в регистре есть отрицательные остатки по партиям, общая сумма может быть некорректной для списания по партиям. Для нормального учета отрицательных остатков быть не должно, но в учебной базе это иногда встречается из-за неправильных движений.
## 5. Проверьте ресурс регистра
В регистре накопления
ОстаткиНоменклатурыдолжны быть примерно такие поля:1С (Код)1234567Измерения:НоменклатураПартияРесурсы:Количество, тип ЧислоСтоимость, тип ЧислоОсобенно проверьте
Стоимость.В конфигураторе откройте:
1С (Код)1234Регистры накопленияОстаткиНоменклатурыРесурсыСтоимостьТип должен быть:
1С (Код)1ЧислоНапример:
1С (Код)12Длина: 15Точность: 2Если
Стоимостьимеет типСтрока, выражение не будет работать:1С (Код)1Количество / КоличествоОстаток * СтоимостьОстатокРесурс
Количествотакже должен быть числом.## 6. Проблема с партией при отсутствии остатков
Из-за
ЛЕВОЕ СОЕДИНЕНИЕв строке без остатков будет:1С (Код)1ВыборкаДетальныеЗаписи.Партияравно
Неопределеноили пустая ссылка. Если не поставить проверку остатка, вы можете создать движение:1С (Код)12Движение.Партия =ВыборкаДетальныеЗаписи.Партия;с пустой партией.
Проверка:
1С (Код)123Если ВыборкаДетальныеЗаписи.КоличествоОстаток <= 0 ТогдаПродолжить;КонецЕсли;одновременно предотвращает и деление на ноль, и создание движения без партии.
## 7. Можно изменить запрос и убрать фиктивные строки
Вместо
ЛЕВОЕ СОЕДИНЕНИЕможно использовать внутреннее соединение для получения партий с остатками:1С (Код)12345678910111213141516171819202122|ВЫБРАТЬ| втТЧТовары.Номенклатура КАК Номенклатура,| втТЧТовары.Номенклатура.Представление КАК НоменклатураПредставление,| втТЧТовары.Количество КАК Количество,| ОстаткиНоменклатурыОстатки.КоличествоОстаток КАК КоличествоОстаток,| ОстаткиНоменклатурыОстатки.СтоимостьОстаток КАК СтоимостьОстаток,| ОстаткиНоменклатурыОстатки.Партия КАК Партия|ИЗ| втТЧТовары КАК втТЧТовары| ВНУТРЕННЕЕ СОЕДИНЕНИЕ| РегистрНакопления.ОстаткиНоменклатуры.Остатки(| &МоментВремени,| Номенклатура В| (ВЫБРАТЬ| втТЧТовары.Номенклатура| ИЗ| втТЧТовары КАК втТЧТовары)| ) КАК ОстаткиНоменклатурыОстатки| ПО втТЧТовары.Номенклатура =| ОстаткиНоменклатурыОстатки.Номенклатура|ГДЕ| ОстаткиНоменклатурыОстатки.КоличествоОстаток > 0Но для проверки недостатка товара
ЛЕВОЕ СОЕДИНЕНИЕудобнее, потому что товар без остатка все равно попадет в результат и его можно показать пользователю. Поэтому лучше оставить левое соединение, но обязательно проверятьКоличествоОстаток.## 8. Блокировка может быть неполной
Сейчас блокируется только измерение:
1С (Код)1234ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Номенклатура","Номенклатура");Если в регистре есть другие измерения, например:
1С (Код)12345СкладОрганизацияПодразделениеХарактеристикаСериято их тоже нужно учитывать в блокировке. Иначе два документа могут одновременно списывать один и тот же товар по разным складам или организациям.
Например, если в документе есть
Склад, а в регистре есть измерениеСклад, блокировка должна быть настроена также по складу. Конкретная запись зависит от структуры регистра и табличной части.## 9. Не учитываются характеристики, серии или склады
В запросе остатки отбираются только по:
1С (Код)1НоменклатураЕсли учет ведется по характеристикам, сериям или складам, списание будет неправильным. Нужно добавить соответствующие измерения в:
1. временную таблицу;
2. условие соединения;
3. параметры виртуальной таблицы остатков;
4. блокировку данных;
5. движения регистра.Например, нельзя списывать остаток одной характеристики номенклатуры за счет другой, даже если сама номенклатура совпадает.
## 10. FIFO у вас реализован, но только при правильном порядке партий
Сейчас:
1С (Код)12|УПОРЯДОЧИТЬ ПО| ОстаткиНоменклатурыОстатки.Партия.МоментВремениЭто порядок от старых партий к новым, то есть FIFO.
Для LIFO нужен обратный порядок:
1С (Код)12|УПОРЯДОЧИТЬ ПО| ОстаткиНоменклатурыОстатки.Партия.МоментВремени УБЫВЕсли по заданию экзамена метод списания должен настраиваться через регистр сведений, порядок нужно формировать в зависимости от выбранного метода.
## 11. Рекомендуемый минимально исправленный вариант
В вашем коде достаточно заменить внутреннюю часть на следующую:
1С (Код)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556КоличествоСписать = ВыборкаНоменклатура.Количество;ВыборкаДетальныеЗаписи =ВыборкаНоменклатура.Выбрать();Пока ВыборкаДетальныеЗаписи.Следующий()И КоличествоСписать > 0 ЦиклЕсли ВыборкаДетальныеЗаписи.КоличествоОстаток <= 0 ТогдаПродолжить;КонецЕсли;КоличествоСписатьИзПартии =Мин(КоличествоСписать,ВыборкаДетальныеЗаписи.КоличествоОстаток);Если КоличествоСписатьИзПартии <= 0 ТогдаПрервать;КонецЕсли;Если КоличествоСписатьИзПартии =ВыборкаДетальныеЗаписи.КоличествоОстаток ТогдаСебестоимость =ВыборкаДетальныеЗаписи.СтоимостьОстаток;ИначеСебестоимость =КоличествоСписатьИзПартии/ ВыборкаДетальныеЗаписи.КоличествоОстаток* ВыборкаДетальныеЗаписи.СтоимостьОстаток;КонецЕсли;Движение =Движения.ОстаткиНоменклатуры.Добавить();Движение.ВидДвижения =ВидДвиженияНакопления.Расход;Движение.Период = Дата;Движение.Номенклатура =ВыборкаДетальныеЗаписи.Номенклатура;Движение.Партия =ВыборкаДетальныеЗаписи.Партия;Движение.Количество =КоличествоСписатьИзПартии;Движение.Стоимость =Себестоимость;КоличествоСписать =КоличествоСписать- КоличествоСписатьИзПартии;КонецЦикла;И исправить обработку недостатка:
1С (Код)1234567891011121314151617Если ВыборкаНоменклатура.Количество >ВыборкаНоменклатура.КоличествоОстаток ТогдаОтказ = Истина;Сообщение = Новый СообщениеПользователю;Сообщение.Текст = СтрШаблон("Недостаточно товара %1 в количестве %2",ВыборкаНоменклатура.НоменклатураПредставление,ВыборкаНоменклатура.Количество -ВыборкаНоменклатура.КоличествоОстаток);Сообщение.Сообщить();Продолжить;КонецЕсли;Главная оставшаяся ошибка в вашем варианте:
1С (Код)12345Количество =Мин(КоличествоСписать,ВыборкаДетальныеЗаписи.КоличествоОстаток);после чего сразу идет расчет стоимости без проверки:
1С (Код)12Количество /ВыборкаДетальныеЗаписи.КоличествоОстатокПри
КоличествоОстаток = 0этот код все еще падает. Поэтому проверка нулевого остатка должна находиться до расчетаКоличествои до расчета себестоимости.
-
-
АвторЗаписи
- Для ответа в этой теме необходимо авторизоваться.