В процессе автоматизации производственного учета на платформе 1С:Предприятие 8.1 или 8.3 (в режиме обычных форм) часто возникает задача программного формирования документов. Одной из самых трудоемких операций является заполнение документа ОтчетПроизводстваЗаСмену, так как он требует не только создания шапки и таблицы продукции, но и корректного расчета материалов, которые должны списаться в производство согласно установленным спецификациям. Разберем по шагам, как решить эту задачу, проанализируем типичные ошибки и рассмотрим рабочие примеры кода.
Для начала создадим новый объект документа и заполним его основные реквизиты. На этом этапе важно правильно указать организацию, склад и подразделение, так как от них часто зависят счета учета и возможность проведения документа. Проанализируем пример кода:
// Создаем новый документ
НовыйДокумент = Документы.ОтчетПроизводстваЗаСмену.СоздатьДокумент();
// Заполняем основные реквизиты
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.НайтиПоКоду("000000001");
НовыйДокумент.Склад = Справочники.Склады.НайтиПоКоду("000000003");
НовыйДокумент.ПодразделениеОрганизации = Справочники.ПодразделенияОрганизаций.НайтиПоКоду("000000001");
НовыйДокумент.ОтражатьВНалоговомУчете = Ложь;
НовыйДокумент.ИспользоватьМатериалы = Истина;
НовыйДокумент.СчетЗатрат = ПланыСчетов.Хозрасчетный.ОсновноеПроизводство;
НовыйДокумент.Комментарий = "Создано программно (автозаполнение)";
Обратите внимание на реквизит ИспользоватьМатериалы. Если установить его в значение Истина, это даст программе понять, что при проведении документа должны быть сформированы движения по списанию сырья со склада.
Далее добавим в документ информацию о том, какой продукт был изготовлен. Именно на основе этой строки мы будем в дальнейшем искать спецификацию и рассчитывать материалы. Рассмотрим, как добавить строку в табличную часть Продукция:
НоваяСтрокаПродукции = НовыйДокумент.Продукция.Добавить();
НоменклатураПродукта = Справочники.Номенклатура.НайтиПоКоду("00000000016");
НоваяСтрокаПродукции.Номенклатура = НоменклатураПродукта;
НоваяСтрокаПродукции.Количество = 10; // Например, 10 единиц продукции
НоваяСтрокаПродукции.ПлановаяСтоимость = 100;
НоваяСтрокаПродукции.Счет = ПланыСчетов.Хозрасчетный.ГотоваяПродукция;
НоваяСтрокаПродукции.СуммаПлановая = НоваяСтрокаПродукции.Количество * НоваяСтрокаПродукции.ПлановаяСтоимость;
НоваяСтрокаПродукции.НоменклатурнаяГруппа = Справочники.НоменклатурныеГруппы.НайтиПоКоду("000000001");
Одной из главных сложностей является автоматическое определение спецификации для выбранной номенклатуры. Существует несколько способов получить ссылку на спецификацию. Разберем наиболее надежные из них.
Вариант А: Использование метода Выбрать(). Этот метод позволяет найти первую попавшуюся спецификацию, где владельцем является наш продукт:
ВыборкаСпецификаций = Справочники.СпецификацииНоменклатуры.Выбрать(, НоменклатураПродукта);
Если ВыборкаСпецификаций.Следующий() Тогда
НоваяСтрокаПродукции.Спецификация = ВыборкаСпецификаций.Ссылка;
КонецЕсли;
Вариант Б: Получение основной спецификации. В типовых конфигурациях часто используется регистр сведений или реквизит номенклатуры для хранения «основной» спецификации. Выясним, как получить её правильно:
// Попробуем получить через реквизит (в некоторых редакциях Бухгалтерии)
СпецификацияПоУмолчанию = НоменклатураПродукта.ОсновнаяСпецификацияНоменклатуры;
// Если реквизит пуст, можно использовать типовой метод общего модуля
СпецификацияПоУмолчанию = УправлениеПроизводством.ОпределитьСпецификациюПоУмолчанию(НоменклатураПродукта, ТекущаяДата());
НоваяСтрокаПродукции.Спецификация = СпецификацияПоУмолчанию;
Когда продукция и её спецификация определены, необходимо заполнить табличную часть Материалы. Если вы пытаетесь использовать типовую функцию РассчитатьРасходСырьяПоСпецификации, важно помнить: документ должен быть предварительно записан. Если документ существует только в памяти как объект, запрос внутри типовой функции не сможет найти данные по его ссылке и выдаст ошибку Таблица не найдена.
Если же мы хотим полностью контролировать процесс, воспользуемся прямым запросом к справочнику спецификаций. Рассмотрим этот алгоритм:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СпецСостав.Номенклатура КАК Материал,
| СпецСостав.Количество КАК КоличествоНорма
|ИЗ
| Справочник.СпецификацииНоменклатуры.ИсходныеКомплектующие КАК СпецСостав
|ГДЕ
| СпецСостав.Ссылка = &Спецификация";
Запрос.УстановитьПараметр("Спецификация", НоваяСтрокаПродукции.Спецификация);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаМатериалов = РезультатЗапроса.Выбрать();
Пока ВыборкаМатериалов.Следующий() Цикл
НоваяСтрокаМат = НовыйДокумент.Материалы.Добавить();
НоваяСтрокаМат.Номенклатура = ВыборкаМатериалов.Материал;
// Рассчитываем количество: норма из спецификации * количество произведенной продукции
НоваяСтрокаМат.Количество = ВыборкаМатериалов.КоличествоНорма * НоваяСтрокаПродукции.Количество;
// Заполняем счета и аналитику
НоваяСтрокаМат.Счет = ПланыСчетов.Хозрасчетный.СырьеИМатериалы;
НоваяСтрокаМат.СтатьяЗатрат = Справочники.СтатьиЗатрат.НайтиПоНаименованию("Материальные расходы");
НоваяСтрокаМат.НоменклатурнаяГруппа = НоваяСтрокаПродукции.НоменклатурнаяГруппа;
КонецЦикла;
После того как мы перебрали все позиции продукции и добавили материалы, в таблице могут появиться дубликаты (если разные продукты используют один и тот же материал). Чтобы отчет выглядел корректно и правильно проводился, проанализируем таблицу материалов и выполним её свертку:
// Сворачиваем таблицу по ключевым полям, суммируя количество
НовыйДокумент.Материалы.Свернуть("Номенклатура, Счет, НоменклатурнаяГруппа, СтатьяЗатрат", "Количество");
// Записываем и проводим документ
Попытка
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись); // Или Проведение
Сообщить("Документ успешно создан: " + НовыйДокумент.Ссылка);
Исключение
Сообщить("Ошибка при создании документа: " + ОписаниеОшибки());
КонецПопытки;
Проанализируем несколько критических моментов, которые помогут избежать ошибок в будущем:
Коэффициент, чтобы не списать в тысячу раз больше или меньше материалов.НайтиПоНаименованию или жесткое прописывание счета 10.01 — плохая практика. Рекомендуем использовать функцию СчетаУчетаНоменклатуры.ПолучитьСчетаУчета(), которая автоматически вернет нужные счета исходя из настроек регистра сведений.Объект.Записать() перед вызовом. Запрос внутри типовых механизмов ищет данные в базе данных, а не в оперативной памяти.Таким образом, мы разобрали полный цикл программного создания документа ОтчетПроизводстваЗаСмену. Использование прямых запросов к табличным частям спецификаций обеспечивает наибольшую гибкость и скорость работы, особенно когда требуется создать сразу большое количество документов на основе внешних данных.