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

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

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

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

1. Определяем цель и контекст задачи

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

  1. Оперативный просмотр рентабельности: Менеджеры по продажам хотят быстро видеть наценку по каждой позиции прямо в документе, чтобы принимать обоснованные решения о скидках или ценообразовании.
  2. Контроль "стоп-цен": Иногда необходимо запретить продажу товара ниже определенной закупочной цены или с неприемлемо низкой рентабельностью. Отображение закупочной цены помогает визуально контролировать этот порог.
  3. Информационная функция: Просто для справки, чтобы видеть, по какой цене товар был закуплен.

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

2. Понимание механизмов 1С:Управление торговлей (УТ 11)

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

2.1. Работа с упаковками и единицами измерения в УТ 11

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

  1. КоличествоУпаковок: Количество в выбранной пользователем упаковке.
  2. Количество: Количество в базовой единице измерения номенклатуры (автоматически пересчитывается из КоличествоУпаковок с учетом коэффициента).

Таким образом, нам не придется вручную заниматься сложными пересчетами коэффициентов из базовой единицы в единицу упаковки, так как система УТ делает это за нас. Важно будет лишь получить закупочную цену, соответствующую выбранной единице или упаковке, или правильно пересчитать ее к текущей единице измерения строки документа.

2.2. Источники данных для определения себестоимости

Для определения себестоимости товаров в 1С:УТ используются регистры накопления. Основными из них являются:

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

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

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

3. Реализация решения: добавление колонки и программный расчет

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

3.1. Модификация формы документа

Мы будем работать с формой документа РеализацияТоваровУслуг. Нам необходимо выполнить следующие шаги:

  1. Откроем конфигуратор: Запускаем 1С в режиме конфигуратора.
  2. Найдем документ: В дереве объектов конфигурации найдем ветку Документы и выберем РеализацияТоваровУслуг.
  3. Откроем форму: Развернем документ, найдем ветку Формы и откроем ФормаДокумента.
  4. Добавим реквизиты в табличную часть: В дереве реквизитов формы найдем табличную часть Товары. Добавим новые реквизиты, например:
    • ЦенаЗакупки (Тип: Число, Точность: 2)
    • СуммаЗакупки (Тип: Число, Точность: 2)
    • Дельта (Тип: Число, Точность: 2, может быть вычисляемой)

    Эти реквизиты будут храниться непосредственно в строке табличной части документа.

  5. Разместим колонки на форме: Перетащим новые реквизиты из дерева реквизитов формы в элементы формы, расположив их в таблице Товары. Настроим их заголовки (например, "Цена закупа", "Сумма закупа", "Наценка (%)").

3.2. Программное заполнение закупочной цены

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

Основные события, в которых нам нужно будет пересчитывать закупочную цену:

  1. При создании на сервере: ПриСозданииНаСервере - для первоначального заполнения при открытии или создании нового документа.
  2. При изменении номенклатуры: Событие ПриИзменении для поля Номенклатура в табличной части.
  3. При изменении упаковки/единицы измерения: Событие ПриИзменении для поля Упаковка (или ЕдиницаИзмерения, если упаковки не используются) в табличной части.
  4. При изменении количества: Событие ПриИзменении для поля КоличествоУпаковок или Количество.
  5. При изменении даты документа: Событие ПриИзменении для поля Дата документа, так как закупочная цена может зависеть от даты.
  6. При подборе товаров: В обработке подбора также можно сразу заполнять эти поля.

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

Функция для получения закупочной цены (пример псевдокода):


