1с регистр сведений запрет удаления

Обновлено: 08.07.2024

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

Особенности реализации : если в наборе записей регистра сведений больше одной записи, то подробно протоколируется только первая запись набора и количество записей в наборе.

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

О практической пользе этой процедуры. Эта процедура, будучи своевременно встроена в базу 1С Зарплата и управление персоналом 8.2, реально помогла мне отследить порчу конкретным пользователем штатного расписания в базе 1С Зарплата и Управление персоналом 8.2 и переклассифицировать инцидент «порча данных штатного расписания» из разряда «программный сбой» в «ошибку пользователя».

Процедура ЖурнализированиеРегистраСведенийПередЗаписью ( Источник , Отказ , Замещение ) Экспорт

ТаблицаДляПечати1 = НаборЗаписей . Выгрузить ();

Если ТаблицаДляПечати1 . Количество () = 0 Тогда
// в регистр пишется пустой набор записей, т.е. происходит удаление из регистра сведений по наложенному в наборе отбору

ЧтоПроисходит1 = "Удаление в регистре сведений" ;

МетаданныеНабора = НаборЗаписей . Метаданные ();

ИмяРегистра = МетаданныеНабора . ПолноеИмя ();

// Формируем текст условия блока ГДЕ основного запроса,
// в соответствии с установленным отбором для набора записей

Запрос = Новый Запрос ;

СписокПолейУсловияОтбораТекст = "" ;
Итерация = 0 ;
Для каждого ЭлементОтбора Из НаборЗаписей . Отбор Цикл
Если не ЭлементОтбора . Использование Тогда
Продолжить;
КонецЕсли;

Если Итерация = 0 Тогда
ОписаниеИзменений1 = ОписаниеИзменений1 + "Отбор : " ;
Иначе
СписокПолейУсловияОтбораТекст = СписокПолейУсловияОтбораТекст + " И " ;
КонецЕсли;

СписокПолейУсловияОтбораТекст = СписокПолейУсловияОтбораТекст + " Набор." + ЭлементОтбора . Имя + " = &" + ЭлементОтбора . Имя ;

// здесь нужно сохранить в журнале регистрации значения отборов
ОписаниеИзменений1 = ОписаниеИзменений1 + "" + ЭлементОтбора . Имя + " = " + СокрЛП ( Формат ( ЭлементОтбора . Значение , "ДФ=dd.MM.yyyy" )) + "" + Символы . ПС ;

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

Итерация = 1 ;
КонецЦикла;

Если Итерация = 1 Тогда
СписокПолейУсловияОтбораТекст = " ГДЕ " + СписокПолейУсловияОтбораТекст ;
КонецЕсли;

Запрос . Текст = "ВЫБРАТЬ * ИЗ " + ИмяРегистра + " КАК Набор
| " + СписокПолейУсловияОтбораТекст + "" ;

// ТаблицаДляПечати1 = Запрос.Выполнить().Выгрузить();
// а тут лежат старые данные в регистре (которые перетираются. )
// их можно анализировать и печатать

Иначе // записывается непустой набор записей

ЧтоПроисходит1 = "Запись в регистр сведений" ;

Для каждого Запись1 Из ТаблицаДляПечати1 Цикл

Для каждого Колонка1 Из ТаблицаДляПечати1 . Колонки Цикл

ЗначениеКолонки1 = Запись1 [ Колонка1 . Имя ];
Если ЗначениеЗаполнено ( ЗначениеКолонки1 ) Тогда // печатаем почти все значения непустых измерений, ресурсов, реквизитов!

ТипЗнач1 = ТипЗнч ( ЗначениеКолонки1 );

МожноПечататьПолноеЗначениеКолонки1 = Истина;
Если ТипЗнач1 = Тип ( "Число" ) Тогда
Если ЗначениеКолонки1 > 1000 Тогда
// защита данных в журнале регистрации : не печатаем числовые значения колонок, если они превышают 1000 !
// например, это могут быть вилки окладов!
МожноПечататьПолноеЗначениеКолонки1 = Ложь;
КонецЕсли;
КонецЕсли;

Если МожноПечататьПолноеЗначениеКолонки1 Тогда
ОписаниеИзменений1 = ОписаниеИзменений1 + Колонка1 . Имя + " = " + Формат ( ЗначениеКолонки1 , "ДФ=dd.MM.yyyy" ) + Символы . ПС ;

Если СсылкаДляОтраженияВЖурналеРегистрации1 = Неопределено Тогда
Если ТипЗнач1 <> Тип ( "Дата" ) Тогда
СсылкаДляОтраженияВЖурналеРегистрации1 = ЗначениеКолонки1 ;
КонецЕсли;
КонецЕсли;

