gifts2017

Ошибка расчета остатков в СКД и ее программное исправление на примере Универсального отчета

Опубликовал Владислав Рожевский (vvr908) в раздел Программирование - Практика программирования

В отчетах, построенных на СКД, в ряде случаев некорректно рассчитываются начальные и конечные остатки по группировкам. Проблема известная, и написано по этому поводу уже немало, но почему-то в типовом универсальном отчете по метаданным она до сих пор проявляется. Дело в том, что при программной работе с СКД разработчики отчета не учли ряд особенностей самой платформы 1С8 (причем это относится как к 8.1, так и к 8.2).
Эти особенности и методы программной работы с ними рассматриваются в данной статье.

Не знаю, многим ли уже довелось столкнуться с ошибкой расчета начальных и конечных остатки по группировкам. Лично мне "посчастливилось", притом неоднократно. Причина, как мне удалось выяснить, кроется в неверных настройках полей данных СКД, важности которых многие начинающие (да и не очень) программисты пока до конца не осознают.

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

Если вы ранее не сталкивались с этой проблемой, то для лучшего понимания ее сути предлагаю воспроизвести ее самим с помощью универсального отчета (по метаданным). Запускаем отчет, выбираем любой непустой регистр накопления с остатками и оборотами, включаем в настройках отчета (скрин1) флажок "Детальные записи", указываем какие-нибудь группировки и добавляем в состав выводимых полей Регистратор. Вуаля - начальные и конечные остатки суммируются по каждой группировке. Получается отчет с абсолютно некорректными цифрами, который показывать пользователям никак нельзя.

Для решения такой проблемы необходимо корректно заполнить настройки полей набора данных СКД - в частности, поле "Роль", которое имеет ключевое значение.

РЕШЕНИЕ интерактивное (не подходит для Универсального отчета):

Открываем схему компоновки данных вашего отчета и смотрим в настройки полей набора данных.

Для полей начальных и конечных остатков по каждому из ресурсов необходимо заполнить роль: выбрать группу ролей "Остаток" и в ней указать значение "Нач. остаток" или "Кон. остаток" соответственно. Так (скрин2) это делается в конструкторе СКД.

Аналогичным образом необходимо проставить роль "Измерение" для всех измерений вашего набора данных.

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

Такие настройки полей данных СКД в большинстве случаев позволяют добиться корректного расчета остатков по группировкам, когда с настройками по умолчанию они рассчитываются некорректно .

РЕШЕНИЕ программное (на примере Универсального отчета по метаданным):

Теперь рассмотрим, как исправить эту же ошибку в Универсальном отчете по метаданным. Универсальный отчет отличается от большинства прочих отчетов тем, что схема компоновки данных там генерируется полностью программно, поэтому настраивать роли для полей данных СКД тоже приходится программно.

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

Аналогичным образом при создании полей набора данных для измерений необходимо проставить им роль "Измерение". Код будет примерно следующим: 

НовоеИзмерение = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Измерение.Имя, Измерение.Синоним);

НовоеИзмерение.Роль.Измерение = Истина;

 

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

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

Кроме того, в отчет нигде программно не добавляются поля "Номер строки" и "Регистратор". Мне показалось это странным, т.к. в итоговом наборе данных они присутствуют.

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

Ниже я предлагаю рецепт, который помог мне почти полностью решить эту проблему платформы и Универсального отчета по метаданным: 

Вот этот фрагмент кода модуля объекта:

/

// Добавляем поля периода

Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда

ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);

КонецЕсли;

нужно заменить на следующий:

// Добавляем поля периода

Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда

СписокПериодов = ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);

//Заполним служебные поля и проставим периоды вручную, т.к. платформа их не заполняет

Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "НомерСтроки", "Номер строки");

Поле.Роль.НомерПериода = 1;

Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "Регистратор", "Регистратор");

Поле.Роль.НомерПериода = 2;

сч = 3;

Для Каждого ПолеПериод Из СписокПериодов Цикл

