Какие ошибки в коде процедуры «ОбработкаПроведения»?

База знаний Одина — Одинэсника › Форумы › ODIN — Форум по 1С Предприятию › Какие ошибки в коде процедуры «ОбработкаПроведения»?

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

        А какие ошибки в таком варианте?

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

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

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

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

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

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

        ВыборкаНоменклатура = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

        Пока ВыборкаНоменклатура.Следующий() Цикл
        Если ВыборкаНоменклатура.Количество > ВыборкаНоменклатура.КоличествоОстаток Тогда

        Отказ = Истина;

        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = СтрШаблон(«Недостаточно товара %1 в количестве %2», ВыборкаНоменклатура.НоменклатураПредставление,
        ВыборкаНоменклатура.Количество — ВыборкаНоменклатура.КоличествоОстаток);
        Сообщение.Сообщить();
        КонецЕсли;

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

        КоличествоСписать = ВыборкаНоменклатура.Количество;

        ВыборкаДетальныеЗаписи = ВыборкаНоменклатура.Выбрать();

        Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        Количество = Мин(КоличествоСписать, ВыборкаДетальныеЗаписи.КоличествоОстаток);

        Если Количество = ВыборкаДетальныеЗаписи.КоличествоОстаток Тогда
        Себестоимость = ВыборкаДетальныеЗаписи.СтоимостьОстаток;
        Иначе
        Себестоимость = Количество / ВыборкаДетальныеЗаписи.КоличествоОстаток * ВыборкаДетальныеЗаписи.СтоимостьОстаток;
        КонецЕсли;

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

        КоличествоСписать = КоличествоСписать — Количество;

        Если КоличествоСписать <= 0 Тогда
        Прервать;
        КонецЕсли;

        КонецЦикла;
        КонецЦикла;

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

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

          В этом варианте ошибка с преобразованием в Число и делением на ноль все еще возможна. Исправление с выборкой детализации сделано правильно:

          Но в коде остались несколько проблем.

          ## 1. Главная ошибка: не проверяется остаток партии

          В запросе используется ЛЕВОЕ СОЕДИНЕНИЕ. Если по номенклатуре нет остатков, запрос все равно вернет строку из втТЧТовары, а поля регистра будут заменены на нули:

          В результате во внутреннем цикле может появиться запись:

          А затем выполняется:

          То есть происходит деление на ноль.

          Добавьте проверку в самое начало внутреннего цикла:

          Это обязательная проверка.

          ## 2. Исправленный внутренний цикл

          Ваш внутренний цикл лучше записать так:

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

          1. Нет деления на ноль.
          2. Не создается движение с нулевым количеством.
          3. Цикл автоматически прекращается, когда весь товар списан.

          ## 3. Ошибка с переменной Отказ

          Сейчас у вас:

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

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

          Но тогда пользователю будет показана только первая ошибка. Для экзамена обычно лучше делать две фазы:

          1. Сначала проверить остатки по всем товарам.
          2. Только если ошибок нет, формировать движения.

          ## 4. Нельзя полностью полагаться на проверку группового остатка

          Проверка:

          правильная по смыслу. Но надо понимать, что КоличествоОстаток рассчитывается запросом через:

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

          ## 5. Проверьте ресурс регистра

          В регистре накопления ОстаткиНоменклатуры должны быть примерно такие поля:

          Особенно проверьте Стоимость.

          В конфигураторе откройте:

          Тип должен быть:

          Например:

          Если Стоимость имеет тип Строка, выражение не будет работать:

          Ресурс Количество также должен быть числом.

          ## 6. Проблема с партией при отсутствии остатков

          Из-за ЛЕВОЕ СОЕДИНЕНИЕ в строке без остатков будет:

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

          с пустой партией.

          Проверка:

          одновременно предотвращает и деление на ноль, и создание движения без партии.

          ## 7. Можно изменить запрос и убрать фиктивные строки

          Вместо ЛЕВОЕ СОЕДИНЕНИЕ можно использовать внутреннее соединение для получения партий с остатками:

          Но для проверки недостатка товара ЛЕВОЕ СОЕДИНЕНИЕ удобнее, потому что товар без остатка все равно попадет в результат и его можно показать пользователю. Поэтому лучше оставить левое соединение, но обязательно проверять КоличествоОстаток.

          ## 8. Блокировка может быть неполной

          Сейчас блокируется только измерение:

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

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

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

          ## 9. Не учитываются характеристики, серии или склады

          В запросе остатки отбираются только по:

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

          1. временную таблицу;
          2. условие соединения;
          3. параметры виртуальной таблицы остатков;
          4. блокировку данных;
          5. движения регистра.

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

          ## 10. FIFO у вас реализован, но только при правильном порядке партий

          Сейчас:

          Это порядок от старых партий к новым, то есть FIFO.

          Для LIFO нужен обратный порядок:

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

          ## 11. Рекомендуемый минимально исправленный вариант

          В вашем коде достаточно заменить внутреннюю часть на следующую:

          И исправить обработку недостатка:

          Главная оставшаяся ошибка в вашем варианте:

          после чего сразу идет расчет стоимости без проверки:

          При КоличествоОстаток = 0 этот код все еще падает. Поэтому проверка нулевого остатка должна находиться до расчета Количество и до расчета себестоимости.

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