Параметры схемы СКД: редактор диапазона цен, выражения и безопасная привязка

Учебные задачи

Объявляем собственные числовые границы МинЦена85/МаксЦена85 и полные выражения 50*2/100*7. Редактируем порядок, default и формулу, удаляем и восстанавливаем каталог, сверяем независимую копию. Запрос цены500 начинает использовать обе границы лишь после полного восстановления — без неизвестных имён на промежуточных этапах.

Где и как запускать

Полный серверный общий модуль УчебныеЗадачи новой пустой конфигурации УчебныйДиапазонЦен85; в1С не запускался.

Подготовка

  1. НОВАЯ отдельная учебная база, не копия рабочей: точно8.5.1.1343, конфигурация УчебныйДиапазонЦен85, совместимость Не использовать. Нет бизнес-объектов, расширений, подписок и фоновых заданий; модули вручную пустые, кроме данного общего модуля.

  2. Конфигуратор → Общие → Общие модули → УчебныеЗадачи: Сервер=Да, Вызов сервера=Да, Клиент=Нет, Глобальный=Нет, Привилегированный=Нет. Вставить весь основной модуль. Создать свою внешнюю обработку УчебныйДиапазонЦен85 → управляемая Форма: команда/кнопка Выполнить с действием Выполнить. Отдельный полный модуль формы дан ниже; реквизиты не нужны.

  3. Будущая ручная подготовка вне опыта: завершить прежний свой сеанс, сохранить, Конфигурация → Обновить конфигурацию базы данных (F7), открыть свежий собственный клиент и свою обработку. Здесь F7, BSL, запросы и компоновка не запускались.

  4. Код сам объявляет Local-источник СвоиЦены85 и набор Цены85 с буквальными A01/500, вручную заданными КодТовара Строка/Цена Число. Во время редактирования параметров запрос не содержит ссылок на параметры.

  5. Цена и числовые defaults обеих границ имеют единый явный учебный тип: целые неотрицательные значения 0–999. Цена500, минимум100, максимум700 и временный максимум800 укладываются в три разряда. Квалификатор передаётся вторым аргументом ОписаниеТипов; это ограничение собственного конечного стенда, не правило цен рабочей базы.

Процедура ВыполнитьПример() Экспорт
    ПроверитьСвойПустойСтенд();
    Если ПривилегированныйРежим() Тогда
        ВызватьИсключение "Привилегированный режим не нужен";
    КонецЕсли;
    Пусто = Новый Массив;
    Имена = СтрРазделить("МинЦена85,МаксЦена85", ",");
    Значения = Новый Массив;
    Значения.Добавить(100);
    Значения.Добавить(700);
    Выражения = СтрРазделить("50 * 2|100 * 7", "|");
    ОдинМинимум = Новый Массив;
    ОдинМинимум.Добавить(100);
    ОдинМаксимум = Новый Массив;
    ОдинМаксимум.Добавить(700);
    ОбратныеЗначения = Новый Массив;
    ОбратныеЗначения.Добавить(700);
    ОбратныеЗначения.Добавить(100);
    Схема = СоздатьСвоюОсновуДиапазона();
    Каталог = Схема.Параметры;
    ПроверитьСвоиГраницы(Схема, Пусто, Пусто, Пусто, Ложь);
    Минимум = Каталог.Добавить();
    ЗаполнитьСвоюГраницу(Схема, Минимум, "МинЦена85", 100, "50 * 2");
    ПроверитьСвоиГраницы(Схема, СтрРазделить("МинЦена85", ","), ОдинМинимум, СтрРазделить("50 * 2", "|"), Ложь);
    Максимум = Каталог.Вставить(0);
    ЗаполнитьСвоюГраницу(Схема, Максимум, "МаксЦена85", 700, "100 * 7");
    ПроверитьСвоиГраницы(Схема, СтрРазделить("МаксЦена85,МинЦена85", ","), ОбратныеЗначения, СтрРазделить("100 * 7|50 * 2", "|"), Ложь);
    Каталог.Сдвинуть(Максимум, 1);
    ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, Ложь);
    Максимум.Значение = 800;
    Максимум.Выражение = "100 * 8";
    ДругиеЗначения = Новый Массив;
    ДругиеЗначения.Добавить(100);
    ДругиеЗначения.Добавить(800);
    ПроверитьСвоиГраницы(Схема, Имена, ДругиеЗначения, СтрРазделить("50 * 2|100 * 8", "|"), Ложь);
    Максимум.Значение = 700;
    Максимум.Выражение = "100 * 7";
    ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, Ложь);
    Копия = СоздатьСвоюОсновуДиапазона();
    ВосстановитьСвоиГраницы(Копия);
    Каталог.Удалить(Минимум);
    ПроверитьСвоиГраницы(Схема, СтрРазделить("МаксЦена85", ","), ОдинМаксимум, СтрРазделить("100 * 7", "|"), Ложь);
    ПроверитьСвоиГраницы(Копия, Имена, Значения, Выражения, Ложь);
    НовыйМинимум = Каталог.Вставить(0);
    ЗаполнитьСвоюГраницу(Схема, НовыйМинимум, "МинЦена85", 100, "50 * 2");
    ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, Ложь);
    Каталог.Очистить();
    ПроверитьСвоиГраницы(Схема, Пусто, Пусто, Пусто, Ложь);
    ПроверитьСвоиГраницы(Копия, Имена, Значения, Выражения, Ложь);
    ВосстановитьСвоиГраницы(Схема);
    ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, Ложь);
    Набор = Схема.НаборыДанных.Найти("Цены85");
    Если ТипЗнч(Набор) <> Тип("НаборДанныхЗапросСхемыКомпоновкиДанных") Тогда
        ВызватьИсключение "Нужен собственный набор до привязки диапазона";
    КонецЕсли;
    Набор.Запрос = "ВЫБРАТЬ Данные.КодТовара КАК КодТовара, Данные.Цена КАК Цена ИЗ (ВЫБРАТЬ ""A01"" КАК КодТовара, 500 КАК Цена) КАК Данные ГДЕ Данные.Цена >= &МинЦена85 И Данные.Цена <= &МаксЦена85";
    ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, Истина);
    ПроверитьСвоиГраницы(Копия, Имена, Значения, Выражения, Ложь);
    Сообщить("Числовые параметры 100/700 и их выражения восстановлены; запрос привязан только после этого");