ПолеПериод.Значение.Роль.НомерПериода = сч;

Если сч > 3 Тогда

    ПолеПериод.Значение.Роль.ТипПериода = ТипПериодаКомпоновкиДанных.Дополнительный;

КонецЕсли;

сч = сч+1;

КонецЦикла;

КонецЕсли;

 

Мне удалось найти одно ограничение, связанное с этим решением. Для корректного расчета нач. и кон. остатков необходимо, чтобы при использовании в отчете каких-либо реквизитов документа-регистратора сам регистратор также был выбран. В остальном универсальный отчет после таких доработок больше не вызывает аллергии у пользователей.

UPDATE: мне подсказали в комментариях, что на диске ИТС в свое время вышла статья на эту тему. К сожалению, эта статья прошла мимо меня, но помочь мне в решении проблем с универсальным отчетом она бы смогла лишь отчасти. Увы, заморочки платформы со служебными полями СКД, такими как "Recorder", там также не описаны.

Тем не менее, я нашел на Мисте ссылку на нее и рекомендую ознакомиться, ибо полезно:

статья "Типичные проблемы при расчете остатков"

В любом случае, я надеюсь, что моя статья поможет всем тем, у кого возникли аналогичные проблемы. В свое время я потратил на поиски этого решения уйму времени...

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Форель (indigo_) 22.01.10 10:14
Спасибо за статью. Мне тоже довелось с этой ошибкой столкнуться, теперь уже стало понятнее, в чем проблема
2. Владислав Рожевский (vvr908) 22.01.10 10:23
(1) Да, я помню, что сильно удивился первый раз, когда мне пользователи на эту ошибку указали. Пришлось разбираться, т.к. уже было штук 30 различных сохраненных пользовательских настроек - не писать же на каждую свой отчет... ;)

Заодно вспомнил, что у меня есть материал для еще одной заметочки на тему универсального отчета, на днях напишу и выложу.
3. naus (Danil.Potapov) 22.01.10 18:20
"Мне удалось найти одно ограничение, связанное с этим решением"

попробуй установить флаг у поля "регистратор" - обязательное.
4. Владислав Рожевский (vvr908) 22.01.10 22:08
(3) Я думал об этом, но не пробовал. Мне кажется, это не совсем верное решение проблемы, т.к. регистратор далеко не всегда вообще нужно выводить в отчет (как и детальные записи в принципе). Что будет выведено в отчет, если в настройках флажок "Детальные записи" снят, а поле Регистратор помечено в СКД как обязательное? Не знаю, надо будет попробовать.
5. Владислав Рожевский (vvr908) 23.01.10 01:23
(3) Попробовал сделать Регистратор обязательным полем. Сразу же полезли ошибки СКД из-за последовательности нумерации периодов. Никому этот вариант не рекомендую, не зря он казался мне подозрительным. ;) Лучше уж оставить все в исходном варианте, как в статье.
6. naus (Danil.Potapov) 23.01.10 13:33
(5) Да, про обязательное поле брякнул не то. Сейчас посмотрю....
7. Алексей Воронин (Dirk Diggler) 27.01.10 09:10
На каких релизах проверяли?
8. Владислав Рожевский (vvr908) 27.01.10 17:42
(7) На Бух 1.6.22.4 и ранее.
В УПП та же картина на нескольких релизах (точнее, на всех, виденных мной), точно номера не помню.

Только что проверил на платформе 8.2.10.73 в той же Бух 1.6.22.4 - глючит все так же.
9. Марина Строева (Glafira) 29.01.10 09:29
В стандартных отчетах 1С:Торговли при выводе детальных записей по регистратору необходимо добавлять также поле Период для правильного расчета группировок и остатков (Период+Документ движения (регистратор)). Видимо здесь такая же ситуация.
10. Владислав Рожевский (vvr908) 29.01.10 13:03
(9) Попробовал добавить к регистратору еще поле "Период секунда" - не помогает, как и добавление любого другого поля. Ошибка остается. Стандартные отчеты УТ, очевидно, реализованы несколько иначе (скорее всего, при их создании все же использовался конструктор СКД).
11. Владислав Рожевский (vvr908) 29.01.10 13:04
Больше всего напрягает тот факт, что создаваемые самой платформой при инициализации компоновщика настроек служебные поля "Регистратор" ("Recorder") и "номер строки" потом никак нельзя изменять - к ним не удается обратиться как к обычному полю набора данных СКД.

