Как правильно реализовать проведение документа Сборка в 1С и избежать дублирования проводок

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет
← На главную

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

Анализ бизнес-логики задачи

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

  1. Детали (Материалы) учитываются в разрезе номенклатуры и складов. Себестоимость рассчитывается как средняя по конкретному складу.
  2. Мебель (Товары) учитывается в разрезе номенклатуры и свойств (наборов характеристик через План видов характеристик). Важный нюанс: себестоимость мебели рассчитывается как средняя по свойству сразу по всем складам.
  3. Документ Сборка должен сформировать две группы проводок:
    • Дт Основное производство — Кт Материалы: списание всех необходимых деталей по их себестоимости.
    • Дт Товары — Кт Основное производство: оприходование готовой мебели на общую сумму списанных материалов.

Выявление причины ошибки дублирования записей

Разберем ситуацию, описанную в вопросе. Разработчик использует цикл для обхода выборки из запроса, в котором содержатся данные о составе собираемого изделия. Если в состав мебели входит 5 деталей, цикл выполнится 5 раз. Если внутри этого цикла добавить команду Движения.Управленческий.Добавить() для счета Товары, то система создаст 5 записей о приходе одного и того же стола или шкафа. Это методическая ошибка.

Проанализируем некорректный фрагмент кода:


Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Списание детали (Кт Материалы) - это верно внутри цикла
    Движение = Движения.Управленческий.Добавить();
    Движение.СчетДт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
    Движение.СчетКт = ПланыСчетов.Управленческий.Материалы;
    // ... заполнение субконто ...
    
    // ПРИХОД ТОВАРА (Дт Товары) - ОШИБКА: находится внутри цикла по деталям!
    Движение = Движения.Управленческий.Добавить();
    Движение.СчетДт = ПланыСчетов.Управленческий.Товары;
    Движение.СчетКт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
    // Это приведет к дублям, если деталей больше одной
КонецЦикла;

Правильное решение: использование накопительной переменной

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

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

  1. Создаем переменную ИтоговаяСебестоимость и обнуляем её перед циклом.
  2. В цикле по деталям рассчитываем себестоимость каждой детали, создаем проводку списания и прибавляем полученную сумму к ИтоговаяСебестоимость.
  3. После завершения цикла (когда все детали списаны), создаем ОДНУ проводку по дебету счета Товары на всю накопленную сумму.

Рассмотрим пример исправленного кода:


ПеременнаяСебестоимостьСборки = 0;

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

    // 1. Проводка списания материала
    Движение = Движения.Управленческий.Добавить();
    Движение.СчетДт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
    Движение.СчетКт = ПланыСчетов.Управленческий.Материалы;
    Движение.Сумма = СуммаСписанияДетали;
    Движение.КоличествоКт = ВыборкаДетальныеЗаписи.КоличествоДетали;
    Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = ВыборкаДетальныеЗаписи.НаименованиеДетали;
    Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склад] = СкладСписания;

    // Накапливаем общую стоимость
    ПеременнаяСебестоимостьСборки = ПеременнаяСебестоимостьСборки + СуммаСписанияДетали;
КонецЦикла;

// 2. Проводка прихода готовой продукции (ВНЕ ЦИКЛА)
Если ПеременнаяСебестоимостьСборки > 0 Тогда
    Движение = Движения.Управленческий.Добавить();
    Движение.СчетДт = ПланыСчетов.Управленческий.Товары;
    Движение.СчетКт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
    Движение.Сумма = ПеременнаяСебестоимостьСборки;
    Движение.КоличествоДт = ВыборкаТовары.Количество; // Берем из данных шапки или другой выборки
    Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = НоменклатураМебель;
    Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Свойства] = СвойствоМебели;
    Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Склад] = СкладПриходования;
КонецЕсли;

Продвинутое решение: группировка и итоги в запросе

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

Рекомендуется использовать запрос с обходом по группировкам (иерархический обход):

  1. В тексте запроса используем предложение ИТОГИ ПО... (например, по НоменклатураТовара).
  2. Используем вложенные циклы: внешний цикл по итоговым записям (готовая продукция), внутренний цикл по детальным записям (состав этой продукции).
  3. Для каждой итерации внешнего цикла мы будем знать, какой товар собирается, а во внутреннем цикле соберем сумму всех его деталей.

Посмотрим на структуру такого обхода:


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

Пока ВыборкаТовар.Следующий() Цикл
    СуммаПоСборке = 0;
    ВыборкаДетали = ВыборкаТовар.Выбрать();
    
    Пока ВыборкаДетали.Следующий() Цикл
        // Формируем Кт Материалы
        // СуммаПоСборке = СуммаПоСборке + РассчитаннаяСебестоимость;
    КонецЦикла;
    
    // Здесь формируем Дт Товары на общую СуммаПоСборке
КонецЦикла;

Важные замечания по методике 1С:Специалист

При решении подобных задач на экзамене, обратите внимание на следующие моменты:

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

← На главную