КонецПроцедуры

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры

Функция СвойЧисловойТипЦены()
    Возврат Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(3, 0, ДопустимыйЗнак.Неотрицательный));
КонецФункции

Процедура ПроверитьСвоиКвалификаторыЦены(Описание)
    Если ТипЗнч(Описание) <> Тип("ОписаниеТипов") Тогда
        ВызватьИсключение "Нужно собственное числовое описание учебной цены или границы";
    КонецЕсли;
    Квалификаторы = Описание.КвалификаторыЧисла;
    Если ТипЗнч(Квалификаторы) <> Тип("КвалификаторыЧисла") Или Квалификаторы.Разрядность <> 3
        Или Квалификаторы.РазрядностьДробнойЧасти <> 0
        Или Квалификаторы.ДопустимыйЗнак <> ДопустимыйЗнак.Неотрицательный Тогда
        ВызватьИсключение "Учебная цена и её границы должны иметь целый неотрицательный тип от 0 до 999";
    КонецЕсли;
КонецПроцедуры

Функция СоздатьСвоюОсновуДиапазона()
    Схема = Новый СхемаКомпоновкиДанных;
    Источник = Схема.ИсточникиДанных.Добавить();
    Источник.Имя = "СвоиЦены85";
    Источник.ТипИсточникаДанных = "Local";
    Набор = Схема.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
    Если ТипЗнч(Набор) <> Тип("НаборДанныхЗапросСхемыКомпоновкиДанных") Тогда
        ВызватьИсключение "Нужен собственный набор запроса";
    КонецЕсли;
    Набор.Имя = "Цены85";
    Набор.ИсточникДанных = "СвоиЦены85";
    Набор.АвтоЗаполнениеДоступныхПолей = Ложь;
    Набор.Запрос = "ВЫБРАТЬ ""A01"" КАК КодТовара, 500 КАК Цена";
    Имена = СтрРазделить("КодТовара,Цена", ",");
    Типы = СтрРазделить("Строка,Число", ",");
    Для Индекс = 0 По 1 Цикл
        Поле = Набор.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
        Если ТипЗнч(Поле) <> Тип("ПолеНабораДанныхСхемыКомпоновкиДанных") Тогда
            ВызватьИсключение "Нужно собственное простое описание поля";
        КонецЕсли;
        Поле.Поле = Имена[Индекс];
        Поле.ПутьКДанным = Имена[Индекс];
        Если Типы[Индекс] = "Число" Тогда
            Поле.ТипЗначения = СвойЧисловойТипЦены();
        Иначе
            Поле.ТипЗначения = Новый ОписаниеТипов("Строка");
        КонецЕсли;
    КонецЦикла;
    ПроверитьСвоюОсновуДиапазона(Схема, Ложь);
    Возврат Схема;