Я надеялся, что в новых версиях платформы что-нибудь изменится, но уже 8.2.10 вышла, а воз и ныне там.
12. Игорь Фрунзэ (gorodok11) 16.08.10 23:16
У меня есть похожий отчет, только на основе универсального отчета, по регистрам партий (комплексная конфигурация, УПП). Проблема с выводом начальных остатков и расшифровок по документам. При выводе расшифровки строки в режиме период+регистратор и отключения детальных записей - все получается (проверено) кроме начальных остатков. Кто-нибудь поможет решать эту проблему?

http://files.mail.ru/I4ZLS2
13. Эстер Коган (e.kogan) 05.10.11 17:01
К сожалению, без прописывания ролей полям остатков тоже ни фига не работает... Пришлось менять процедуру из модуля (роли для измерений и остатков):

Процедура ДобавитьПоляНабораДанных()
		
	Если ТипДанных = "РегистрыНакопления" ИЛИ ТипДанных = "РегистрыСведений" Тогда
		
		// Добавляем измерения
		Для каждого Измерение Из Метаданные[ТипДанных][ИмяОбъекта].Измерения Цикл
			// Э.
			полеизм=ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Измерение.Имя, Измерение.Синоним);
			полеизм.Роль.Измерение=Истина;
			//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Измерение.Имя, Измерение.Синоним);
			// Э.
		КонецЦикла;
		
		// Добавляем реквизиты
		Если ПустаяСтрока(ИмяТаблицы) Тогда
			Для каждого Реквизит Из Метаданные[ТипДанных][ИмяОбъекта].Реквизиты Цикл
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Реквизит.Имя, Реквизит.Синоним);
			КонецЦикла;
		КонецЕсли;
		
		// Добавляем поля периода
		//Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда
		//	ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);
		//КонецЕсли;
        // Я. http://infostart.ru/public/64376/
		Если ИмяТаблицы = "ОстаткиИОбороты" ИЛИ ИмяТаблицы = "Обороты" Тогда
			СписокПериодов = ТиповыеОтчеты.ДобавитьПоляПериодаВНаборДанных(СхемаКомпоновкиДанных.НаборыДанных[0]);
			//Заполним служебные поля и проставим периоды вручную, т.к. платформа <= 8.1.14 делает это некорректно
			Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "НомерСтроки", "Номер строки");
			Поле.Роль.НомерПериода = 1;
			Поле = ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], "Регистратор", "Регистратор");
			Поле.Роль.НомерПериода = 2;
			сч = 3;
			Для Каждого ПолеПериод Из СписокПериодов Цикл
				ПолеПериод.Значение.Роль.НомерПериода = сч;
				Если сч > 3 Тогда
					ПолеПериод.Значение.Роль.ТипПериода = ТипПериодаКомпоновкиДанных.Дополнительный;
				КонецЕсли;
				сч = сч+1;
			КонецЦикла;
		КонецЕсли;	
		// Я.
		
		// Добавляем ресурсы
		Для каждого Ресурс Из Метаданные[ТипДанных][ИмяОбъекта].Ресурсы Цикл
			Если ИмяТаблицы = "Обороты" Тогда
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Оборот", Ресурс.Синоним);
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "Оборот");

			ИначеЕсли ИмяТаблицы = "ОстаткиИОбороты" Тогда
				
				ПапкаПолейНабораДанных = СхемаКомпоновкиДанных.НаборыДанных[0].Поля.Добавить(Тип("ПапкаПолейНабораДанныхСхемыКомпоновкиДанных"));
				ПапкаПолейНабораДанных.Заголовок   = Ресурс.Синоним;
				ПапкаПолейНабораДанных.ПутьКДанным = Ресурс.Имя;
	                                                                                         
				// Э. нач
				полерес=ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "НачальныйОстаток", Ресурс.Синоним + " нач. остаток", Ресурс.Имя + "." + Ресурс.Имя + "НачальныйОстаток");
				полерес.Роль.Остаток=Истина;
				полерес.Роль.ТипОстатка=ТипОстаткаКомпоновкиДанных.НачальныйОстаток;
				полерес.Роль.ГруппаОстатка=Ресурс.Имя;
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "НачальныйОстаток");
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Приход", Ресурс.Синоним + " приход", Ресурс.Имя + "." + Ресурс.Имя + "Приход");
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Приход");
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Расход", Ресурс.Синоним + " расход", Ресурс.Имя + "." + Ресурс.Имя + "Расход");
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Расход");
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Оборот", Ресурс.Синоним + " оборот", Ресурс.Имя + "." + Ресурс.Имя + "Оборот");
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Оборот");
				
				полерес=ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "КонечныйОстаток", Ресурс.Синоним + " кон. остаток", Ресурс.Имя + "." + Ресурс.Имя + "КонечныйОстаток");
				полерес.Роль.Остаток=Истина;
				полерес.Роль.ГруппаОстатка=Ресурс.Имя;
				полерес.Роль.ТипОстатка=ТипОстаткаКомпоновкиДанных.КонечныйОстаток;
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "КонечныйОстаток");
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "НачальныйОстаток", Ресурс.Синоним + " нач. остаток", Ресурс.Имя + "." + Ресурс.Имя + "НачальныйОстаток");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "НачальныйОстаток");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Приход", Ресурс.Синоним + " приход", Ресурс.Имя + "." + Ресурс.Имя + "Приход");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Приход");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Расход", Ресурс.Синоним + " расход", Ресурс.Имя + "." + Ресурс.Имя + "Расход");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Расход");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Оборот", Ресурс.Синоним + " оборот", Ресурс.Имя + "." + Ресурс.Имя + "Оборот");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "Оборот");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "КонечныйОстаток", Ресурс.Синоним + " кон. остаток", Ресурс.Имя + "." + Ресурс.Имя + "КонечныйОстаток");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "." + Ресурс.Имя + "КонечныйОстаток");
				// Э. кон
				
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "НачальныйОстаток", Ресурс.Синоним + " нач. остаток");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "НачальныйОстаток");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Приход", Ресурс.Синоним + " приход");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "Приход");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Расход", Ресурс.Синоним + " расход");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "Расход");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "Оборот", Ресурс.Синоним + " оборот");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "Оборот");
				//
				//ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя + "КонечныйОстаток", Ресурс.Синоним + " кон. остаток");
				//ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя + "КонечныйОстаток");
				
			ИначеЕсли ТипДанных = "РегистрыСведений" Тогда
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя, Ресурс.Синоним);
				
				Типы = Ресурс.Тип.Типы();
				Если Типы.Количество() = 1 И Типы[0] = Тип("Число") Тогда
					ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя);
				КонецЕсли;
				
			ИначеЕсли ИмяТаблицы = "" Тогда
				
				ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Ресурс.Имя, Ресурс.Синоним);
				ТиповыеОтчеты.ДобавитьПолеИтога(СхемаКомпоновкиДанных, Ресурс.Имя);
				
			КонецЕсли;	
		КонецЦикла;
		
	ИначеЕсли ТипДанных = "Документы" ИЛИ ТипДанных = "Справочники" Тогда
		
		Если ПустаяСтрока(ИмяТаблицы) Тогда
			ОбъектМетаданных = Метаданные[ТипДанных][ИмяОбъекта];
		Иначе
			ОбъектМетаданных = Метаданные[ТипДанных][ИмяОбъекта].ТабличныеЧасти[ИмяТаблицы];
		КонецЕсли;
		
		// Добавляем реквизиты
		Для каждого Реквизит Из ОбъектМетаданных.Реквизиты Цикл
			ТиповыеОтчеты.ДобавитьПолеНабораДанных(СхемаКомпоновкиДанных.НаборыДанных[0], Реквизит.Имя, Реквизит.Синоним);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры
...Показать Скрыть
14. Владислав Рожевский (vvr908) 14.02.12 00:38
Недавно мне понадобилась эта статья, когда мы стали пытаться анализировать полученную из другой базы через COM-соединение таблицу остатков и оборотов с детальными записями. В исходной базе остатки считались корректно, но после выгрузки результата запроса в таблицу значений и обработки этой таблицы в другой БД настройки пришлось прописывать вручную - тут и вспомнилось про эту статью.

Выяснилось, что если содержимое строки таблицы значений содержит движения накопления по ресурсам вида "нач.остаток/приход/расход/кон.остаток", то для корректного расчета остатков по группировкам средствами СКД необходимо выполнить настройки, аналогичные описываемым в статье.
В исходной таблице значений обязательно должно быть поле периода (дата движения или период из р/н). Если его там нет - добавляем. Затем в настройках СКД нужно присвоить этому полю роль "Период, 1", и далее заполнить роли для полей остатков и измерений, как описано в статье.
При этом желательно все измерения сделать обязательными, а поля необязательных измерений убрать вообще, т.к. группировка по "необязательным" измерениям мешает корректному расчету остатков.
Конечно, результат получится далеко не столь гибким, как мы привыкли видеть в отчетах на СКД. Но он хотя бы будет верным!
15. Vladimir (Boroda) 16.02.12 21:02
Отличная статья, очень полезные комментарии, хорошо её дополняющие. Спасибо.
16. Владислав Рожевский (vvr908) 16.02.12 22:58
(15) Boroda, спасибо за столь положительный отзыв!
Как раз после того, как написал последний комментарий, я еще раз все перечитал и решил полностью переработать текст статьи.
Теперь статья не только про универсальный отчет, которым далеко не все пользуются, но главным образом про нюансы настройки СКД.
Я не претендую на особое знание глубинных механизмов СКД, но делюсь тем, что удалось накопать мне самому и вместе с коллегами.
17. Владислав Рожевский (vvr908) 18.02.12 18:31
(13) e.kogan, в статье сразу предлагается прописать роли для полей остатков с помощью типовой процедуры общего модуля. ;)
Т.е. вашу конструкцию вида
полерес.Роль.Остаток=Истина;
полерес.Роль.ТипОстатка=ТипОстаткаКомпоновкиДанных.НачальныйОстаток;
полерес.Роль.ГруппаОстатка=Ресурс.Имя;
...Показать Скрыть

