Как рассчитать стоимость единицы объема (1 литр) на основе цены упаковки (канистры) в 1С 7.7

Программист 1С v7.7 Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

При автоматизации учета в сфере услуг (например, в прачечных, химчистках или автомойках) часто возникает задача пересчета стоимости сырья. Моющие средства и расходные материалы обычно закупаются крупным оптом — в канистрах по 10, 20 или более литров. Однако при оказании услуг нормы расхода рассчитываются в миллилитрах или граммах на единицу работы (например, на 1 килограмм белья). Попытка рассчитывать и хранить цену за один литр или миллилитр непосредственно в карточке товара вручную приводит к огромным трудозатратам при каждом изменении закупочных цен.

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

Архитектурная ошибка: почему нельзя просто записывать цену за литр в справочник

Начинающие разработчики часто пытаются решить задачу «в лоб»: создать в справочнике номенклатуры реквизит ЦенаЗаЛитр и при изменении цены канистры программно делить её на объем и записывать обратно в справочник. Рассмотрим, почему этот подход ошибочен и опасен:

  1. Потеря истории изменений (периодичность). Цены на рынке постоянно меняются. Если мы просто перезаписываем реквизит в справочнике, мы теряем информацию о том, сколько стоило средство месяц назад. Документы за прошлые периоды при перепроведении начнут использовать новые цены, что приведет к искажению финансовой отчетности.
  2. Блокировка периодических реквизитов. В 1С 7.7 цены обычно хранятся как периодические реквизиты. Если цена была установлена документом (например, «Поступление ТМЦ» или «Переоценка»), платформа жестко заблокирует попытку изменить значение реквизита кодом, выдав ошибку «Значение установлено документом!».
  3. Избыточность и сложность поддержки. Если у вас 8 видов моющих средств и 20 различных программ стирки, ручное или полуавтоматическое обновление десятков связанных цен быстро превратится в рутину и приведет к ошибкам из-за человеческого фактора.

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

Шаг 1. Организация связи единиц измерения через подчиненные справочники

Для гибкого пересчета объемов (канистра → литр → миллилитр) мы рекомендуем использовать классический механизм подчиненных справочников. Проанализируем эту структуру подробнее.

Создадим справочник «Номенклатура» (владелец) и подчиненный ему справочник «Единицы». В подчиненном справочнике нам понадобятся следующие реквизиты:

За базовую единицу измерения номенклатуры (с коэффициентом 1) логично принять минимальную неделимую единицу расхода — миллилитр (мл). В этом случае коэффициенты для других упаковок будут выглядеть следующим образом:

При такой архитектуре, зная цену закупки одной канистры (например, 20 литров за 5800 рублей), мы всегда можем легко получить цену одного миллилитра программным путем:

ЦенаЗаМл = ЦенаКанистры / КоэффициентКанистры (20 000) = 0.29 руб.

Шаг 2. Разработка структуры «Спецификации стирки»

Чтобы автоматизировать расчет расхода средств в зависимости от программы стирки, нам понадобится справочник норм расходов. Опишем спецификацию:

  1. Создадим справочник «ПрограммыСтирки» (например: «Хлопок 60°», «Деликатная», «Шерсть»).
  2. Создадим подчиненный ему справочник «Спецификации». Каждый элемент этого справочника будет определять норму расхода конкретного моющего средства для данной программы.
  3. В справочнике «Спецификации» добавим реквизиты:
    • МоющееСредство (тип: Справочник.Номенклатура).
    • НормаРасхода (тип: Число, точность 3 знака). Норма расхода в миллилитрах на 1 килограмм сухого белья.

Это позволит нам гибко настраивать нормы расхода. Например, для программы «Хлопок 60°» мы можем указать: Стиральный порошок — 15 мл/кг, Кондиционер — 5 мл/кг. Для программы «Шерсть»: Специальный гель — 25 мл/кг.

Шаг 3. Программная обработка проведения и расчет себестоимости

Теперь рассмотрим, как связать все эти данные воедино при проведении документа списания (например, «Акт оказанных услуг» или «Акт стирки»). Документ содержит реквизиты: ПрограммаСтирки (ссылка на справочник) и ВесБелья (в килограммах).

Разберем по шагам алгоритм списания материалов в модуле документа в процедуре ОбработкаПроведения(). Нам нужно получить нормы расхода по программе, рассчитать объем списания в миллилитрах, перевести его в единицы учета на складе и рассчитать сумму списания по средней стоимости.