КонецФункции

Процедура ПроверитьСвоюОсновуДиапазона(Схема, ЗапросПривязан)
    Если ТипЗнч(Схема) <> Тип("СхемаКомпоновкиДанных")
        Или Схема.ИсточникиДанных.Количество() <> 1 Или Схема.НаборыДанных.Количество() <> 1 Тогда
        ВызватьИсключение "Нужны прежние свои источник и набор";
    КонецЕсли;
    Источник = Схема.ИсточникиДанных.Получить(0);
    Если ТипЗнч(Источник) <> Тип("ИсточникДанныхСхемыКомпоновкиДанных")
        Или Источник.Имя <> "СвоиЦены85" Или Источник.ТипИсточникаДанных <> "Local" Тогда
        ВызватьИсключение "Не совпал собственный источник";
    КонецЕсли;
    Набор = Схема.НаборыДанных.Получить(0);
    Если ТипЗнч(Набор) <> Тип("НаборДанныхЗапросСхемыКомпоновкиДанных") Тогда
        ВызватьИсключение "Нужен собственный набор запроса";
    КонецЕсли;
    ОжидаемыйЗапрос = "ВЫБРАТЬ ""A01"" КАК КодТовара, 500 КАК Цена";
    Если ЗапросПривязан Тогда
        ОжидаемыйЗапрос = "ВЫБРАТЬ Данные.КодТовара КАК КодТовара, Данные.Цена КАК Цена ИЗ (ВЫБРАТЬ ""A01"" КАК КодТовара, 500 КАК Цена) КАК Данные ГДЕ Данные.Цена >= &МинЦена85 И Данные.Цена <= &МаксЦена85";
    КонецЕсли;
    Если Набор.Имя <> "Цены85" Или Набор.ИсточникДанных <> "СвоиЦены85" Или Набор.АвтоЗаполнениеДоступныхПолей
        Или Набор.Запрос <> ОжидаемыйЗапрос Или Набор.Поля.Количество() <> 2
        Или Схема.НаборыДанных.Найти("Цены85") <> Набор Тогда
        ВызватьИсключение "Не совпали весь запрос, поля, имя и поиск собственного набора";
    КонецЕсли;
    Имена = СтрРазделить("КодТовара,Цена", ",");
    Типы = СтрРазделить("Строка,Число", ",");
    Для Индекс = 0 По 1 Цикл
        Поле = Набор.Поля.Получить(Индекс);
        Если ТипЗнч(Поле) <> Тип("ПолеНабораДанныхСхемыКомпоновкиДанных")
            Или Поле.Поле <> Имена[Индекс] Или Поле.ПутьКДанным <> Имена[Индекс]
            Или ТипЗнч(Поле.ТипЗначения) <> Тип("ОписаниеТипов") Тогда
            ВызватьИсключение "Не совпали полные собственные пути, порядок и типы полей";
        КонецЕсли;
        ПолныеТипы = Поле.ТипЗначения.Типы();
        Если ПолныеТипы.Количество() <> 1 Или ПолныеТипы[0] <> Тип(Типы[Индекс]) Тогда
            ВызватьИсключение "Не совпал полный набор типов собственного поля";
        КонецЕсли;
        Если Типы[Индекс] = "Число" Тогда
            ПроверитьСвоиКвалификаторыЦены(Поле.ТипЗначения);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Процедура ЗаполнитьСвоюГраницу(Схема, Параметр, Имя, Значение, Выражение)
    ПроверитьСвоюОсновуДиапазона(Схема, Ложь);
    Если ТипЗнч(Параметр) <> Тип("ПараметрСхемыКомпоновкиДанных") Или Схема.Параметры.Индекс(Параметр) < 0 Тогда
        ВызватьИсключение "Нужен текущий собственный параметр именно этой схемы";
    КонецЕсли;
    Если Имя <> "МинЦена85" И Имя <> "МаксЦена85" Тогда
        ВызватьИсключение "В опыте объявляются только собственные границы цен";
    КонецЕсли;
    Если ТипЗнч(Значение) <> Тип("Число") Или ТипЗнч(Выражение) <> Тип("Строка") Тогда
        ВызватьИсключение "Значение числовое, а полное выражение хранится строкой";
    КонецЕсли;
    Параметр.Имя = Имя;
    Параметр.ТипЗначения = СвойЧисловойТипЦены();
    Параметр.Значение = Значение;
    Параметр.Выражение = Выражение;
    Параметр.Использование = ИспользованиеПараметраКомпоновкиДанных.Авто;