можно заменить на:
ТиповыеОтчеты.ЗаполнитьПолеНабораДанныхОстаток(полерес, Ресурс.Имя);

По моему опыту, этого будет вполне достаточно.
Хотя и ваш вариант, конечно, тоже будет работать.

P.S. Все обсуждаемые изменения касаются процедуры ДобавитьПоляНабораДанных() модуля объекта УниверсальныйОтчетПоМетаданным.
Все желающие "починить" универсальный отчет могут заменить код этой процедуры на код, приведенный e.kogan в (13).
18. Роман Романов (romansun) 18.02.12 20:09
на мисте есть обсуждение этой темы... ссылку, к сожалению, не дам... гуглите

на ИТС есть статья, полностью и на примерах раскрывающая тему ролей в СКД. В указанном обсуждении на мисте есть ссылки на скрины статьи ИТС :)


что интересно, в обычных запросах с итогами остатки считаются верно самой 1С на уровне платформы. И вот эту "верность" срубить весьма непросто.. )) Даже при скидывании выборки во временную таблицу 1С всё равно "помнит" остаточные поля и верно считает остатки (т.е. арифметически - неверно). Отбить память об остатках получается только при втором или третьем перескидывании во временную таблицу...

а в СКД получается, наоборот, механизмы запросных итогов они видимо отключили, как и сами итоги, поэтому приходится принудительно распределять роли полей для корректного расчета остатков.
19. Владислав Рожевский (vvr908) 18.02.12 21:06
(18) romansun, спасибо за наводку, надо будет поискать :)
Только статья-то исходно была написана 2 года назад, когда еще никакой информации толком не было...

UPD: вот, нашел на мисте кое-что с ИТС: статья "Типичные проблемы при расчете остатков"
20. Эстер Коган (e.kogan) 20.02.12 13:34
(17) это хорошо, когда конфигурация типовая :)
21. Владислав Рожевский (vvr908) 20.02.12 13:38
(20) e.kogan, ну у вас в коде тоже без вызова типовых функций все того же общего модуля не обошлось ))
22. Эстер Коган (e.kogan) 20.02.12 13:47
*упс, дважды отправилось
23. Эстер Коган (e.kogan) 20.02.12 13:47
(21) Гм, оно у меня переопределяется :) На самом деле это форма с экспортными процедурами :) самый простой способ выдрать всё на свете из конфы.
24. Роман Романов (romansun) 20.02.12 22:45
(19)

дадада.. :) она самая

и что меня убивает лично во всей этой ситуации - что такая, действительно, важная вещь, как "Роли" в библии СКД от Хрусталёвой (библии, ибо больше нигде консолидированно инфы по СКД толком нет) описана в виде одной странички с общим смыслом: "а еще в СКД есть роли, они нужны... нужны, т.к. помогают при расчете остатков и других клевых штук.. больше я вам ничего не скажу,... как-то так"
25. Владислав Рожевский (vvr908) 20.02.12 23:17
(23) e.kogan, хитро придумано )) я, честно говоря, тупо общий модуль перетащил в свое время в самописную конфу и поудалял оттуда все лишнее.

Одно отличие тут может стать заметным - если общий модуль может быть серверным, то форма - нет.
26. Владислав Рожевский (vvr908) 05.06.12 01:18
Был приятно удивлен появлением ссылки на статью на главной странице в рубрике "Выбор экспертов".
Хоть статья уже и не новая, я все же надеюсь, что она окажется познавательной в том числе и для тех, кто перейдет по ссылке из чистого любопытства. Для меня в свое время и сами-то роли в СКД были довольно загадочным явлением, а уж о таком странном поведении платформы я и вовсе не подозревал.
27. Пацталоцци Задунайский (Пацталоцци) 11.06.12 19:20
Самое удивительное и смешное во всей этой истории - это постоянные разговоры восьмёрочников о том, что восьмёрка якобы на порядок круче и эффективнее чем 7.7.
Да это же стыд и срам.
Это называется прогрессивная платформа!
Для написания обычного отчёта на СКД приходится залезать в дебри такого извращённого бреда, в который любой обкуренный шаман с бубном побрезгует соваться.
olbu; kentavr27; +2 Ответить 1
28. Пацталоцци Задунайский (Пацталоцци) 11.06.12 19:23
Вы хоть сами понимаете, что вместо нормальной и стабильной программерской работы, занимаетесь совершенно ненормальным "научным тыком" ???
29. Пацталоцци Задунайский (Пацталоцци) 11.06.12 19:30
Для странного, алогичного поведения платформы вы ищете странные, алогичные методы багофиксинга.
И я бы ещё понял, если бы выгода от использования СКД была настолько существенна, что перекрывала бы эти затраты времени и рассудка.
Но ведь этого не происходит.
Насколько я понимаю, основным плюсом СКД декларировалась очень высокая скорость разработки отчётов и, как следствие, экономия времени программиста?
Ну и?
Много времени сэкономили, бегая с бубном вокруг сабжа?
30. Владислав Рожевский (vvr908) 12.06.12 18:21
(29) Пацталоцци, я не вполне понимаю - какие есть альтернативы? Забить на СКД и перевести всех клиентов обратно на 7.7?
Мы работаем с теми механизмами, какие есть, а претензии имеет смысл адресовать не сюда, а к 1С.
31. Владислав Рожевский (vvr908) 12.06.12 18:25
(27) Пацталоцци, в любом случае, в статье идет речь об ошибке, которая проявляется в довольно специфичных условиях. Обычно для разработки простого отчета на СКД никаких сакральных знаний не требуется, достаточно просто написать запрос.
32. Виталий (nafa) 13.06.12 21:22
(31) Ничего себе специфические условия - поле остатка в запросе. В Бухгалтерии таких вобще-то процентов 90.

Столкнулся с ситуацией, когда прописывания ролей полям оказалось недостаточным. Пришлось сделать обязательным 2 поля: период и регистратор. (на то как отчет выдается пользователю не влияет, только на процедуру расчета.)

Дополнение

Пришлось и все измерения обязательными сделать (т.к. иначе отсутствие/наличие одного из измерений в запросе влияло на остаток :( ) зато теперь работает как часы.
33. Владислав Рожевский (vvr908) 14.06.12 10:34
(32) nafa, обычно поля остатка правильно распознаются конструктором СКД и роли им прописываются без участия программиста. Конечно, можно заполнять схему компоновки программно, а не интерактивно. В этом случае роли приходится прописывать вручную, но это все же довольно специфичная ситуация.
34. Виталий (nafa) 14.06.12 11:29
(33) vvr908,
Поля остатка то распознаются, но почему без этих галок то в самом примитивном отчете по регистру (простой запрос типа выбрать измерение1, измерение2, начальный остаток, приход, расход, конечный остаток, период, регистратор из регистр.ххх.ОстаткиИОбороты(,, Регистратор,)) итог по полям остатка при наличии в выбранных пользователем группировках периода/регистратора и их отсутствии оказывается совершенно разный, а с галками одинаковый.
(Отчет создан без единой ручной правки, только эти галки)
35. Владислав Рожевский (vvr908) 14.06.12 16:21
(34) nafa, да, насчет этих галок вообще вопрос интересный.
А если пользователь, к примеру, будет группировать отчет по периоду, но с заранее неизвестной периодичностью (неделя или месяц)? Делать оба поля ПериодНеделя и ПериодМесяц обязательными? Как на это отреагирует СКД? Надо будет проэкспериментировать и дополнить статью результатами эксперимента...
36. Виталий (nafa) 14.06.12 23:44
(35) vvr908,
Есть есть обязательные группировки Регистратор и Период, то пруппировки ПериодМесяц и ПериодНеделя обязательными делать не надо, и так работает. Кроме того в статье по ссылке выше написано что нельзя одновременно использовать неделю и более крупные периоды - можно, но просто надо понимать что в этом случае неделя которая находится на пересечении месяцев разделится на два кусочка.
37. Максим Кузнецов (Makushimo) 13.09.13 06:31
Полезная статья.
Спасибо автор.
38. Александр Жерздев (Al777) 24.07.14 09:18
Поставил обязательными поля Период и Регистратор. Остатки теперь выводит правильно, но по периодам перестал отчёт работать. В чём может быть причина?
39. Олег (olbu) 22.08.14 14:49
Потратил полдня на это, нигде не видел, что периодичность виртуальной таблицы должна быть "Авто". У меня было "Регистратор" - ничего не получалось, пока не поменял на "Авто"...
40. Валерий Таранович (OpKc) 25.02.15 13:17
(32) nafa, спасибо тебе, мил человек! перерыл пол-интернета, но только в твоём комментарии нашёл истину: "Пришлось сделать обязательным 2 поля: период и регистратор."
41. Ильнур Гараев (fordes) 27.05.16 15:55
(32) nafa, спасибо! Присоединяюсь "обязательно"!
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа