Зачем нужен архитектор 1С. Часть 2

gtp72yyqlijnpwhhbw5qwqsfbbc.png

В предыдущей статье habr.com/ru/companies/otus/articles/772490 я постарался на конкретном примере показать различие между разработчиком 1С и архитектором 1С. Насколько по разному они подходят к решению одной и той же задачи. В этот раз я приведу пример, в котором различие между разработчиком и архитектором доходит до крайности. Решение архитектора, с точки зрения разработчика, противоречит самим основам.
Если бы в 1С было полноценное ООП, тогда решение описываемой проблемы могло бы быть другим. Да и само решение нам должно быть интересно не как эталон, а скорее, как иллюстрация различия в мышлении.
Представим себе, что мы проектируем на 1С некий продукт для неопределенно широкого круга лиц. Что-то вроде типовой конфигурации. У нас будет несколько десятков документов, каждый из которых будет порождать движения в регистрах. Представим себе регистр остатков товаров на складах. Тут все более или менее однообразно. Документ поступления делает движения со знаком плюс. Документ расхода делает в принципе такие же движения, но со знаком минус. Есть еще возврат товаров от покупателя и возврат товаров поставщику. По отношению к регистру остатков, это в сущности те же самые документы поступления и списания. Еще может быть документ перемещения между складами. Этот документ делает одновременно движения со знаком минус для одного склада и со знаком плюс для другого. Также у нас может быть несколько вариаций как приходных, так и расходных документов. Мы можем выделять приход импортного товара в отдельный документ. Можем различать розничные и оптовые продажи. Что-то может продаваться на условиях комиссии и т.д.
В результате может возникнуть десяток-другой видов документов. Но, при этом, движения в регистр остатков товаров у этих документов будут однообразны. Алгоритм формирования движений может быть не совсем уж примитивным. Например, при формировании расходных движений, мы можем проводить проверку на возникновение отрицательных остатков и фиксировать этот факт каким-нибудь способом. Но сам алгоритм от одного вида документов к другому будет оставаться неизменным.
Как в этой ситуации действует разработчик? Он разрабатывает некую общую функции, а затем обращается к ней из процедуры проведения документа. В самом деле. Не копировать же один и тот же код 10–20 раз. Это не правильно. А что будет, если потребуется небольшое изменение? Править одно и то же в 10–20 местах? И все это с риском забыть сделать изменение в одном из мест? С точки зрения разработчика, тут не о чем рассуждать.
Как думает архитектор. А его внимание обращено на совсем другие вещи! У нас тиражируемое решение, которым будут пользоваться многие, думает он. И многие будут его дорабатывать. Возможность относительно легкой доработки мы преподносим потенциальным покупателям, как одно из основных достоинств. Наш маркетинг во многом строится на этом. Надо оценить, сколько специалистов потенциально могут принимать участие в таких доработках. Разработкой продукта у нас занимается один человек, а дорабатывать его будут не меньше, чем тысяча. (Разумеется, эти цифры условны, но пропорция, кстати, вполне соответствует тому, что мы имеем в случае с типовыми конфигурациями фирмы 1С). Поэтому потенциальные неудобства для нашего разработчика, связанные с необходимостью много раз исправлять одно и то же не идут ни в какое сравнение с неудобствами, которые мы создадим для этой тысячи специалистов.
Мы же хотим, чтобы с нашим продуктом было легко. Есть какой-то общий принцип. Документ делает движения в регистр. Где искать модуль проведения общеизвестно, это одно из основных свойств т.н. платформы 1С. Допустим, есть какая-то плевая проблема. Дело на 5 секунд. Открыл модуль, поправил, готово. Но это если алгоритм проведения описан тут же. А если нам надо «провалиться» в какую-то общую функцию, то все становится немного сложнее.
Если кому-то сейчас кажется, что я выдумываю проблему на пустом месте, то посмотрите, как это выглядит в настоящий момент в одной из типовых конфигураций 1С. Вот «Управление торговлей» одной из последних версий. Если быть точным, 11.5.10.73. Модуль проведения документа «РеализацияТоваровУслуг», тот самый, где должны быть описаны алгоритмы формирования записей в регистры, выглядит так

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

Да, мы здесь не видим никаких алгоритмов формирования записей в регистрах. Надо идти в общий модуль и смотреть процедуру ПроведениеДокументов.ОбработкаПроведенияДокумента ()

Смотрим

Процедура ОбработкаПроведенияДокумента(Документ, Отказ, ДопПараметры = Неопределено) Экспорт
	
	Если СвойстваДокумента(Документ).РежимЗаписи <> РежимЗаписиДокумента.Проведение Тогда
		Возврат;
	КонецЕсли;
	
	ПровестиДокумент(Документ, Отказ, ДопПараметры);
	
КонецПроцедуры

