Мы остановились на построении дерева цен.
В дерево добавляем следующие колонки:
- Номенклатура
- Характеристика
- ЕдиницаИзмерения
- ВидНоменклатуры
- ХарактеристикаЦО
- СерияЦО
- УпаковкаЦО
- ЦеноваяГруппа
В цикле по ВидЦены из ВыбранныеЦены добавляются колонки:
- Валюта
- СтараяЦена
- Формула
- ПроцентИзменения
- СуммаИзменения
- ИзмененаВручную
- ФормулаИзмененаВручную
- ИзмененаАвтоматически
- ЗапретРедактирования
- Упаковка (если ИспользоватьУпаковкиНоменклатуры)
И тут нежданная радость. Если тип дерева - из интерфеса, то исполняется та самая простыня кода, которая даже выглядит страшно.
Но мы - на сервере, и дерево у нас - просто дерево.
Поэтому мы копируем дерево с колонками в сформированную ранее структуру и возвращаемся. Ура!
***
Лирическое отступление - 2. Здесь, на ИС, нашелся мануал для пользователя. Особенно приятна заключительная фраза там:
Вот мы с Вами и рассмотрели все нюансы, подводные камни.
И пожелание удачного внедрения - пользователю.
Что действительно порадовало, можно ожидать единого механизма ценообразования в УТ и ERP. С учетом себестоимости продукции для ERP, разумеется.
***
Однако продолжим наши раскопки.
Мы вернулись с деревом цен из УстановкаЦенСервер.ПостроитьДеревоЦен
Теперь процедура УстановитьЦены вручает нам параметры и отправляет рассчитать цены (наконец-то!)
// рассчитать цены
ПараметрыРасчета = Новый Структура();
ПараметрыРасчета.Вставить("ВидыЦен", МассивВидовЦен);
ПараметрыРасчета.Вставить("ТолькоВыделенныеСтроки", Ложь);
ПараметрыРасчета.Вставить("ТолькоНезаполненные", Ложь);
ПараметрыРасчета.Вставить("ЗагрузкаСтарыхЦен", Ложь);
ПараметрыРасчета.Вставить("ОкруглениеРучныхЦен", Ложь);
ПараметрыРасчета.Вставить("РасчетПоФормулам", Истина);
УстановкаЦенСервер.РассчитатьЦены(СтруктураФормы, ПараметрыРасчета);
Количества букв в этой процедуре не много, но, как и ожидалось, это не конечная глубина вложенности. Начиная с создания кеша (без проверки на Неопределено :) ).
// Рассчитывает цены в таблице цен
//
// Параметры:
// Форма - см. ПостроитьДеревоЦен.Форма
// ПараметрыРасчета - Структура - Параметры расчета
// КэшДанных - см. ИнициализироватьСтруктуруКэшаДанных
//
Процедура РассчитатьЦены(Форма, ПараметрыРасчета, КэшДанных = Неопределено) Экспорт
Форма.Модифицированность = Истина;
КэшДанных = ИнициализироватьСтруктуруКэшаДанных(КэшДанных);
МассивВидовЦен = Новый Массив;
Для Каждого ВидЦены Из ПараметрыРасчета.ВидыЦен Цикл
Если ТипЗнч(ВидЦены) = Тип("Структура") Тогда
МассивВидовЦен.Добавить(УстановкаЦенКлиентСервер.НайтиСтрокуВидаЦен(Форма.ВыбранныеЦены, ВидЦены.ВидЦены));
Иначе
МассивВидовЦен.Добавить(УстановкаЦенКлиентСервер.НайтиСтрокуВидаЦен(Форма.ВыбранныеЦены, ВидЦены));
КонецЕсли;
КонецЦикла;
Если Не ПараметрыРасчета.ТолькоВыделенныеСтроки Тогда
ТаблицаНоменклатуры = СоздатьТаблицуНоменклатурыПоДеревуЦен(Форма, Ложь);
Иначе
ТаблицаНоменклатуры = СоздатьТаблицуНоменклатуры(Форма);
Для Каждого ВыделеннаяСтрока Из Форма.Элементы.ДеревоЦен.ВыделенныеСтроки Цикл
СтрокаТаблицыЦен = Форма.ДеревоЦен.НайтиПоИдентификатору(ВыделеннаяСтрока);
НоваяСтрока = ТаблицаНоменклатуры.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТаблицыЦен);
Если Форма.ИспользуетсяЦенообразование25 Тогда
НоваяСтрока.УпаковкаЦОДляСвязи = ?(СтрокаТаблицыЦен.УпаковкаЦО = СтрокаТаблицыЦен.ЕдиницаИзмерения, Справочники.УпаковкиЕдиницыИзмерения.ПустаяСсылка(), СтрокаТаблицыЦен.УпаковкаЦО);
НоваяСтрока.СерияЦОДляСвязи = ?(СтрокаТаблицыЦен.СерияЦО.Предопределенный, Справочники.СерииНоменклатурыДляЦенообразования.ПустаяСсылка(), СтрокаТаблицыЦен.СерияЦО);
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если ПараметрыРасчета.ЗагрузкаСтарыхЦен Тогда
СтруктураПараметров = Новый Структура();
СтруктураПараметров.Вставить("МассивСтрокВидовЦен", МассивВидовЦен);
СтруктураПараметров.Вставить("ДатаДокумента", ПараметрыРасчета.ДатаСтарыхЦен);
СтруктураПараметров.Вставить("ПроцентИзмененияЦены", ПараметрыРасчета.ПроцентИзмененияЦены);
СтруктураПараметров.Вставить("ПрименятьОкругление", ПараметрыРасчета.ПрименятьОкругление);
СтруктураПараметров.Вставить("ТолькоНезаполненные", ПараметрыРасчета.ТолькоНезаполненные);
ЗагрузитьЗначенияБазовыхЦен(
Форма,
ТаблицаНоменклатуры,
КэшДанных,
СтруктураПараметров);
КонецЕсли;
Если ПараметрыРасчета.ОкруглениеРучныхЦен Тогда
ПрименитьОкруглениеКРучнымЦенам(Форма, ТаблицаНоменклатуры, МассивВидовЦен, КэшДанных);
КонецЕсли;
Если ПараметрыРасчета.РасчетПоФормулам Тогда
Если Не ПараметрыРасчета.ЗагрузкаСтарыхЦен Тогда
ЗагрузитьЗначенияБазовыхЦен(Форма, ТаблицаНоменклатуры, КэшДанных);
КонецЕсли;
ВычислитьЦеныПоДаннымИБ(Форма, ТаблицаНоменклатуры, МассивВидовЦен, ПараметрыРасчета.ТолькоНезаполненные, КэшДанных);
РассчитатьВычисляемыеЦены(
Форма,
ТаблицаНоменклатуры,
КэшДанных,
?(Не ПараметрыРасчета.ЗагрузкаСтарыхЦен, МассивВидовЦен, Неопределено),
ПараметрыРасчета.ТолькоНезаполненные,
,
ПараметрыРасчета.ВидыЦен);
КонецЕсли;
Если Форма.РассчитыватьАвтоматически Тогда
// Список видов цен, которые, которые зависят от изменяемых
ЗависимыеЦены = ПолучитьСтрокиНастроекЗависимыхВидовЦен(Форма, МассивВидовЦен);
РассчитатьВычисляемыеЦены(
Форма,
ТаблицаНоменклатуры,
КэшДанных,
ЗависимыеЦены,
ПараметрыРасчета.ТолькоНезаполненные);
КонецЕсли;
Если ТипЗнч(Форма.ДеревоЦен) = Тип("ДанныеФормыДерево") И ПараметрыРасчета.РасчетПоФормулам Тогда
Для Каждого ВидЦеныДляОбработки Из МассивВидовЦен Цикл //ДанныеФормыЭлементКоллекции
УстановитьПометкуИзмененныхФормул(Форма,,ВидЦеныДляОбработки.Ссылка);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Создается таблица с номенклатурой.
Подгружаются (считываются из базы) старые значения цен.
Загадочное "округление ручных цен" - даже знать не хочу, что это.
И - па-бам! - РасчетПоФормулам... две процедуры нам в стек
ВычислитьЦеныПоДаннымИБ(Форма, ТаблицаНоменклатуры, МассивВидовЦен, ПараметрыРасчета.ТолькоНезаполненные, КэшДанных);
РассчитатьВычисляемыеЦены(
Форма,
ТаблицаНоменклатуры,
КэшДанных,
?(Не ПараметрыРасчета.ЗагрузкаСтарыхЦен, МассивВидовЦен, Неопределено),
ПараметрыРасчета.ТолькоНезаполненные,
,
ПараметрыРасчета.ВидыЦен);
ВычислитьЦены вызывается в скриптах 7 раз, 5 - из модуля УстановкаЦенСервер (в том числе из нашей точки), дважды - из формы документа УстановкаЦенНоменклатуры.
Приводить ее листинг не будем, поскольку в ней в цикле по виду цены вызывается ВычислитьЗначенияЦеныПоДаннымИБ2_5.
В последней получаем схему компоновки данных (определенную для вида цены), проверяем ее на вариант ФО_20 и(!) ФО_25, дописываем текст запроса схемы компоновки, добавляем поля и связи по флагам опций (реально функциональных), загружаем настройки, определяем группировки, параметры.
Устанавливаем отбор по сегменту Номенклатуры.
И, наконец, выполняем запрос и выводим в таблицу значений ДанныеОтчета.
Если я правильно помню всю пройденную цепочку, это вообще первый раз мы обратились к базе запросом. В цикле, ага. А как тут без цикла, если схема компоновки у каждого вида цены - своя. Но если она настолько "своя", то как мы сумели каждую из них обработать одним и тем же куском кода?
Терзают автора смутные подозрения.
Отдельно получаем КоэффициентыУпаковокНоменклатурыДереваТоваров.
И - да, да, вы правы - вызываем очередной уровень вложенности: ЗагрузитьЦеныИзТаблицыЗначений.
Оправдывая титул "надменного и неприятного", предложу полюбоваться изящностью выбора "наценивать-не-наценивать":
Если СтрокаВидЦены.СпособЗаданияЦены = Перечисления.СпособыЗаданияЦен.НаценкаНаЦенуПоступления
Или СтрокаВидЦены.СпособЗаданияЦены = Перечисления.СпособыЗаданияЦен.НаценкаНаЦенуВводаОстатков
Или СтрокаВидЦены.СпособЗаданияЦены = Перечисления.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоКонкурентам
Или СтрокаВидЦены.СпособЗаданияЦены = Перечисления.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоПоставщикам
Или СтрокаВидЦены.СпособЗаданияЦены = Перечисления.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоСебестоимости Тогда
СтруктураПараметров.Вставить("Наценивать", Истина);
ЗагрузитьЦеныИзТаблицыЗначений(
Форма,
ДанныеОтчета,
ТаблицаКоэффициентовУпаковокНоменклатуры,
КэшДанных,
СтрокаВидЦены,
СтруктураПараметров);
Иначе
СтруктураПараметров.Вставить("Наценивать", Ложь);
ЗагрузитьЦеныИзТаблицыЗначений(Форма,
ДанныеОтчета,
ТаблицаКоэффициентовУпаковокНоменклатуры,
КэшДанных,
СтрокаВидЦены,
СтруктураПараметров);
КонецЕсли;
Нет, никаких претензий, чисто эстетика, что вы! Короче, я бы этот кусок сделал так:
Запрос = Новый Запрос("Выбрать &СрокаСпособ В (Значение(Перечисление.СпособыЗаданияЦен.НаценкаНаЦенуПоступления),
| Значение(Перечисление.СпособыЗаданияЦен.НаценкаНаЦенуВводаОстатков),
| Значение(Перечисление.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоКонкурентам),
| Значение(Перечисление.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоПоставщикам),
| Значение(Перечисление.СпособыЗаданияЦен.ЗаполнятьПоДаннымИБПоСебестоимости))
| КАК НацениватьНеНаценивать");
Запрос.УстановитьПараметр("СрокаСпособ",СтрокаВидЦены.СпособЗаданияЦены);
ВыборкаЗапроса = Запрос.Выполнить().Выбрать();
ВыборкаЗапроса.Следующий();
СтруктураПараметров.Вставить("Наценивать", ВыборкаЗапроса.НацениватьНеНаценивать);
ЗагрузитьЦеныИзТаблицыЗначений(Форма,
ДанныеОтчета,
ТаблицаКоэффициентовУпаковокНоменклатуры,
КэшДанных,
СтрокаВидЦены,
СтруктураПараметров);
Если кто-либо из критиков опять потребует замеров (см.комментарии), пусть сделает.
Только пусть сначала вынесет этот запрос из цикла по виду цены, а потом замеряет, сколько его душеньке будет угодно.
А нас ждет ЗагрузитьЦеныИзТаблицыЗначений. И это будет последний листинг в цепочке, которую автор сюда вытаскивает.
Нет, обойдемся без листинга, ничего, требующего особой внимательности там нет. Разве что вот это:
СтрокаТаблицыЦен = НайтиСтрокуДереваЦен(Форма, СтрокаИсточник, КэшДанных);
Для двух переданных параметров ищется вхождение СтрокаИсточник в дереве, входящем в форму.
Воздержусь от комментария, я же белый и приятный.
В общем, там пороги срабатывания, наценки, упаковки, валюта (опять же!).
Теперь рассмотрим РассчитатьВычисляемыеЦены
Там в цикле по Номенклатуре - цикл по ВидЦены, по "БазоваяЦена Из ВидЦены.ВлияющиеЦены":
ЗначениеЦены = ОбщегоНазначения.ВычислитьВБезопасномРежиме(Формула, ПараметрыДляВычисления);
... еще один вложенный уровень. Но там уже просто
Вычислить(Выражение);
**
Рассчитав цены, мы попадаем в попытку (и это здесь наш совсем-совсем последний шаг).
Документы.УстановкаЦенНоменклатуры.ЗаписатьИзмененияЦенНаСервере(СтруктураФормы, ТекстКомментария, 1);
Там в транзакции выполняется цикл проведения сохраненных в СтруктураФормы документов.
**
Итак, что мы увидели?
Во-первых, надо еще посмотреть, какие бывают формулы для Вычислить(Выражение).
Во-вторых, посмотреть, какие бывают схемы (шаблоны схем?) СКД. И почему их обрабатывают в цикле.
Без этого говорить о какой-либо оптимизации не приходится.
В-третьих, надо изобразить диаграмму исследованной нами здесь цепочки вложенных процедур. А то вот так, пробежавшись по тексту, в голове сложилось, конечно, общее представление о том, что происходит, но нужна картинка.
Итак, это план:
- формулы
- схемы СКД
- диаграмма с процедурами обновления цен
- и еще одна диаграмма с объектами конфигурации.