КонецПроцедуры

Процедура ПроверитьСвоиГраницы(Схема, Имена, Значения, Выражения, ЗапросПривязан)
    ПроверитьСвоюОсновуДиапазона(Схема, ЗапросПривязан);
    Каталог = Схема.Параметры;
    Если ТипЗнч(Каталог) <> Тип("ПараметрыСхемыКомпоновкиДанных")
        Или Каталог.Количество() <> Имена.Количество() Или Значения.Количество() <> Имена.Количество()
        Или Выражения.Количество() <> Имена.Количество() Тогда
        ВызватьИсключение "Не совпал полный собственный состав границ";
    КонецЕсли;
    Для Индекс = 0 По Имена.Количество() - 1 Цикл
        Параметр = Каталог.Получить(Индекс);
        Если ТипЗнч(Параметр) <> Тип("ПараметрСхемыКомпоновкиДанных")
            Или Параметр.Имя <> Имена[Индекс] Или ТипЗнч(Параметр.Значение) <> Тип("Число")
            Или Параметр.Значение <> Значения[Индекс] Или ТипЗнч(Параметр.Выражение) <> Тип("Строка")
            Или Параметр.Выражение <> Выражения[Индекс]
            Или Параметр.Использование <> ИспользованиеПараметраКомпоновкиДанных.Авто
            Или Каталог.Индекс(Параметр) <> Индекс Или Каталог[Индекс] <> Параметр
            Или Каталог[Имена[Индекс]] <> Параметр Или Каталог.Найти(Имена[Индекс]) <> Параметр
            Или ТипЗнч(Параметр.ТипЗначения) <> Тип("ОписаниеТипов") Тогда
            ВызватьИсключение "Не совпали имена, числовые defaults, полные выражения, порядок и поиск границ";
        КонецЕсли;
        ПолныеТипы = Параметр.ТипЗначения.Типы();
        Если ПолныеТипы.Количество() <> 1 Или ПолныеТипы[0] <> Тип("Число") Тогда
            ВызватьИсключение "Границы имеют только собственный числовой тип";
        КонецЕсли;
        ПроверитьСвоиКвалификаторыЦены(Параметр.ТипЗначения);
    КонецЦикла;
    Если Каталог.Найти("НетТакойГраницы85") <> Неопределено Тогда
        ВызватьИсключение "Отсутствующая собственная граница не должна находиться";
    КонецЕсли;
    Если ЗапросПривязан Тогда
        Если Имена.Количество() <> 2 Или Имена[0] <> "МинЦена85" Или Имена[1] <> "МаксЦена85"
            Или Значения[0] > Значения[1] Тогда
            ВызватьИсключение "Привязанный запрос требует обеих восстановленных согласованных границ";
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Процедура ВосстановитьСвоиГраницы(Схема)
    Пусто = Новый Массив;
    ПроверитьСвоиГраницы(Схема, Пусто, Пусто, Пусто, Ложь);
    Минимум = Схема.Параметры.Добавить();
    ЗаполнитьСвоюГраницу(Схема, Минимум, "МинЦена85", 100, "50 * 2");
    Максимум = Схема.Параметры.Добавить();
    ЗаполнитьСвоюГраницу(Схема, Максимум, "МаксЦена85", 700, "100 * 7");
КонецПроцедуры
УчебныйДиапазонЦен85.Форма

Своя внешняя обработка УчебныйДиапазонЦен85 → управляемая Форма → Модуль

Полный отдельный модуль своей формы с серверным вызовом; здесь не запускался.