// На сервере
Функция ПолучитьЗакупочнуюЦенуДляСтроки(Номенклатура, Количество, Упаковка, ДатаДокумента, Организация)

    // Инициализируем цену закупки
    ЦенаЗакупки = 0;
    
    // Определим базовую единицу измерения номенклатуры
    // В УТ 11, если работаем через упаковки, система уже пересчитала Количество
    // в базовых единицах. Нам нужно получить закупочную цену за базовую единицу
    // и затем, при необходимости, пересчитать ее к выбранной Упаковке.
    
    // 1. Получение себестоимости из регистра СебестоимостьТоваров
    // Это предпочтительный способ, так как учитывает метод списания (ФИФО, по средней и т.д.)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    СебестоимостьТоваровОстатки.СтоимостьОстаток / СебестоимостьТоваровОстатки.КоличествоОстаток КАК Цена
        |ИЗ
        |    РегистрНакопления.СебестоимостьТоваров.Остатки(
        |        &ДатаДокумента,
        |        Номенклатура = &Номенклатура
        |            И Организация = &Организация
        |            И ВидЗапасов = &ВидЗапасов // Важно для раздельного учета НДС и прочего
        |    ) КАК СебестоимостьТоваровОстатки
        |ГДЕ
        |    СебестоимостьТоваровОстатки.КоличествоОстаток > 0
        |
        |УПОРЯДОЧИТЬ ПО
        |    СебестоимостьТоваровОстатки.Период УБЫВ"; // Если нужна последняя себестоимость до даты документа
    
    Запрос.УстановитьПараметр("ДатаДокумента", ДатаДокумента);
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
    Запрос.УстановитьПараметр("Организация", Организация);
    Запрос.УстановитьПараметр("ВидЗапасов", Перечисления.ВидыЗапасов.ВНаличии); // или другой вид запасов

    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        ЦенаЗакупкиБазоваяЕдиница = Выборка.Цена;
    Иначе
        // Если нет остатков или себестоимость не определена, можно попробовать получить
        // последнюю цену поставщика из регистра сведений
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |    ЦеныНоменклатурыКонтрагентовСрезПоследних.Цена КАК Цена
            |ИЗ
            |    РегистрСведений.ЦеныНоменклатурыКонтрагентов.СрезПоследних(
            |        &ДатаДокумента,
            |        Номенклатура = &Номенклатура
            |    ) КАК ЦеныНоменклатурыКонтрагентовСрезПоследних
            |
            |УПОРЯДОЧИТЬ ПО
            |    ЦеныНоменклатурыКонтрагентовСрезПоследних.Период УБЫВ";

        Запрос.УстановитьПараметр("ДатаДокумента", ДатаДокумента);
        Запрос.УстановитьПараметр("Номенклатура", Номенклатура);

        РезультатЗапросаЦеныПоставщика = Запрос.Выполнить();
        Если Не РезультатЗапросаЦеныПоставщика.Пустой() Тогда
            ВыборкаЦеныПоставщика = РезультатЗапросаЦеныПоставщика.Выбрать();
            ВыборкаЦеныПоставщика.Следующий();
            ЦенаЗакупкиБазоваяЕдиница = ВыборкаЦеныПоставщика.Цена;
        Иначе
            ЦенаЗакупкиБазоваяЕдиница = 0;
        КонецЕсли;
    КонецЕсли;

    // Теперь, если Упаковка не является базовой единицей, пересчитаем цену
    Если ЦенаЗакупкиБазоваяЕдиница > 0 И Упаковка <> Неопределено Тогда
        КоэффициентПересчета = Упаковка.Коэффициент; // Получаем коэффициент из объекта Упаковки
        Если КоэффициентПересчета > 0 Тогда
            // Цена закупки будет пересчитана на одну единицу выбранной упаковки
            ЦенаЗакупки = ЦенаЗакупкиБазоваяЕдиница * КоэффициентПересчета;
        Иначе
            ЦенаЗакупки = ЦенаЗакупкиБазоваяЕдиница; // Если коэффициент 0 или 1, то это базовая единица или ошибка
        КонецЕсли;
    Иначе
        ЦенаЗакупки = ЦенаЗакупкиБазоваяЕдиница;
    КонецЕсли;

    Возврат ЦенаЗакупки;

КонецФункции

// Пример обработчика события на форме документа
// На сервере
Процедура ТоварыНоменклатураПриИзмененииНаСервере(Элемент)

    ТекущиеДанныеСтроки = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанныеСтроки = Неопределено Тогда Возврат; КонецЕсли;

    // Получаем текущие параметры строки
    Номенклатура = ТекущиеДанныеСтроки.Номенклатура;
    Количество = ТекущиеДанныеСтроки.Количество; // Количество в базовых единицах
    Упаковка = ТекущиеДанныеСтроки.Упаковка; // Выбранная упаковка
    ДатаДокумента = Объект.Дата;
    Организация = Объект.Организация;

    Если Номенклатура = Неопределено Тогда
        ТекущиеДанныеСтроки.ЦенаЗакупки = 0;
        ТекущиеДанныеСтроки.СуммаЗакупки = 0;
        ТекущиеДанныеСтроки.Дельта = 0;
        Возврат;
    КонецЕсли;

    // Вызываем функцию получения закупочной цены
    ЦенаЗакупкиСтроки = ПолучитьЗакупочнуюЦенуДляСтроки(Номенклатура, Количество, Упаковка, ДатаДокумента, Организация);

    // Заполняем новые поля
    ТекущиеДанныеСтроки.ЦенаЗакупки = ЦенаЗакупкиСтроки;
    ТекущиеДанныеСтроки.СуммаЗакупки = ЦенаЗакупкиСтроки * Количество; // Сумма по количеству в базовых единицах
    
    // Расчет дельты (наценки)
    Если ТекущиеДанныеСтроки.ЦенаЗакупки > 0 Тогда
        ТекущиеДанныеСтроки.Дельта = (ТекущиеДанныеСтроки.Цена - ТекущиеДанныеСтроки.ЦенаЗакупки) / ТекущиеДанныеСтроки.ЦенаЗакупки * 100; // Наценка в процентах
    Иначе
        ТекущиеДанныеСтроки.Дельта = 0;
    КонецЕсли;

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