Здесь тоже нет того, что мы ищем. Надо идти в процедуру ПровестиДокумент (). Эта процедура не то, чтобы большая, но в один экран уже не влазит, поэтому не буду демонстрировать ее полностью. Порывшись в ней, найдем, что надо теперь нам надо изучить функцию ТаблицыДляДвижений ()

...
	Если Свойства.РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
		ТаблицыДляДвижений = ТаблицыДляДвижений(Документ, МенеджерДокумента, ДвижимыеРегистры, ДопПараметры);
		ОтразитьДвиженияПодчиненныхРегистров(Механизмы, Документ, ТаблицыДляДвижений, Отказ);
	КонецЕсли;
...

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

rfq9wj377ofe2gyujeknmu-efqu.png

Функция ТаблицыДляДвижений(Документ, МенеджерДокумента, ДвижимыеРегистры, ДопПараметры)
	
	Если ЗначениеЗаполнено(ДвижимыеРегистры) Тогда
		
		Если ДопПараметры = Неопределено Тогда
			ДопПараметры = ДопПараметрыИнициализироватьДанныеДокументаДляПроведения();
		КонецЕсли;
		
		ТаблицыДляДвижений = МенеджерДокумента.ДанныеДокументаДляПроведения(Документ.Ссылка,
																			ДвижимыеРегистры,
																			ДопПараметры);
		
		Если Документ.ДополнительныеСвойства.ПроведениеДокументов.Свойство("ДополнительныеТаблицыДанныхДокумента") Тогда
			Для Каждого Таблица Из Документ.ДополнительныеСвойства.ПроведениеДокументов.ДополнительныеТаблицыДанныхДокумента Цикл
				Если ТаблицыДляДвижений.Свойство(Таблица.Ключ) Тогда
					ВызватьИсключение СтрШаблон(НСтр("ru = 'Пересечение имен таблиц данных документа: ""%1"".'"), Таблица.Ключ);
				Иначе
					ТаблицыДляДвижений.Вставить(Таблица.Ключ, Таблица.Значение);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		Возврат ТаблицыДляДвижений;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции

Еще пара-тройка шагов, и мы наконец доберемся до того, что искали

Функция ДанныеДокументаДляПроведения(Документ, Регистры, ДопПараметры = Неопределено) Экспорт
	
	Если ДопПараметры = Неопределено Тогда
		ДопПараметры = ПроведениеДокументов.ДопПараметрыИнициализироватьДанныеДокументаДляПроведения();
	КонецЕсли;
	
	Запрос			= Новый Запрос;
	ТекстыЗапроса	= Новый СписокЗначений;
	
	Если Не ДопПараметры.ПолучитьТекстыЗапроса Тогда
		////////////////////////////////////////////////////////////////////////////
		// Создадим запрос инициализации движений
		
		ЗаполнитьПараметрыИнициализации(Запрос, Документ);
		
		////////////////////////////////////////////////////////////////////////////
		// Сформируем текст запроса
		СформироватьСуммыДокументаВВалютахУчета(Запрос, ТекстыЗапроса, Регистры);
		
		ТекстЗапросаТаблицаЗаказыКлиентов(Запрос, ТекстыЗапроса, Регистры);
		ТекстЗапросаТаблицаДвиженияСерийТоваров(Запрос, ТекстыЗапроса, Регистры);
		ТекстЗапросаТаблицаПереданнаяВозвратнаяТара(Запрос, ТекстыЗапроса, Регистры);
...

Увидим текст запроса, хотя и там тоже не обойдется без отсылки к еще одной функции в том же модуле менеджера.

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

Вот во что превращается 5-ти секундное дело. Поэтому наш архитектор решает, что алгоритмы формирования записей в регистрах будут располагаться в соответствующих общеизвестных местах в модулях документов в полном объеме, со всей сопутствующей логикой. И да, к изумлению разработчиков, они будут повторяться. Это может привести к дополнительным трудозатратам разработчиков продукта. Но их в тысячу раз меньше, чем тех, кто будет этот продукт дорабатывать. Кроме того, следует оценить вероятность этих самых дополнительных трудозатрат. Вполне возможно, что разработчики продукта, никогда и не «полезут» в логику проведения документов. Они ее один раз уже хорошо продумали, сделали, оттестировали. А вот специалист, который занимается адаптацией продукта под нужды конкретного заказчика, наоборот, скорее всего «полезет». Его для этого и позвали. В результате один человеко-час разработчика продукта противостоит более, чем тысяче человеко-часов таких же разработчиков, но занимающихся адаптацией продукта. В этой ситуации отдавать предпочтение удобству разработчиков продукта просто «не экологично».
Вот такой еще один пример различия в подходах разработчика и архитектора. Надеюсь, это немного прояснит для вас суть работы архитектора 1С.

rfq9wj377ofe2gyujeknmu-efqu.png

© Habrahabr.ru