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