Как правильно выгрузить себестоимость из проводок и сопоставить ее с данными документа в 1С:Бухгалтерии?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет Торговля и дистрибуция
← На главную

При разработке произвольных отчетов или внешних обработок в 1С:Бухгалтерии 3.0 часто возникает задача сопоставления данных о реализации товаров (количества и выручки) с данными об их себестоимости, которые хранятся в регистре бухгалтерии Хозрасчетный. Ситуация осложняется тем, что в некоторых конфигурациях учетная политика может не предполагать ведение количественного учета в определенных разрезах, или стандартные отчеты, такие как «Валовая прибыль», не выводят нужные данные в специфических условиях. Рассмотрим подробнее, как решить проблему получения себестоимости единицы товара через запрос к проводкам и почему стандартные подходы с перебором строк могут работать некорректно.

Анализ проблемы и типичных ошибок в коде

Проанализируем ситуацию, с которой столкнулся автор темы. Имеется таблица значений ИтогТаблица, содержащая данные о продажах. Необходимо добавить в нее колонку СебестоимостьОбщая, получив данные из регистра бухгалтерии по проводкам Дт 90.02 — Кт 41.03. Основная трудность заключается в «связывании» строки таблицы и записи регистра.

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

Ошибка 2: Цикл в цикле. В восьмом сообщении мы видим классическую ошибку производительности и логики:


Пока Выборка.Следующий Цикл
    Для каждого НовСтр Из ИтогТаблица Цикл
        НовСтр.СебестоимостьОбщая = Окр(Выборка.СуммаОборот, 2);
    КонецЦикла;
КонецЦикла;

Разберем, почему этот код не работает. В каждой итерации выборки из запроса программа перебирает все строки таблицы ИтогТаблица и записывает в них значение текущей проводки. В результате, после завершения работы всех циклов, во всех строках вашей таблицы будет стоять сумма из самой последней проводки, обработанной запросом. Сопоставления конкретной строки конкретному документу здесь не происходит.

Шаг 1. Формирование правильного запроса к регистру бухгалтерии

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

Рассмотрим структуру запроса, который позволит вытащить данные в разрезе регистратора (документа) и номенклатуры:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
    |	ХозрасчетныйДвиженияССубконто.СубконтоКт1 КАК Номенклатура,
    |	ХозрасчетныйДвиженияССубконто.Сумма КАК СуммаСебестоимости,
    |	ХозрасчетныйДвиженияССубконто.КоличествоКт КАК КоличествоСебестоимости
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(
    |			&Дата1,
    |			&Дата2,
    |			СчетДт В ИЕРАРХИИ (&СписокСчетовДТ)
    |				И СчетКт В ИЕРАРХИИ (&СписокСчетовКТ),
    |			,
    |			) КАК ХозрасчетныйДвиженияССубконто
    |ГДЕ
    |	НЕ ХозрасчетныйДвиженияССубконто.Регистратор ССЫЛКА Документ.КорректировкаДолга
    |	И НЕ ХозрасчетныйДвиженияССубконто.Регистратор ССЫЛКА Документ.КорректировкаРеализации";

Запрос.УстановитьПараметр("Дата1", НачалоДня(Дата1));
Запрос.УстановитьПараметр("Дата2", КонецДня(Дата2));
Запрос.УстановитьПараметр("СписокСчетовДТ", ПланыСчетов.Хозрасчетный.НайтиПоКоду("90.02"));
Запрос.УстановитьПараметр("СписокСчетовКТ", ПланыСчетов.Хозрасчетный.НайтиПоКоду("41.03.2"));

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

Важные моменты:

  1. Используем СубконтоКт1 — для счета 41 (Товары) первым субконто почти всегда является Номенклатура. Именно по этому полю мы будем искать строку в нашей таблице.
  2. Для получения количества в проводках используем КоличествоКт, так как товар списывается с кредита счета 41. Если количественный учет в базе отключен, это поле будет равно нулю.
  3. Используем условие ССЫЛКА вместо ТИПЗНАЧЕНИЯ — это более производительный способ проверки типа документа в запросе 1С.

Шаг 2. Эффективное сопоставление данных через поиск в таблице

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


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

Пока Выборка.Следующий() Цикл
    
    // Подготавливаем структуру для поиска нужной строки в ИтогТаблица
    ПараметрыПоиска = Новый Структура;
    ПараметрыПоиска.Вставить("Регистратор", Выборка.Регистратор);
    ПараметрыПоиска.Вставить("НаименованиеНоменклатуры", Выборка.Номенклатура.Наименование); // Или по ссылке, если в таблице ссылка
    
    НайденныеСтроки = ИтогТаблица.НайтиСтроки(ПараметрыПоиска);
    
    Для Каждого СтрокаОтчета Из НайденныеСтроки Цикл
        // Если в одной проводке сумма за всё количество по документу,
        // а в таблице строки разбиты, возможно потребуется пропорциональное деление.
        // Но в простейшем случае записываем общую сумму:
        СтрокаОтчета.СебестоимостьОбщая = Выборка.СуммаСебестоимости;
        
        // Расчет себестоимости единицы, если количество в строке больше нуля
        Если СтрокаОтчета.Количество <> 0 Тогда
             СтрокаОтчета.СебестоимостьЕдиницы = Выборка.СуммаСебестоимости / Выборка.КоличествоСебестоимости;
        КонецЕсли;
    КонецЦикла;
    
КонецЦикла;

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

Шаг 3. Использование GUID и НомерСтроки

Посмотрим на вопрос автора о связи через GUID или НомерСтроки. К сожалению, в типовой конфигурации «Бухгалтерия предприятия» прямая связь между записью регистра бухгалтерии и номером строки табличной части документа отсутствует. Поле НомерСтроки в регистре относится к порядковому номеру проводки в наборе записей регистратора, а не к строке документа Реализация товаров и услуг.

Поэтому единственным надежным ключом связи является комбинация Документ (Регистратор) + Номенклатура. Если же в одном документе продается одна и та же позиция с разной ценой или характеристиками (что для БП редкость), сопоставить их с проводками без изменения логики проведения документа будет невозможно.

Рекомендации по оптимизации

Если ваша таблица ИтогТаблица очень большая, метод НайтиСтроки может работать медленно. В таком случае разберем по шагам оптимизацию через Соответствие:

  1. Создаем Соответствие, где ключом будет уникальная строка, например: Строка(Выборка.Регистратор.УникальныйИдентификатор()) + "_" + СокрЛП(Выборка.Номенклатура.Код).
  2. Сначала одним циклом по выборке запроса заполняем это соответствие суммами себестоимости.
  3. Затем вторым циклом проходим по ИтогТаблица, формируем такой же ключ и мгновенно получаем значение из соответствия.

Такой подход позволит избежать вложенных циклов и значительно ускорит выгрузку данных в Excel, особенно если документов в отчете за период накопилось несколько тысяч. Помните также, что себестоимость в 1С:Бухгалтерии может корректироваться регламентной операцией «Закрытие месяца». Если вы формируете отчет за незакрытый период, данные о себестоимости могут быть либо предварительными (по плановым ценам или последним закупкам), либо отсутствовать вовсе, если списание настроено «по расчету в конце месяца».

← На главную