В конфигурации 1С:Управление нашей фирмой (УНФ) предусмотрен механизм динамического формирования цен по формулам относительно базовых статических видов цен (например, Закупочная или РекомендованнаяЗаводом). В типовом варианте механизм сводится к текстовой замене операндов на числовые значения с последующим вызовом встроенной функции Вычислить().
Однако на практике часто требуется сложная условная логика: например, если задана рекомендованная цена производителя, использовать её, а если она равна нулю или не заполнена — рассчитывать розничную цену как закупочную с наценкой 35%. Разберем, с какими ограничениями платформы приходится сталкиваться при решении этой задачи и как реализовать поддержку условий.
Проанализируем техническую причину, почему стандартная конструкция Если ... Тогда ... Иначе ... КонецЕсли не может быть напрямую передана в типовой расчет формулы. Метод платформы Вычислить() предназначен исключительно для выражений встроенного языка, возвращающих значение. Управляющие конструкции и операторы ветвления вызывают синтаксическую ошибку.
Самый простой способ обойти это ограничение без изменения программного кода — использовать тернарный оператор ?(), который поддерживается функцией Вычислить():
?([РекомендованнаяЗаводом] > 0, [РекомендованнаяЗаводом], [Закупочная] * 1.35)
Тернарные операторы можно вкладывать друг в друга, однако для конечных пользователей (менеджеров по ценообразованию) чтение и редактирование многоуровневых вложенных скобок представляет сложность. Рассмотрим варианты доработки конфигурации, позволяющие использовать привычный синтаксис Если ... Тогда ... Иначе.
Разберем по шагам реализацию пользовательского синтаксиса условий с помощью расширения конфигурации. В модуле ЦенообразованиеФормулыСервер переопределим процедуру РасчетДанныхПоФормуле с аннотацией &Вместо.
Логика работы механизма строится следующим образом:
ЕСЛИ, запускаем синтаксический разбор строки в массив структур.Условие и Значение.Вычислить(): при получении первого значения Истина берем соответствующую формулу значения и прерываем цикл.Посмотрим на пример реализации алгоритма:
&НаСервере
&Вместо("РасчетДанныхПоФормуле")
Процедура Расш1_РасчетДанныхПоФормуле(Знач ФормулаСтрокой, СтруктураОперандов, РасчетныеДанные = Неопределено, ОкруглятьВБольшуюСторону = Неопределено, ПравилоОкругления = Неопределено) Экспорт
// Заменяем идентификаторы операндов на их фактические значения
Для Каждого Операнд Из СтруктураОперандов Цикл
ФормулаСтрокой = СтрЗаменить(ФормулаСтрокой, Операнд.Ключ, Операнд.Значение);
КонецЦикла;
// Проверяем наличие условий в формуле
Если СтрНайти(ВРег(ФормулаСтрокой), "ЕСЛИ") > 0 Тогда
ЗначениеФормулы = Неопределено;
МассивУсловий = РазложитьУсловияВМассив(ФормулаСтрокой);
Для Каждого ЭлементУсловия Из МассивУсловий Цикл
Если Вычислить(ЭлементУсловия.Условие) Тогда
ЗначениеФормулы = ЭлементУсловия.Значение;
Прервать;
КонецЕсли;
КонецЦикла;
Если ЗначениеФормулы <> Неопределено Тогда
ФормулаСтрокой = ЗначениеФормулы;
КонецЕсли;
КонецЕсли;
// Вызов типового расчета финального числового выражения
ПродолжитьВызов(ФормулаСтрокой, Новый Структура, РасчетныеДанные, ОкруглятьВБольшуюСторону, ПравилоОкругления);
КонецПроцедуры
Для разбора исходного текста формулы на пары «Условие – Формула» используем вспомогательные функции:
Функция РазложитьУсловияВМассив(ТекстУсловия) Экспорт
МассивПодстрок = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ТекстУсловия, "Тогда", Ложь);
КоличествоПодстрок = МассивПодстрок.Количество();
МассивУсловийЗначений = Новый Массив;
Индекс = 0;
Для Каждого Подстрока Из МассивПодстрок Цикл
СтруктураУсловий = Новый Структура;
Если Индекс = 0 Тогда
// Первое условие после ключевого слова ЕСЛИ
СтруктураУсловий.Вставить("Условие", ПочиститьСтроку(Подстрока));
МассивУсловийЗначений.Добавить(СтруктураУсловий);
Индекс = Индекс + 1;
Продолжить;
КонецЕсли;
МассивСтроки = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(Подстрока, "Иначе", Ложь);
Если Индекс = (КоличествоПодстрок - 1) Тогда
// Финальный блок ИНАЧЕ ... КОНЕЦЕСЛИ
МассивУсловийЗначений[Индекс - 1].Вставить("Значение", ПочиститьСтроку(МассивСтроки[0]));
СтруктураУсловий = Новый Структура;
СтруктураУсловий.Вставить("Условие", "1=1");
СтруктураУсловий.Вставить("Значение", ПочиститьСтроку(МассивСтроки[1]));
МассивУсловийЗначений.Добавить(СтруктураУсловий);
Возврат МассивУсловийЗначений;
КонецЕсли;
// Промежуточные блоки ИНАЧЕЕСЛИ
МассивУсловийЗначений[Индекс - 1].Вставить("Значение", ПочиститьСтроку(МассивСтроки[0]));
СтруктураУсловий.Вставить("Условие", ПочиститьСтроку(МассивСтроки[1]));
МассивУсловийЗначений.Добавить(СтруктураУсловий);
Индекс = Индекс + 1;
КонецЦикла;
Возврат МассивУсловийЗначений;
КонецФункции
Функция ПочиститьСтроку(Знач Текст)
Текст = СтрЗаменить(Текст, "Если", "");
Текст = СтрЗаменить(Текст, "если", "");
Текст = СтрЗаменить(Текст, "Тогда", "");
Текст = СтрЗаменить(Текст, "тогда", "");
Текст = СтрЗаменить(Текст, "Иначе", "");
Текст = СтрЗаменить(Текст, "иначе", "");
Текст = СтрЗаменить(Текст, "КонецЕсли", "");
Текст = СтрЗаменить(Текст, "конецЕсли", "");
Текст = СтрЗаменить(Текст, "Конецесли", "");
Текст = СтрЗаменить(Текст, "конец", "");
Возврат СокрЛП(Текст);
КонецФункции
Если внесение доработок в конфигурацию или подключение расширений нежелательно, рассмотрим типовую альтернативу — расчет цен на основе схемы компоновки данных (произвольный запрос). В настройках вида цен УНФ доступен способ расчета цен на основании СКД.
Преимущества использования СКД:
ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ.ЕСТЬNULL() позволяет корректно обрабатывать номенклатурные позиции, по которым еще нет записей в регистре сведений ЦеныНоменклатуры.Вычислить().Пример выражения в запросе СКД для аналогичной задачи:
ВЫБОР
КОГДА ЕСТЬNULL(ЦеныРекомендованные.Цена, 0) > 0
ТОГДА ЦеныРекомендованные.Цена
ИНАЧЕ ЕСТЬNULL(ЦеныЗакупочные.Цена, 0) * 1.35
КОНЕЦ
При настройке и разработке механизмов расчета цен с условиями обратим внимание на важные детали:
Попытка ... Исключение, чтобы ошибка в одной позиции номенклатуры не прерывала расчет всего прайс-листа или проведения документа.Неопределено или Null, заменяя их числовым нулем (0).ПередЗаписью справочника видов цен путем тестового вычисления с фиктивными числовыми данными.