&НаКлиенте
Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
    Сообщить("Числовые границы, полные выражения, порядок и безопасная привязка запроса проверены");
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере()
    УчебныеЗадачи.ВыполнитьПример();
КонецПроцедуры

Разбор по шагам

  1. Значение параметра — числовой default, а Выражение — строка, по которой вычисляется значение. СвойЧисловойТипЦены() задаёт Число с явными квалификаторами, а Использование.Авто задаётся отдельно. Собственные арифметические формулы 50 * 2 и 100 * 7 осмысленно соответствуют границам100/700, но движок здесь их не вычисляет.

  2. Добавить минимум в конец; Вставить(0) добавить максимум перед ним. Полностью проверить обратный порядок, имена, defaults, выражения, типы и поиск. Сдвинуть(Максимум,1) принимает объект и относительное смещение, возвращая минимум/максимум.

  3. Для максимума отдельно изменить default700→800 и полную формулу100 * 7→100 * 8; проверить оба значения и затем вернуть исходные. Сохранение default само по себе не доказывает правильность выражения.

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

  5. Заново построить независимую схему и обе её границы. Удалить минимум как объект, не индекс; старую ссылку не использовать. Вставить новый минимум, очистить собственный каталог при сохранённом запросе БЕЗ параметров, подтвердить0 и сохранённость копии, затем заново добавить обе границы.

  6. Только после восстановления минимум100/максимум700 сменить запрос на настоящий внешний ВЫБРАТЬ ИЗ (своя буквальная строка) с ГДЕ Цена >= &МинЦена85 И Цена <= &МаксЦена85. Финальный контроль требует существующих двух согласованных границ. Копия остаётся с исходным запросом без привязки.

  7. В УНФ настройка печатных форм действительно получает параметр через Найти, проверяет Неопределено, затем вызывает Удалить(ПараметрСхемы) и Сдвинуть(ПараметрСхемы,Смещение). Рабочая форма здесь не вызывается; используются только собственные определения.

  8. ПроверитьСвоиКвалификаторыЦены сверяет Разрядность=3, РазрядностьДробнойЧасти=0 и ДопустимыйЗнак=Неотрицательный отдельно у поля Цена и у каждого числового параметра во всех прежних состояниях. КодТовара остаётся Строка. Исходные запросы, значения, выражения, безопасная поздняя привязка и независимая копия не меняются.

Как проверить результат

  1. Будущий итог модели: каталог МинЦена85/МаксЦена85, тип каждого только Число, defaults100/700, полные строки50 * 2/100 * 7 и Авто. В оригинале запрос имеет обе параметрические границы; в копии тот же полный каталог и прежний непараметрический запрос.

  2. По арифметике50*2=100,100*7=700, а число500 находится между этими значениями. Здесь выражения не исполнялись, запрос не запускался, строка результата и значения настроек не получены: literals/defaults не выдаются за вычисленный результат.

  3. Будущий отрицательный контроль: после первоначального заполнения максимума изменить только Максимум.Выражение = "100 * 8", оставив default700 и эталон100 * 7. Полная проверка должна отказать при прежних размерах и именах. Вернуть код, сохранить/F7 вне опыта и открыть новый собственный сеанс; здесь контроль не выполнялся.

  4. runtimeVerifiedScenarios=0. Только статическая сверка контрактов и итогового HTML. Нет исполнения 1С, рабочих данных, записи, сети, компоновщика или печати.

Ожидаемый результат

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

На что обратить внимание

  1. Выражение хранит строку формулы, Значение — default. Их математическое соответствие объяснено, но фактическая оценка выражений и приоритет настроек компоновщика требуют отдельной runtime-проверки.

  2. Вставить — абсолютный индекс, Сдвинуть — объект/относительное смещение, Удалить — объект. После удаления и очистки старые ссылки не используются; новые параметры создаются заново.

  3. Во время неполного каталога запрос намеренно не ссылается на границы. Нельзя очистить объявления под запросом с &МинЦена85/&МаксЦена85 и назвать это готовой схемой. Финальная привязка выполняется только после полного восстановления.

  4. Авто не обещает конкретное исполненное значение настройки. Пользовательские настройки, визуальный отчёт и реальная строка отбора этим примером не проверены.

  5. ПараметрФункциональныхОпций — имя действительного параметра опции, отдельная задача с метаданными. Здесь оно не заполняется пустой или произвольной строкой ради покрытия.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.