Блокировка данных 1С нужна, когда код в одной транзакции читает данные, решает по ним и записывает результат: проверяет остаток и списывает товар, берёт последний номер и увеличивает счётчик, выбирает свободный заказ и помечает его взятым. Без блокировки между чтением и записью вклинивается другой сеанс: остаток уходит в минус, номер повторяется, заказ достаётся двоим. Ниже — устройство объекта БлокировкаДанных, выбор режима и шаблоны для проведения, обработки и счётчика.
Задача встречается в проведении с контролем остатков, обработках распределения, нумерации, очередях обмена с сайтом и маркетплейсами. Всё это — про управляемый режим: в свойствах корня конфигурации («Конфигурация» → «Открыть конфигурацию», правый щелчок по корню → «Свойства») «Режим управления блокировкой данных» равен «Управляемый», как в УТ 11.4 и КА 2.5; программно — Метаданные.РежимУправленияБлокировкойДанных. В автоматическом режиме целостность обеспечивает СУБД.
Не путайте понятия. Управляемая блокировка живёт внутри транзакции и защищает данные от других транзакций. Объектная (Заблокировать() у объекта, ЗаблокироватьДанныеДляРедактирования()) не даёт двоим редактировать один объект и транзакции не требует. Запрет входа на время обновления — это блокировка базы, а причины ожиданий и ошибок разобраны в статье о конфликте блокировок.
Объект — коллекция элементов. Элемент создаёт метод Добавить("Пространство"), дальше задаётся, что блокировать:
Область — имя пространства, например "РегистрНакопления.ОстаткиТоваров";Режим — РежимБлокировкиДанных.Исключительный (по умолчанию) или Разделяемый;УстановитьЗначение(Поле, Значение) — условие на поле; вместо значения можно передать Новый Диапазон(Начало, Конец) с датами или числами, Неопределено открывает границу;ИсточникДанных + ИспользоватьИзИсточникаДанных(ПолеПространства, ПолеИсточника) — значения из таблицы значений, табличной части или результата запроса, строка источника = комбинация. Одно поле нельзя задать и значением, и из источника.Заблокировать() ставит все элементы, и держатся они до конца транзакции. Если область занята несовместимой чужой блокировкой, сеанс ждёт, а не дождавшись, получает «Конфликт блокировок при выполнении транзакции».
| Пространство | Поля для условий |
|---|---|
Справочник.Имя, Документ.Имя и другие ссылочные объекты | Ссылка; с 8.3.3 ещё реквизиты из свойства «Поля блокировки данных» |
РегистрСведений.Имя | Период (у периодического), измерения |
РегистрСведений.Имя.НаборЗаписей | Регистратор — только у подчинённого регистратору |
РегистрНакопления.Имя | Период, измерения |
РегистрНакопления.Имя.НаборЗаписей | Регистратор |
РегистрБухгалтерии.Имя | Период, Счет, Субконто1…, измерения |
Константа.Имя | полей нет |
Поле без условия означает «любое значение», элемент без условий блокирует всё пространство. Для служебного регистра это допустимо (так БСП в ОбщегоНазначения.УдалитьЗаписиКэшаВерсий блокирует КэшПрограммныхИнтерфейсов), для регистра остатков — нет: пользователи выстроятся в очередь.
Блокировки конфликтуют, если их области пересекаются, а режимы несовместимы: «Склад = Основной» и «Номенклатура = Кабель» пересекаются на кабеле основного склада. Управляемые блокировки в 1С 8.3 ставятся на значения полей, а не на строки, поэтому заблокировать можно и комбинацию, которой в регистре ещё нет.
Два помощника для общего серверного модуля:
// Общий модуль БлокировкиДанныхСервер (флаг «Сервер»).
// Блокирует все комбинации значений из таблицы одним элементом.
// Параметры:
// Пространство - Строка - например, "РегистрНакопления.ОстаткиТоваров".
// Таблица - ТаблицаЗначений - только ключевые колонки, их имена совпадают
// с полями пространства (измерения, Период, Ссылка).
// Разделяемая - Булево - Истина, если данные только читаются.
// Пример: ЗаблокироватьПоТаблице("РегистрНакопления.ОстаткиТоваров",
// Товары.Выгрузить(, "Склад, Номенклатура"));
Процедура ЗаблокироватьПоТаблице(Пространство, Таблица, Разделяемая = Ложь) Экспорт
ПроверитьТранзакцию();
Если Таблица.Количество() = 0 Тогда
Возврат; // блокировать нечего
КонецЕсли;
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить(Пространство);
Элемент.Режим = ?(Разделяемая, РежимБлокировкиДанных.Разделяемый,
РежимБлокировкиДанных.Исключительный);
Элемент.ИсточникДанных = Таблица;
Для Каждого Колонка Из Таблица.Колонки Цикл
Элемент.ИспользоватьИзИсточникаДанных(Колонка.Имя, Колонка.Имя);
КонецЦикла;
Блокировка.Заблокировать();
КонецПроцедуры
// Блокирует записи периодического регистра за интервал дат по одному
// значению измерения: например, курсы одной валюты за месяц перед перезаписью.
Процедура ЗаблокироватьЗаПериод(Пространство, Измерение, Значение, Начало, Конец) Экспорт
ПроверитьТранзакцию();
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить(Пространство); // режим по умолчанию - исключительный
Элемент.УстановитьЗначение(Измерение, Значение);
// Границы диапазона - Дата или Число; Неопределено - граница открыта.
Элемент.УстановитьЗначение("Период", Новый Диапазон(Начало, Конец));
Блокировка.Заблокировать();
КонецПроцедуры
// Без транзакции блокировка ничего не защищает - сообщаем об ошибке сразу.
Процедура ПроверитьТранзакцию()
Если Не ТранзакцияАктивна() Тогда
ВызватьИсключение НСтр("ru = 'Блокировку данных ставьте внутри транзакции'");
КонецЕсли;
КонецПроцедуры
| У другого сеанса уже стоит | Просим разделяемую | Просим исключительную |
|---|---|---|
| Разделяемая | ставится сразу | ждём |
| Исключительная | ждём | ждём |
Исключительная блокировка 1С — для данных, которые вы меняете или по которым решаете, что записать: остатки, счётчики, очередь. Вторая транзакция с такой же блокировкой дождётся фиксации первой и прочитает уже новые значения.
Разделяемая блокировка 1С — для данных, которые вы только читаете, но они не должны измениться до конца транзакции: лимиты, настройки, цены. Читать их так могут сколько угодно сеансов параллельно, а писатель подождёт. Если бы каждое проведение брало исключительную блокировку на общую запись настроек, все документы встали бы в очередь к одной строке.
Не повышайте режим внутри транзакции: прочитали под разделяемой и пишете те же данные (при записи платформа ставит исключительную). Если так сделают два сеанса сразу, каждый ждёт другого — взаимоблокировка. Собираетесь писать — сразу берите исключительную. Оба режима в одной блокировке:
// Модуль объекта документа «ОтгрузкаКлиенту» - вызывается из ОбработкаПроведения.
// Регистр сведений ЛимитыКлиентов: измерение Клиент, ресурс Лимит.
// Регистр накопления ДолгиКлиентов: измерение Клиент, ресурс Сумма.
// Лимит документ только читает, долг - проверяет и увеличивает.
Процедура ЗаблокироватьЛимитИДолг(Клиент)
Блокировка = Новый БлокировкаДанных;
// Разделяемая: читать лимит с такой же блокировкой могут и другие,
// а изменить его никто не сможет, пока наша транзакция не закончится.
ЭлементЛимит = Блокировка.Добавить("РегистрСведений.ЛимитыКлиентов");
ЭлементЛимит.Режим = РежимБлокировкиДанных.Разделяемый;
ЭлементЛимит.УстановитьЗначение("Клиент", Клиент);
// Исключительная: долг клиента меняем, вторая отгрузка
// тому же клиенту подождёт нашей фиксации.
ЭлементДолг = Блокировка.Добавить("РегистрНакопления.ДолгиКлиентов");
ЭлементДолг.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементДолг.УстановитьЗначение("Клиент", Клиент);
Блокировка.Заблокировать(); // оба элемента - одним вызовом, до любого чтения
КонецПроцедуры
Расходный документ не должен увести остаток в минус, даже если десять человек проводят документы одновременно. Способов два: блокировка по измерениям до чтения остатков или запись с БлокироватьДляИзменения и проверка после записи. Как формируются движения документа и устроены остатки в запросе — в отдельных статьях.
Склад указан в строке табличной части, поэтому оба поля блокировки берутся из результата запроса.
// Модуль объекта документа «РасходнаяНакладная».
// Табличная часть Товары: Номенклатура, Склад, Количество (склад - в строке).
// Регистр накопления ОстаткиТоваров (остатки): измерения Склад и Номенклатура,
// ресурс Количество. Режим блокировок конфигурации - управляемый.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Транзакцию открыла платформа, документ в ней уже записан.
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.Текст =
"ВЫБРАТЬ
| Т.Склад КАК Склад,
| Т.Номенклатура КАК Номенклатура,
| СУММА(Т.Количество) КАК Количество
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
| Документ.РасходнаяНакладная.Товары КАК Т
|ГДЕ
| Т.Ссылка = &Ссылка
|
|СГРУППИРОВАТЬ ПО
| Т.Склад,
| Т.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Товары.Склад КАК Склад,
| ВТ_Товары.Номенклатура КАК Номенклатура
|ИЗ
| ВТ_Товары КАК ВТ_Товары";
Пакет = Запрос.ВыполнитьПакет();
// 1. Блокировка - до чтения остатков и только на пары «склад + товар»
// из документа: каждая строка источника - одна комбинация.
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрНакопления.ОстаткиТоваров");
Элемент.Режим = РежимБлокировкиДанных.Исключительный;
Элемент.ИсточникДанных = Пакет[1];
Элемент.ИспользоватьИзИсточникаДанных("Склад", "Склад");
Элемент.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");
Блокировка.Заблокировать();
// 2. Перепроведение: убираем старые движения, чтобы они не попали в остаток.
Движения.ОстаткиТоваров.Записывать = Истина;
Движения.ОстаткиТоваров.Записать();
// 3. Остатки читаем уже под блокировкой.
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Товары.Склад КАК Склад,
| ВТ_Товары.Номенклатура КАК Номенклатура,
| ВТ_Товары.Количество КАК Нужно,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Есть
|ИЗ
| ВТ_Товары КАК ВТ_Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки(
| ,
| (Склад, Номенклатура) В
| (ВЫБРАТЬ
| ВТ.Склад,
| ВТ.Номенклатура
| ИЗ
| ВТ_Товары КАК ВТ)) КАК Остатки
| ПО ВТ_Товары.Склад = Остатки.Склад
| И ВТ_Товары.Номенклатура = Остатки.Номенклатура
|ГДЕ
| ВТ_Товары.Количество > ЕСТЬNULL(Остатки.КоличествоОстаток, 0)";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Склад %1, %2: нужно %3, в наличии %4",
Выборка.Склад, Выборка.Номенклатура, Выборка.Нужно, Выборка.Есть);
Сообщение.Сообщить();
Отказ = Истина;
КонецЦикла;
Если Отказ Тогда
Возврат; // транзакция откатится, блокировка снимется вместе с ней
КонецЕсли;
// 4. Движения. Набор с флагом Записывать платформа запишет после обработчика.
Для Каждого СтрокаТовара Из Товары Цикл
Движение = Движения.ОстаткиТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Склад = СтрокаТовара.Склад;
Движение.Номенклатура = СтрокаТовара.Номенклатура;
Движение.Количество = СтрокаТовара.Количество;
КонецЦикла;
КонецПроцедуры
Склад и Номенклатура из источника — заблокированы только пары документа, проведение по другим товарам не ждёт.ИсточникДанных = Пакет[1] — все комбинации одним элементом. Отдельный Заблокировать() на каждую строку медленнее и чаще ведёт к взаимоблокировкам.Заблокировать(), потом запрос к остаткам: прочитанное до блокировки могло устареть, пока сеанс ждал.Контролируется текущий остаток; для остатка на дату документа передайте в первый параметр Остатки() границу его момента. Запрос удобно отладить в бесплатной консоли запросов.
Свойство БлокироватьДляИзменения есть у наборов записей регистров накопления и бухгалтерии. Со значением Истина платформа при записи набора сама блокирует все комбинации измерений из его записей — остаётся записать движения и проверить, не стал ли остаток отрицательным. Так сделано в УТ 11.4: в модуле набора записей регистра ТоварыНаСкладах обработчик ПередЗаписью ставит БлокироватьДляИзменения = Истина.
// Тот же документ, второй способ: сначала записываем движения,
// потом проверяем, не ушёл ли остаток в минус.
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
Движения.ОстаткиТоваров.Записывать = Истина;
Для Каждого СтрокаТовара Из Товары Цикл
Движение = Движения.ОстаткиТоваров.ДобавитьРасход();
Движение.Период = Дата;
Движение.Склад = СтрокаТовара.Склад;
Движение.Номенклатура = СтрокаТовара.Номенклатура;
Движение.Количество = СтрокаТовара.Количество;
КонецЦикла;
// При записи платформа сама заблокирует все комбинации измерений
// из набора - явная БлокировкаДанных не нужна.
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина;
// Пишем все наборы сейчас. Флаг Записывать снимается,
// повторной записи после обработчика не будет.
Движения.Записать();
// Остаток читаем после записи: в нём уже учтён этот документ,
// а старые движения при перепроведении замещены новыми.
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Регистратор", Ссылка);
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Склад КАК Склад,
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки(
| ,
| (Склад, Номенклатура) В
| (ВЫБРАТЬ
| Д.Склад,
| Д.Номенклатура
| ИЗ
| РегистрНакопления.ОстаткиТоваров КАК Д
| ГДЕ
| Д.Регистратор = &Регистратор)) КАК Остатки
|ГДЕ
| Остатки.КоличествоОстаток < 0";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = СтрШаблон("Склад %1, %2: не хватает %3",
Выборка.Склад, Выборка.Номенклатура, -Выборка.Остаток);
Сообщение.Сообщить();
Отказ = Истина; // откат вернёт и уже записанные движения
КонецЦикла;
КонецПроцедуры
Плюсы: нет отдельного чтения перед записью и проблемы перепроведения — старые движения замещаются до проверки. Учтите: Движения.Записать() пишет все наборы с флагом Записывать, вызывайте его, когда сформированы все движения; в автоматическом режиме блокировок БлокироватьДляИзменения вызывает исключение.
Обработка-очередь: несколько сборщиков нажимают «Взять заказы», и каждый должен получить свои, без пересечений. Тот же приём подходит для фоновых заданий, которые параллельно разбирают общую очередь выгрузки. Код — в модуле формы: на клиенте только вызов и сообщение, работа с данными — в серверной функции без контекста.
// Модуль формы обработки «СборкаЗаказов».
// Регистр сведений ОчередьСборки (независимый, непериодический):
// измерение Заказ (ДокументСсылка.ЗаказКлиента),
// ресурсы ВРаботе (Булево) и Сборщик (Строка, 100).
&НаКлиенте
Процедура ВзятьЗаказы(Команда)
Взятые = ВзятьСвободныеЗаказы();
Если Взятые.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Свободных заказов нет, попробуйте через минуту.");
Иначе
ПоказатьОповещениеПользователя("Взято заказов: " + Взятые.Количество());
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ВзятьСвободныеЗаказы()
Взятые = Новый Массив;
// 1. Кандидаты - обычным запросом, без транзакции и без блокировки.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 10
| Очередь.Заказ КАК Заказ
|ИЗ
| РегистрСведений.ОчередьСборки КАК Очередь
|ГДЕ
| НЕ Очередь.ВРаботе";
Кандидаты = Запрос.Выполнить().Выгрузить();
Если Кандидаты.Количество() = 0 Тогда
Возврат Взятые;
КонецЕсли;
// Параметр важен только в режиме «Автоматический и управляемый».
НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
Попытка
// 2. Исключительная блокировка только на записи-кандидаты.
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.ОчередьСборки");
Элемент.ИсточникДанных = Кандидаты;
Элемент.ИспользоватьИзИсточникаДанных("Заказ", "Заказ");
Блокировка.Заблокировать();
// 3. Перечитываем под блокировкой: пока ждали, часть заказов
// могли забрать. Решаем только по свежим данным.
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 5
| Очередь.Заказ КАК Заказ
|ИЗ
| РегистрСведений.ОчередьСборки КАК Очередь
|ГДЕ
| Очередь.Заказ В (&Кандидаты)
| И НЕ Очередь.ВРаботе";
Запрос.УстановитьПараметр("Кандидаты", Кандидаты.ВыгрузитьКолонку("Заказ"));
Выборка = Запрос.Выполнить().Выбрать();
// 4. Изменяем и записываем.
Пока Выборка.Следующий() Цикл
Запись = РегистрыСведений.ОчередьСборки.СоздатьМенеджерЗаписи();
Запись.Заказ = Выборка.Заказ;
Запись.ВРаботе = Истина;
Запись.Сборщик = ИмяПользователя();
Запись.Записать(); // замещает запись с тем же заказом
Взятые.Добавить(Выборка.Заказ);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Возврат Взятые;
КонецФункции
Ключевой шаг — третий. Кандидатов выбрали без блокировки, и пока сеанс ждал Заблокировать(), сосед мог забрать часть заказов. Решение принимается только по данным, прочитанным после блокировки, иначе второй сборщик перепишет чужой заказ на себя. Транзакция короткая: сама сборка, печать и любые долгие действия идут после фиксации.
Собственную нумерацию — заявок с сайта, пропусков, партий — удобно хранить в регистре сведений: ключ счётчика в измерениях, последний номер в ресурсе. Функция выдаёт пачку номеров одной короткой транзакцией; общие приёмы — в статье о записи в регистр сведений.
// Общий модуль (флаг «Сервер»). Регистр сведений Нумераторы, непериодический:
// измерения ВидНумерации (Строка, 50) и Год (Число, 4), ресурс Последний (Число, 10).
// Возвращает первый и последний номер выделенной пачки.
Функция ВыделитьНомера(ВидНумерации, Год, Количество = 1) Экспорт
// Внутри чужой транзакции блокировка счётчика продержится до её конца.
// Если пропуски номеров допустимы, вызывайте до начала основной транзакции.
НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
Попытка
// Блокируем ключ счётчика. Записи может ещё не быть - не важно:
// управляемая блокировка ставится на значения полей, а не на строки.
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.Нумераторы");
Элемент.УстановитьЗначение("ВидНумерации", ВидНумерации);
Элемент.УстановитьЗначение("Год", Год);
Блокировка.Заблокировать();
Набор = РегистрыСведений.Нумераторы.СоздатьНаборЗаписей();
Набор.Отбор.ВидНумерации.Установить(ВидНумерации);
Набор.Отбор.Год.Установить(Год);
Набор.Прочитать();
Если Набор.Количество() = 0 Тогда
Запись = Набор.Добавить();
Запись.ВидНумерации = ВидНумерации;
Запись.Год = Год;
Иначе
Запись = Набор[0];
КонецЕсли;
Первый = Запись.Последний + 1;
Запись.Последний = Запись.Последний + Количество;
Набор.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Возврат Новый Структура("Первый, Последний", Первый, Первый + Количество - 1);
КонецФункции
Вызов ВыделитьНомера("ЗаявкаССайта", 2026, 50) выдаёт 50 номеров за одно обращение к счётчику. Блокировка стоит на ключе «вид нумерации + год», разные счётчики друг другу не мешают, а пока записи нет, два сеанса всё равно не создадут две «первые». Выбор за вами: внутри транзакции записи документа номер при откате вернётся (нумерация без пропусков), но остальные будут ждать конца проведения. Если пропуски допустимы, вызывайте функцию до основной транзакции.
БлокировкаДанных, Заблокировать() — до первого чтения и сразу в нужном режиме.НачатьТранзакцию() или после фиксации; подробнее — в статье о транзакциях.Период через Диапазон пересекается со всеми блокировками регистра без условия на период: их область покрывает все даты.РежимУправленияБлокировкойДанных.Управляемый, как в шаблонах выше: без него она будет автоматической.Заблокировать() — проверка идёт по устаревшим данным.БлокировкаДанных.БлокировкаДанных доступна на сервере, в толстом клиенте и внешнем соединении; в процедуре &НаКлиенте управляемой формы — ошибка «Тип не определен (БлокировкаДанных)». О контекстах — в статье про директивы компиляции.НачатьТранзакцию() — данные не защищены. В помощнике выше это ловит ТранзакцияАктивна().Никак: метода «Разблокировать» нет. Блокировки снимаются при фиксации или отмене самой внешней транзакции, вложенная пара НачатьТранзакцию()/ЗафиксироватьТранзакцию() их не снимает. Всё, что можно сделать вне транзакции, делайте вне её.
Менеджер управляемых блокировок проверяет только тех, кто сам блокирует те же данные, поэтому обычный запрос отчёта из-за неё в очередь не встаёт. Зато он может прочитать данные, которые через секунду изменятся: для отчёта это нормально, для решения «можно ли списать» — нет.
Нет, записываемые данные платформа блокирует сама. Явная блокировка нужна, когда записываемое зависит от прочитанного: остатков, счётчика, статуса в очереди.
Пространство — полное имя объекта, поле — Ссылка: Блокировка.Добавить("Документ.РасходнаяНакладная").УстановитьЗначение("Ссылка", ДокументСсылка), затем Блокировка.Заблокировать(). Для списка ссылок — источник данных с колонкой Ссылка. С 8.3.3 полями блокировки могут быть и реквизиты из свойства «Поля блокировки данных».
Если в часы пик проведение «висит», пользователи ловят «Конфликт блокировок», а переписывать проведение типовой страшно, нужен разбор: найти лишние и слишком широкие блокировки и сузить их, не потеряв контроль остатков. Это задача из раздела «1С тормозит» — опишите симптомы и разместите задачу: оценка бесплатная, ответ в течение рабочего дня.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.