Иначе
ОписаниеИзменений1 = ОписаниеИзменений1 + Колонка1 . Имя + " > 1000" + Символы . ПС ;
КонецЕсли;

Прервать; // прочтём только первую запись

Если ТаблицаДляПечати1 . Количество () > 1 Тогда

// по остальным записям - укажем только полное количество записей

ОписаниеИзменений1 = "Всего записей набора = " + ТаблицаДляПечати1 . Количество () + "; первая запись :" + Символы . ПС + ОписаниеИзменений1 ;

ЗаписьЖурналаРегистрации ( ЧтоПроисходит1 , УровеньЖурналаРегистрации . Предупреждение , МетаданныеНабора , СсылкаДляОтраженияВЖурналеРегистрации1 , СокрЛП ( ОписаниеИзменений1 ));

(1) Нет такого понятия "Удаление записей регистра". Есть Запись пустого набора.
Можно в модуле записей регистра вызвать исключение, если идет запись пустого набора.

(1) Нет такого понятия "Удаление записей регистра". Есть Запись пустого набора.
Можно в модуле записей регистра вызвать исключение, если идет запись пустого набора.

(11) Спасибо за информацию - очень пригодилась. Но со временем обнаружила побочный эффект. Старые записи регистра тоже редактировать не возможно. В связи с этим вопрос, который уже возникал ранее - как отличить удаление от редактирования? (16)
Еще раз. нет такого понятия "Удаление", есть пустой набор.
Если НаборЗаписей.Количество() = 0 - это "удаление", иначе - это редактирование
Если вы не в модуле набора записей, то помните, что количество записей в наборе зависит от установленного отбора по измерениям (17) НаборЗаписей.Количество() = 0 даже если я поменяла значение одного из полей записи. Поэтому и перестало работать. Редактировать записи тоже нельзя. Возможно Количество() подразумевает количество записей с ТАКИМ сочетанием значений полей как я пытаюсь записать. Но такого сочетания правда еще нет. Так что увы - не работает.
Вопрос решили другим способом - решили подчинить регистр документу. Так и редактировать удобнее в дальнейшем и проблема случайного удаления записей решается. Возможно Количество() подразумевает количество записей с ТАКИМ сочетанием значений полей как я пытаюсь записать. Но такого сочетания правда еще нет. (19) Тут речь о том, что при редактировании существующей записи регистра сведений набор записей сначала полностью очищается, а потом записываются новые значения в этот набор.
И получается, что при удалении записей из регистра записывается пустой набор записей с количество() = 0, а при редактировании записи тоже записывается пустой набор записей, а после этого записывается набор с новыми данными.
Если вы добавите команду "Сообщить(ЭтотОбъект.Количество())" в событие "ПриЗаписи" для любого регистра сведений и попробуете удалить запись, а потом поредактировать существующую - сами увидите это всё удаление это частный случай редактирования или через код

Вам нужно почитать про регистры. Иначе не сделаете.

Когда Вы записываете набор записей в регистр, то, по умолчанию, все записи из регистра сведений, соответствующие отбору НабораЗаписей удаляются, и все записи из набора записываются в РегистрСведений. Если отбор не заполнен и в НабореЗаписей нет ни одной записи, то методом Записать(), вы очистите весь регистр.

(7) И как мне поможет это запретить удаление записей в регистре? Я бы попытался решить этот вопрос через настройку формы списка конкретного регистра, так чтобы убрать видимость действия "Удалить" с учетом прав пользователей. Если есть права то пометка на удаление потом стандартная процедура под админом или если вам доступно то Удаление помеченных объектов выбираем только ту запись регистра что пометили жмём удалить если ругается и не удаляет то пишет с чем запись связана и с какими данными в левой части окна обработки удаление помеченных на удаление будет нашь объект или запись регистра в правой связи в левой нужно ссылки на нашу запись регистра которую хотим удалить заминитт другой записью из этого же регистра правильной. После повторить процедуру поиска и удаления уже когда связь измените удалится как миленькая. Перед всем этим я бы до кучи сделал бы стандартную обработку поиск и удаление дублей для данного регистра. Чтоб объединить, похожие записи если в регистре бардак а потом уже удаление помеченные. После поиска дублей верного дубли и кривые записи должны стать легкоудаляемые если это не так то при удалении помеченных объектов меняйте ссылки в документах и данных я описал чуть выше по кнопке изменить когда в левом окне получите если получите объект наша запись регистра с справа ссылки на места её использования. Мне это помогло когда регистры чистил очень помогло правильно Поиск и удаление дублей потом Удаление помеченные объектов. Для справочников аналогично и для всего что есть в базе. Будьте потренируйтесь на тестовой базе с 1-2 записями созданы и намеренно разберётесь со связями на примере удаления контрагента если их 2 или 3 в базе одиноковых и на обоих есть платёжка хоть 1 если есть платёжка, платёжное поручение оба скидываем на одного контрагента обьединяем поиском дублей одного из 2 контрагентов на котором не будет ПП ни одной обработка поиска дублей метит на удаление а потом уже удаление помечены удалит из справочника косяк так правильно. Для БП 3.0 делаем так в ЗУП 3.1 аналогично. По моему во всех современных конфигурациях это есть даже вроде в ЗУП 2.5

