В процессе подготовки к экзамену 1С:Специалист по бухгалтерскому учету часто встречаются задачи, требующие не только знания механизмов платформы, но и понимания методологии учета. Одной из таких задач является реализация документа Сборка, который должен списывать комплектующие (материалы) и приходовать готовое изделие (товар). Рассмотрим типичную проблему, с которой сталкиваются разработчики: дублирование записей прихода в регистре бухгалтерии, и разберем правильный алгоритм решения.
Прежде чем переходить к коду, проанализируем условия задачи. Нам необходимо реализовать сборку мебели, где:
Дт Основное производство — Кт Материалы: списание всех необходимых деталей по их себестоимости.Дт Товары — Кт Основное производство: оприходование готовой мебели на общую сумму списанных материалов.Разберем ситуацию, описанную в вопросе. Разработчик использует цикл для обхода выборки из запроса, в котором содержатся данные о составе собираемого изделия. Если в состав мебели входит 5 деталей, цикл выполнится 5 раз. Если внутри этого цикла добавить команду Движения.Управленческий.Добавить() для счета Товары, то система создаст 5 записей о приходе одного и того же стола или шкафа. Это методическая ошибка.
Проанализируем некорректный фрагмент кода:
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Списание детали (Кт Материалы) - это верно внутри цикла
Движение = Движения.Управленческий.Добавить();
Движение.СчетДт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
Движение.СчетКт = ПланыСчетов.Управленческий.Материалы;
// ... заполнение субконто ...
// ПРИХОД ТОВАРА (Дт Товары) - ОШИБКА: находится внутри цикла по деталям!
Движение = Движения.Управленческий.Добавить();
Движение.СчетДт = ПланыСчетов.Управленческий.Товары;
Движение.СчетКт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
// Это приведет к дублям, если деталей больше одной
КонецЦикла;
Для того чтобы сформировать корректные проводки, мы должны разделить логику списания и логику приходования. Рассмотрим первый способ решения: использование переменной для накопления итоговой себестоимости сборки.
Выясним алгоритм действий:
ИтоговаяСебестоимость и обнуляем её перед циклом.ИтоговаяСебестоимость.Товары на всю накопленную сумму.Рассмотрим пример исправленного кода:
ПеременнаяСебестоимостьСборки = 0;
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Рассчитываем себестоимость детали
СуммаСписанияДетали = ?(ВыборкаДетальныеЗаписи.КоличествоОстаток = ВыборкаДетальныеЗаписи.КоличествоДетали,
ВыборкаДетальныеЗаписи.СуммаОстаток,
ВыборкаДетальныеЗаписи.КоличествоДетали * (ВыборкаДетальныеЗаписи.СуммаОстаток / ВыборкаДетальныеЗаписи.КоличествоОстаток));
// 1. Проводка списания материала
Движение = Движения.Управленческий.Добавить();
Движение.СчетДт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
Движение.СчетКт = ПланыСчетов.Управленческий.Материалы;
Движение.Сумма = СуммаСписанияДетали;
Движение.КоличествоКт = ВыборкаДетальныеЗаписи.КоличествоДетали;
Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = ВыборкаДетальныеЗаписи.НаименованиеДетали;
Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склад] = СкладСписания;
// Накапливаем общую стоимость
ПеременнаяСебестоимостьСборки = ПеременнаяСебестоимостьСборки + СуммаСписанияДетали;
КонецЦикла;
// 2. Проводка прихода готовой продукции (ВНЕ ЦИКЛА)
Если ПеременнаяСебестоимостьСборки > 0 Тогда
Движение = Движения.Управленческий.Добавить();
Движение.СчетДт = ПланыСчетов.Управленческий.Товары;
Движение.СчетКт = ПланыСчетов.Управленческий.ОсновноеПроизводство;
Движение.Сумма = ПеременнаяСебестоимостьСборки;
Движение.КоличествоДт = ВыборкаТовары.Количество; // Берем из данных шапки или другой выборки
Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = НоменклатураМебель;
Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Свойства] = СвойствоМебели;
Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Склад] = СкладПриходования;
КонецЕсли;
Если документ Сборка позволяет собирать несколько разных видов мебели одновременно (в табличной части несколько строк с готовой продукцией), то использование одной переменной будет недостаточно. В этом случае проанализируем ситуацию с точки зрения структуры запроса.
Рекомендуется использовать запрос с обходом по группировкам (иерархический обход):
ИТОГИ ПО... (например, по НоменклатураТовара).Посмотрим на структуру такого обхода:
ВыборкаТовар = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаТовар.Следующий() Цикл
СуммаПоСборке = 0;
ВыборкаДетали = ВыборкаТовар.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
// Формируем Кт Материалы
// СуммаПоСборке = СуммаПоСборке + РассчитаннаяСебестоимость;
КонецЦикла;
// Здесь формируем Дт Товары на общую СуммаПоСборке
КонецЦикла;
При решении подобных задач на экзамене, обратите внимание на следующие моменты:
БлокировкаДанных) перед выполнением запроса. Блокировать нужно счета, по которым считываются остатки (в нашем случае Материалы), по конкретным складам и номенклатуре.Новый Граница(МоментВремени(), ВидГраницы.Исключая). Это критично при перепроведении документа, чтобы он не видел свои собственные старые движения и не искажал расчет средней цены.КоличествоДетали > КоличествоОстаток, необходимо устанавливать Отказ = Истина и выводить информативное сообщение пользователю.Следуя этим правилам и разделяя логику обработки комплектующих и готовых изделий, вы сможете построить надежный механизм проведения документов любой сложности.