// Аналогичные обработчики нужно добавить для ПриИзменении полей Упаковка, КоличествоУпаковок, а также для ПриСозданииНаСервере и ПриИзменении поля Дата документа.
// При изменении КоличествоУпаковок (и соответственно Количество) потребуется пересчет СуммыЗакупки.

// Пример для ПриСозданииНаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // ... стандартный код ...
    Для Каждого СтрокаТовары Из Объект.Товары Цикл
        // Присваиваем значения для вызова нашей функции
        Если СтрокаТовары.Номенклатура <> Неопределено Тогда
            ЦенаЗакупкиСтроки = ПолучитьЗакупочнуюЦенуДляСтроки(СтрокаТовары.Номенклатура, СтрокаТовары.Количество, СтрокаТовары.Упаковка, Объект.Дата, Объект.Организация);
            СтрокаТовары.ЦенаЗакупки = ЦенаЗакупкиСтроки;
            СтрокаТовары.СуммаЗакупки = ЦенаЗакупкиСтроки * СтрокаТовары.Количество;
            Если СтрокаТовары.ЦенаЗакупки > 0 Тогда
                СтрокаТовары.Дельта = (СтрокаТовары.Цена - СтрокаТовары.ЦенаЗакупки) / СтрокаТовары.ЦенаЗакупки * 100;
            Иначе
                СтрокаТовары.Дельта = 0;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Обратите внимание на несколько важных моментов в коде:

  1. Приоритет получения цены: Сначала мы пытаемся получить себестоимость из регистра СебестоимостьТоваров, так как она наиболее точно отражает фактические затраты. Если это не удается (например, нет остатков), то обращаемся к регистру ЦеныНоменклатурыКонтрагентов за последней зарегистрированной ценой поставщика.
  2. Учет даты документа: Запросы используют дату документа для получения актуальных данных на момент его формирования.
  3. Пересчет для упаковки: Если в документе выбрана упаковка, отличная от базовой единицы, мы используем коэффициент из объекта Упаковка для пересчета закупочной цены к единице этой упаковки.
  4. Расчет Дельты: Колонка "Дельта" рассчитывается как процент наценки ((ЦенаПродажи - ЦенаЗакупки) / ЦенаЗакупки * 100).
  5. Обработчики событий: Важно, чтобы при изменении любого параметра строки, влияющего на закупочную цену (номенклатура, количество, упаковка), а также при изменении даты документа, происходил пересчет.

4. Альтернативные подходы и связанные возможности

Хотя основная задача — вывести закупочные цены в документ, есть и другие, смежные решения, которые могут быть полезны:

4.1. Использование отчетов для анализа рентабельности

Для более глубокого и сводного анализа рентабельности по продажам существуют стандартные отчеты в УТ (например, "Валовая прибыль", "Анализ себестоимости", "Прибыльность продаж"). Эти отчеты, как правило, предоставляют более детальную информацию, учитывают все затраты и позволяют анализировать данные за любой период. Отображение цен в документе носит оперативный характер, а отчеты — аналитический.

4.2. Контроль "стоп-цен" и рентабельности

Если цель не только информировать, но и контролировать, то в 1С:УТ есть механизмы для этого:

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

Пример кода для проверки при проведении (в модуле объекта):


// В модуле объекта документа "РеализацияТоваровУслуг"
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // ... стандартный код проведения ...

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

    // ... продолжение стандартного кода проведения ...
КонецПроцедуры

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

Заключение

Мы подробно рассмотрели, как получить и отобразить сумму закупочных цен номенклатуры в документе реализации 1С:УТ. Использование механизмов УТ по работе с упаковками, запрос к регистрам себестоимости и цен контрагентов, а также правильная организация кода на сервере позволяют эффективно решить эту задачу.

Ключевые моменты, которые мы должны помнить:

  1. Всегда уточняйте конфигурацию и ее версию, так как функционал может существенно отличаться.
  2. Используйте существующие механизмы 1С (Упаковки, регистры себестоимости) для упрощения разработки.
  3. Выносите ресурсоемкие расчеты на сервер.
  4. Обеспечьте актуальность данных, привязывая расчеты к соответствующим событиям изменения полей на форме.
  5. Рассмотрите дополнительные возможности, такие как контроль "стоп-цен" и использование отчетов для комплексного анализа.

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

← На главную