У наборов записей и менеджеров записи есть метод Модифицированность().

Как уже было сказано выше, при изменении записи, набор записывается 2-жды, первый раз пустой, для удаления данных, второй для записи новых значений.

Таким образом, при добавлении новой записи:
1.

При изменении записи
1.

При удалении записи
1.

(20) Уточните, пожалуйста. Правильно ли я понимаю, что такая проверка подразумевается как универсальная и должна осуществляться в модуле набора записей регистра?

(20) Только что поставил эксперимент на платформе 8.3.12.1685.

Действительно, все работает так, как Вы описали. Именно такие значения и видно в модуле набора записей в обработчике "ПередЗаписью". Но к сожалению, не является универсальным "рецептом", т.к. подобное поведение наблюдается только при работе с менеджером записи, а соответственно и при интерактивной работе с записями регистра. При работе с набором записей поведение иное.

- пробовал скрывать элементы меню (все равно удаляет по нажатию DEL)
- пробовал обработку событий формы (в форме списка работает, в форме элемента не нашел)

Лучший способ в модуле, работает спасибо besometr

В модуле набора записей для регистра сведений

(23) Извините, пожалуйста, за навязчивость. Просто еще раз хочу отметить, что такой подход актуален только для интерактивной работы пользователя. Т.е. если пользователь запустит какую-нибудь обработку вот с таким алгоритмом, проверка не сработает:
. Т.е. если пользователь запустит какую-нибудь обработку вот с таким алгоритмом, пров

У меня все обработки мои, так что плохого не сделают.
А враждебная обработка может чего угодно наделать.

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

    При добавлении новой записи

Потом записывается новая, при этом, как и при добавлении новой записи

Таким образом, чтобы программно запретить удаление записи регистра сведений, нужно создать следующую процедуру в модуле набора записей:

Как запретить удаление записи регистра сведений 1С по условию

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

Этот вариант работает для непериодического регистра сведений. Для периодических регистров нужно в менеджер записи добавить еще и период.

Как программно запретить удаление записи регистра сведений 1С : 2 комментария

Можно также в форме списка регистра сведений сделать так
&НаКлиенте
Процедура СписокПередУдалением(Элемент, Отказ)
ТекСтрока=Элементы.Список.ТекущиеДанные;
//далее проводить анализ реквизитов записи через ТекСтрока. или ТекСтрока[ ]
//и при необходимости
Отказ=Истина;
КонецПроцедуры

Михаил, да, такой способ должен сработать. Но у него есть одно важное ограничение: он будет работать только при удалении записи из формы списка регистра. А если запись удаляется из какой-то другой формы, или вообще программно, то запрет уже не сработает.

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

Манипулирование записями регистров без использования регистратора

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

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

Однако в 1С:Предприятии 8 существует возможность изменять записи регистров без участия документа. При этом следует учитывать, что каждая запись регистра всегда подчинена одному и только одному регистратору (документу). Поэтому с точки зрения "времени жизни" записи всегда подчинены конкретным регистраторам. Однако, записи могут изменяться без участия самого документа. Главное, чтобы в них имелась ссылка на документ.

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

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

Таким образом, чтобы изменить записи регистра необходимо:

  • создать набор записей;
  • установить отбор по определенному регистратору;
  • прочитать набор;
  • изменить записи набора;
  • записать набор.

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

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

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

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

В регистрах расчетов кроме отбора по регистратору для набора записей можно также установить отбор по другим значениям измерений (только по равенству). Это позволяет выполнить изменение части записей, подчиненных одному регистратору.