Посмотрим на пример программной реализации этого процесса на встроенном языке 1С 7.7:


Процедура ОбработкаПроведения()
    // Создаем объекты для работы со справочниками и регистрами
    СпрСпецификации = СоздатьОбъект("Справочник.Спецификации");
    РегОстатки = СоздатьОбъект("Регистр.ОстаткиМатериалов");
    
    // Устанавливаем фильтр спецификаций по выбранной в документе программе стирки
    СпрСпецификации.ИспользоватьВладельца(ПрограммаСтирки);
    СпрСпецификации.ВыбратьЭлементы();
    
    Пока СпрСпецификации.ПолучитьЭлемент() = 1 Цикл
        ТекущееСредство = СпрСпецификации.МоющееСредство;
        Норма = СпрСпецификации.НормаРасхода;
        
        // Вычисляем общий расход средства в миллилитрах для текущей стирки
        ОбщийРасходМл = Норма * ВесБелья;
        
        // Переводим расход в базовые единицы учета на складе (например, в литры)
        // Предположим, что учет на складе ведется в литрах. Коэффициент литра = 1000
        РасходВЕдиницахУчета = ОбщийРасходМл / 1000;
        
        // Получаем текущую среднюю себестоимость единицы на складе из регистров
        // Для этого используем временный расчет регистра на момент времени документа
        РегОстатки.УстановитьФильтр(ТекущееСредство);
        Если РегОстатки.ИтогиАктуальны() = 0 Тогда
            РегОстатки.ВременныйРасчет(1);
            РассчитатьРегистрыНа(ТекущийДокумент());
        КонецЕсли;
        
        БалансКоличество = РегОстатки.СводныйОстаток(ТекущееСредство, "Количество");
        БалансСумма = РегОстатки.СводныйОстаток(ТекущееСредство, "Сумма");
        
        // Рассчитываем среднюю цену за единицу учета на складе (за 1 литр)
        Если БалансКоличество > 0 Тогда
            СредняяЦенаЗаЛитр = БалансСумма / БалансКоличество;
            СуммаСписания = РасходВЕдиницахУчета * СредняяЦенаЗаЛитр;
        Иначе
            // Если остатка нет, выводим предупреждение и берем цену из прайса
            Предупреждение("Нет остатка по номенклатуре: " + ТекущееСредство.Наименование);
            Продолжить;
        EndIf;
        
        // Выполняем движение расхода по регистру остатков материалов
        РегОстатки.Номенклатура = ТекущееСредство;
        РегОстатки.Количество = РасходВЕдиницахУчета;
        РегОстатки.Сумма = СуммаСписания;
        РегОстатки.ДвижениеРасход();
        
        Сообщить("Списано средство: " + ТекущееСредство.Наименование + 
                 " в количестве: " + Строка(РасходВЕдиницахУчета) + " л. на сумму: " + Строка(СуммаСписания) + " руб.");
    КонецЦикла;
КонецПроцедуры

Альтернативный вариант: программный расчет и запись цены литра через объект «Периодический»

Если в вашей конфигурации нет регистров учета (база написана «с нуля» только на справочниках) и вам всё же требуется рассчитывать и сохранять значение цены одного литра в карточке номенклатуры при обновлении цены канистры, мы должны использовать специальный механизм для работы с периодическими реквизитами — объект Периодический.

Рассмотрим ситуацию: пользователь вносит новую цену канистры объемом 10 литров вручную в справочник ЦеныКанистр. Программа должна автоматически рассчитать цену 1 литра и записать её в периодический реквизит ЦенаЛитр справочника Номенклатура на рабочую дату документа или системы.

Посмотрим на пример кода, реализующего данную задачу:


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

Подведем итоги

Мы проанализировали два подхода к расчету стоимости единицы объема в 1С v7.7. Первый подход — архитектурный (через спецификации и динамический расчет в документах) — является наиболее профессиональным и отказоустойчивым. Он исключает дублирование данных и гарантирует точный расчет себестоимости в любой момент времени независимо от колебания рыночных цен. Второй подход — программный записью в справочник — применим в простых базах данных, где нет документов-регистраторов цен, но требует строгого использования объекта Периодический во избежание системных ошибок платформы.

← На главную