Как заменить значение измерения регистра сведений и при этом не потерять числовые ресурсы. Универсальная процедура массовой замены значения измерения регистра сведений (1С: Программисту: Регистры сведений)

В том случае, когда в каком-то регистре сведений нужно заменить несколько видов значений какого-то конкретного измерения на заданное значение, при простой попытке замены с помощью метода СоздатьНаборЗаписей(), возможно возникновение ситуации попытки записи неуникального набора измерений. При этом могут потеряться числовые значения ресурсов регистра. Код 1С, универсально решающий такую задачу:

// Процедура выполняет в независимом регистре сведений "ИмяРегистраСведений" замену
// всех значений измерения "ИмяИзменяемогоИзмерения", // соответствующих массиву "МассивСтарыхЗначенийИзмерения"
// на новое значение измерения "НовоеЗначениеИзмерения".
// При этом для тех случаев, когда новый набор измерений оказывается совпадающим с уже существующим,
// осуществляется сложение ресурсов "МассивИменСлагаемыхРесурсов" (эти ресурсы должны иметь тип "число")
// В запрос можно передать дополнительное условие, ограничивающее выборку данных из регистра "ДопУсловие"
Процедура ЗаменаИзмеренияНезависимогоРегистра(ИмяРегистраСведений, ИмяИзменяемогоИзмерения, МассивСтарыхЗначенийИзмерения, НовоеЗначениеИзмерения, МассивИменСлагаемыхРесурсов, ДопУсловие = "")

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

	// Создадим набор записей и для плана обмена определим, что выпоняется загрузка данных:
	НаборЗаписей = РегистрыСведений[ИмяРегистраСведений].СоздатьНаборЗаписей();
	НаборЗаписей.ОбменДанными.Загрузка = Истина;

	// запрос к регистру:
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("МассивСтарыхЗначенийИзмерения",	МассивСтарыхЗначенийИзмерения);
	Запрос.УстановитьПараметр("НовоеЗначениеИзмерения",			НовоеЗначениеИзмерения);

	// Подготовим таблицу значений для позиционирования (установки отбора) на нужных записях регистра:
	Запрос.Текст =
	"ВЫБРАТЬ *
	|ИЗ
	|	РегистрСведений." + ИмяРегистраСведений + " КАК Регистр
	|ГДЕ
	|	Регистр." + ИмяИзменяемогоИзмерения + " В (&МассивСтарыхЗначенийИзмерения)" + ДопУсловие;

	Сообщить("1 Приступаем к выполнению запроса к данным регистра """ + ИмяРегистраСведений + """ - " + ТекущаяДата());

	ТаблицаЗаполнения = Запрос.Выполнить().Выгрузить();
	Сообщить("2 Приступаем к обработке результата запроса к регистру """ + ИмяРегистраСведений + """ - " + ТекущаяДата());

	МаксимальноеЗначениеИндикатора = ТаблицаЗаполнения.Количество();

	// заполнение строки progress bar
	ЭлементыФормы.Индикатор2.Значение = 0; // обнуляем, чтобы при повторном запуске статусбар сбросился в 0
	ЭлементыФормы.Индикатор2.МаксимальноеЗначение = МаксимальноеЗначениеИндикатора; // задаем значение для 100%

	Старт = ТекущаяДата();
	ОчереднойСтарт = Старт;

	// переменные для организации прерывания(задержки) - подробнее про организацию цикла задержки выполнения модуля см. http://www.1c-h.ru/?p=2529
	// ПроцентеВыполненния - это реквизит формы (доступный пользователю для редактирования), в котором хранится размер "порции", по порохождению которой нужно приостановить выполнение
	ШагПрерывания = 1;
	ЧислоПрерывания = МаксимальноеЗначениеИндикатора * ПроцентеВыполненния / 100;

	ШестьУтраТекущегоДня = (Дата(Год(ТекущаяДата()), Месяц(ТекущаяДата()), День(ТекущаяДата())) + 21600);
	ОдиннадцВечераТекДня = (Дата(Год(ТекущаяДата()), Месяц(ТекущаяДата()), День(ТекущаяДата())) + 82800);
	ШестьУтраСледующгДня = (Дата(Год(ТекущаяДата()), Месяц(ТекущаяДата()), День(ТекущаяДата())) + 108000);

	// для каждой строки таблицы значений установим отбор и перезапишем регистр
	Для Каждого СтрокаЗаполнения Из ТаблицаЗаполнения Цикл

		ТекущийПН = ЭлементыФормы.Индикатор2.Значение + 1; // - текущий порядковый номер обрабатываемой строки таблицы
		ЭлементыФормы.Индикатор2.Значение = ТекущийПН; // - указываем прирост внутри цикла для ползущего индикатора выполнения обработки

		// посчитаем, сколько времени затрачено и прикинем сколько времени осталось
		// для вывода на морду формы: уже затрачено времени:
		ЗатраченоВремени = ТекущаяДата() - Старт;
		// для вывода на морду формы: затрачено времени на обработку одной строки таблицы:
		ВремениНаЕдиницу = Окр(ЗатраченоВремени / ТекущийПН,8);
		// для вывода на морду формы: времени на выполнение еще требуется:
		ОсталосьВремени = Окр((МаксимальноеЗначениеИндикатора - ТекущийПН)*ВремениНаЕдиницу/60);

		Если ПрерватьНаПроцентахВыполнения И ТекущийПН > ЧислоПрерывания Тогда // ПрерватьНаПроцентахВыполнения - булево, галочка в форме о необходимости задержки выполнения
			// в дневное время цикл задержки:
			Если ТекущаяДата() > ШестьУтраТекущегоДня И ТекущаяДата() < ОдиннадцВечераТекДня Тогда // это день
				// сделаем паузу, чтобы рассосались блокировки
				// в этом цикле мы умножаем количество заданных секунд задержки для дня (СекундЗадержкиДнем) на номер шага прерывания, тем самым определяя, очередную порцию задержки относительно Текущего Времени
				Пока (ТекущаяДата() - Старт) < СекундЗадержкиДнем * ШагПрерывания Цикл
				КонецЦикла;
			// ночное время:
			ИначеЕсли ТекущаяДата() > ОдиннадцВечераТекДня И ТекущаяДата() < ШестьУтраСледующгДня Тогда
				// сделаем паузу, чтобы рассосались блокировки
				// в этом цикле мы умножаем количество заданных секунд задержки для ночи (СекундЗадержкиНочью) на номер шага прерывания, тем самым определяя, очередную порцию задержки относительно Текущего Времени
				Пока (ТекущаяДата() - Старт) < СекундЗадержкиНочью * ШагПрерывания Цикл
				КонецЦикла;
			КонецЕсли;
			//следующее число прерывания и очередной шаг прывания:
			ШагПрерывания = ШагПрерывания + 1;
			ЧислоПрерывания = МаксимальноеЗначениеИндикатора * ПроцентеВыполненния * ШагПрерывания / 100;

		КонецЕсли;
		// 1 - Сначала удалим записи, которые нужно удалить, запомнив слагаемые ресурсы во временной таблице значений "тзСлагаемых"

		// очистим таблицу слагаемых от строк:
		тзСлагаемых.Очистить();

		// для каждого элемента отбора определяем отбор для нашего набора записей по значению этой же графы в текущей строке из таблицы - резултата запроса:
		Для Каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
			ЭлементОтбора.Установить(СтрокаЗаполнения[ЭлементОтбора.Имя])
		КонецЦикла;

		НаборЗаписей.Прочитать(); // - сейчас в этом наборе записей одна строка регистра с уникальным набором измерений и с данными для слагаемых ресурсов

		Для Каждого текЗаписьНабора Из НаборЗаписей Цикл // строка в наборе записей тут одна, ну да ладно, надо же ее как-то получить ,почему бы не в цикле

			СтрокаСлагаемых = тзСлагаемых.Добавить();
			// запомним слагаемые в соответсвующих колонках:
			Для каждого ИмяКолонки Из МассивИменСлагаемыхРесурсов Цикл
				СтрокаСлагаемых[ИмяКолонки] = текЗаписьНабора[ИмяКолонки];
			КонецЦикла; // все, на выходе из цикла строка заполнена

			// Удаление и перезапись делаем в одной транзакции
			НачатьТранзакцию();

			НаборЗаписей.Удалить(текЗаписьНабора); // удаляем устаревшую запись из регистра
			НаборЗаписей.Записать(); // пока не запишем, удаление не подействует?

			// 2 - Теперь добавим новую строку с измененным значением измерения или приплюсуем суммируемые поля в уже существующую, если строка с таким набором измерений уже была

			// создаем менеджер записи
			текЗапись = РегистрыСведений[ИмяРегистраСведений].СоздатьМенеджерЗаписи();

			// заполним все поля новой записи из текущей строки таблицы с результатом запроса
			ЗаполнитьЗначенияСвойств(текЗапись, СтрокаЗаполнения);
			////// или то же самое можно сделать в цикле:
			////Для Каждого КолонкаТЗ Из ТаблицаЗаполнения.Колонки Цикл
			////	текЗапись[КолонкаТЗ.Имя] = СтрокаЗаполнения[КолонкаТЗ.Имя]; // заполнили пока полным набором страых значений
			////КонецЦикла;

			// подменим заменяемое измерение:
			текЗапись[ИмяИзменяемогоИзмерения] = НовоеЗначениеИзмерения;

			// ответственный момент: читаем эту запись (фактически пытаемся спозиционироваться - с проверкой, может уже существует), кстати, если эту команду не выполнить, то весь регистр очиститься как нечего делать, так что тут аккуратнее
			текЗапись.Прочитать();

			Если текЗапись.Выбран() Тогда // спозиционироваться удалось, значит такая запись уже существует и нужно складывать слагаемые ресурсы

				//текЗапись.Количество = текЗапись.Количество + Количество;
				Для каждого ИмяРесурса Из МассивИменСлагаемыхРесурсов Цикл
					текЗапись[ИмяРесурса] = текЗапись[ИмяРесурса] + СтрокаСлагаемых[ИмяРесурса];
				КонецЦикла;

				// записываем изменения в регистре
				текЗапись.Записать();

			Иначе // спозиционироваться не удалось, нужно добавить новую запись, слагаемые ресурсы можно просто записать

				//текЗапись.Записать();
				СтрокаЗаполнения[ИмяИзменяемогоИзмерения] = НовоеЗначениеИзмерения; // запишем новое значение в таблицу, чтобы установить по нему отбор и приплюсовать количество

				// создаем менеджер записи
				НовЗапись = РегистрыСведений[ИмяРегистраСведений].СоздатьМенеджерЗаписи();

				// заполним все поля новой записи из текущей строки таблицы с результатом запроса
				ЗаполнитьЗначенияСвойств(НовЗапись, СтрокаЗаполнения);
				НовЗапись.Записать();

			КонецЕсли;

			// Удаление и перезапись делаем в одной транзакции
			ЗафиксироватьТранзакцию();
		КонецЦикла;

		// очищаем для следующего использования:
		НаборЗаписей.Очистить();

	КонецЦикла;
	Сообщить("3 Окончание исправления  регистра """ + ИмяРегистраСведений + """ - " + ТекущаяДата());

КонецПроцедуры //ЗаменаРесурсаНезависимогоРегистра()

Здесь можно скачать пример обработки замены ресурса и измерения (в зависимости от регистра) «ПартияТовара» во всех регистрах сведений на примере конфигурации логистики: [download id=»8″]
В этой обработке используется приведенная тут универсальная процедура — можно посмотреть пример ее использования.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.