Доброго.
Удаление из подчиненного периодического регистра сведений. Помогите оптимизировать код.
Есть регистр сведений Хранилище акцизных марок.
Периодичность - По позиции регистратора.
Режим записи - Подчинение регистратору.
Измерение Марка, строка длина 160 символов, пишется туда типа такого
170300181766771018001JY3BOHGSSEHPTLU7N57B333G5QDVUZJPLXC3C57ID4X3K2SGV3BTPM5C7TISRHULGNEJ6XXDCIVKH6B6JIIPREFYDXJLMGAMYDT64AZK42ANKLL2MBSQSTSOJBHSDVOWA .
В ресурсах есть ОтметкаВыбытия – тип булево.
Остальные ресурсы в данном вопросе не важны.
У нас в основном в этом регистре одна марка может быть в двух записях.
Если производство, то это два регистратора отчет производства за смену типа ставит на баланс и ТТН где уставлен признак выбывшие.
Если мы купили и продали это два регистратора это Акт к ТТН и ТТН где уставлен признак выбывшие.
Нескорые марки могут туда - сюда кататься, будет несколько записей, главная запись последняя, по ней надо смотреть ОтметкаВыбытия.
На текущий момент в регистре 38 миллионов записей.
Задача зачистить выбывшие марки, оставив данные только за последние полгода.
Пока сделал так. За 12 часов в среднем удаляется полмиллиона записей.
Помогите оптимизировать код. Пока только в голову приходит запустить на нескольких компах с разным диапазоном дат.

Процедура Кнопка1Нажатие(Элемент)
//Удаление записей из регистра, подчиненного регистратору

Сообщить("Начало запроса - " + ТекущаяДата());

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ
| алкХранилищеАкцизныхМарокСрезПоследних.Марка
|ПОМЕСТИТЬ Марки
|ИЗ
| РегистрСведений.алкХранилищеАкцизныхМарок.СрезПоследних(, ) КАК алкХранилищеАкцизныхМарокСрезПоследних
|ГДЕ
| алкХранилищеАкцизныхМарокСрезПоследних.ОтметкаВыбытия = ИСТИНА
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| алкХранилищеАкцизныхМарок.Регистратор КАК Регистратор,
| алкХранилищеАкцизныхМарок.Марка КАК Марка
|ИЗ
| РегистрСведений.алкХранилищеАкцизныхМарок КАК алкХранилищеАкцизныхМарок
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Марки КАК Марки
| ПО алкХранилищеАкцизныхМарок.Марка = Марки.Марка
|ГДЕ
| алкХранилищеАкцизныхМарок.Период МЕЖДУ ДАТАВРЕМЯ(2018, 1, 1, 0, 0 , 1) И ДАТАВРЕМЯ(2020, 6, 1, 0, 0, 1)
|ИТОГИ ПО
| Регистратор";

СчетчикДоки = 0;
СчетчикМарки = 0;

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

Пока ВыборкаРегистратор.Следующий() Цикл
НаборЗаписей = РегистрыСведений.алкХранилищеАкцизныхМарок.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ВыборкаРегистратор.Регистратор);
НаборЗаписей.Прочитать();
сзУдаляемыеЗаписи = Новый СписокЗначений;
ВыборкаДетальныеЗаписи = ВыборкаРегистратор.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Для Каждого Запись Из НаборЗаписей Цикл
Если Запись.Марка = ВыборкаДетальныеЗаписи.Марка Тогда
сзУдаляемыеЗаписи.Добавить(Запись);
СчетчикМарки = СчетчикМарки + 1;
КонецЕсли;
КонецЦикла;
КонецЦикла;

Для Каждого ЭлтСз Из сзУдаляемыеЗаписи Цикл
НаборЗаписей.Удалить(ЭлтСз.Значение);
КонецЦикла;

НаборЗаписей.Записать();
СчетчикДоки = СчетчикДоки + 1;
//Если СчетчикДоки = 100 Тогда
// Прервать;
//КонецЕсли;
Если ТекущаяДата() > Дата(2021, 02, 03, 08, 30 , 00) Тогда
Прервать;
КонецЕсли;

Сообщить("Окончание обработки - " + ТекущаяДата());
Сообщить("СчетчикДоки - " + СчетчикДоки);
Сообщить("СчетчикМарки - " + СчетчикМарки);
КонецПроцедуры

Сам запрос выполняется 20 минут.
Вот для примера последний запуск.

Начало запроса - 02.02.2021 23:10:58
Окончание запроса - 02.02.2021 23:38:54
Окончание обработки - 03.02.2021 8:31:04
СчетчикДоки - 167
СчетчикМарки - 373 695

Читайте также: