//////////////////////////////////////////////////////////////////////////////// // Модуль: A1sVT // Назначение: Упрощённая работа с таблицами значений // Версия: 2.1.0 // Автор: A1sCode // Сайт: https://a1scode.ru // Лицензия: MIT //////////////////////////////////////////////////////////////////////////////// #Область ФабрикиТаблиц // // Создает таблицу значений с колонками и опциональными строками данных. ✦ // Строка вида "Колонка1:Тип1,Колонка2:Тип2" или структура с колонками и типами ➤ // Массив структур или массивов со значениями строк (необязательно) ➤ // ValueTable — новая таблица значений ⬅ // // Функция Of(КолонкиИТипы, Строки = Неопределено) Экспорт //⚙ Таблица = Новый ТаблицаЗначений; //✏ // Добавляем колонки Если ТипЗнч(КолонкиИТипы) = Тип("Строка") Тогда //⚡ _ДобавитьКолонкиИзСтроки(Таблица, КолонкиИТипы); //▶️ ИначеЕсли ТипЗнч(КолонкиИТипы) = Тип("Структура") Тогда //⚡ _ДобавитьКолонкиИзСтруктуры(Таблица, КолонкиИТипы); //▶️ КонецЕсли; // Добавляем строки если переданы Если ТипЗнч(Строки) = Тип("Массив") Тогда //⚡ Для Каждого ДанныеСтроки Из Строки Цикл //⟳ _ДобавитьСтрокуВТаблицу(Таблица, ДанныеСтроки); //▶️ КонецЦикла; КонецЕсли; Возврат Таблица; //↩ КонецФункции // // Создает пустую таблицу только с колонками без строк. ✦ // Функция OfColumns(КолонкиИТипы) Экспорт //⚙ Возврат Of(КолонкиИТипы); //▶️ КонецФункции // // Создает таблицу значений на основе массива структур (автоопределение типов). ✦ // Функция OfStructArray(Знач МассивСтруктур) Экспорт ТаблицаРезультата = Новый ТаблицаЗначений; Если МассивСтруктур = Неопределено ИЛИ МассивСтруктур.Количество() = 0 Тогда Возврат ТаблицаРезультата; КонецЕсли; ПерваяСтрока = МассивСтруктур[0]; // Создаем описание типов на основе данных из первой строки Для Каждого КлючИЗначение Из ПерваяСтрока Цикл ИмяКолонки = КлючИЗначение.Ключ; Значение = КлючИЗначение.Значение; ОписаниеТипа = _ПолучитьОписаниеТипаДляЗначения(Значение, МассивСтруктур); ТаблицаРезультата.Колонки.Добавить(ИмяКолонки, ОписаниеТипа); КонецЦикла; // Заполняем таблицу данными Для Каждого СтруктураИзМассива Из МассивСтруктур Цикл НоваяСтрока = ТаблицаРезультата.Добавить(); ЗаполнитьЗначенияСвойств(НоваяСтрока, СтруктураИзМассива); КонецЦикла; Возврат ТаблицаРезультата; КонецФункции // // Добавляет новую строку в таблицу и заполняет её значениями. ✦ // Функция AddRow(Таблица, ЗначенияСтроки) Экспорт //⚙ НоваяСтрока = Таблица.Добавить(); //✏ Если ТипЗнч(ЗначенияСтроки) = Тип("Структура") Тогда //⚡ ЗаполнитьЗначенияСвойств(НоваяСтрока, ЗначенияСтроки); //✏ ИначеЕсли ТипЗнч(ЗначенияСтроки) = Тип("Массив") Тогда //⚡ _ЗаполнитьСтрокуИзМассива(НоваяСтрока, ЗначенияСтроки); //▶️ КонецЕсли; Возврат НоваяСтрока; //↩ КонецФункции // // Добавляет несколько строк в таблицу из массива. ✦ // Функция AddRows(Таблица, МассивСтрок) Экспорт //⚙ Счетчик = 0; //✏ Для Каждого ДанныеСтроки Из МассивСтрок Цикл //⟳ AddRow(Таблица, ДанныеСтроки); //▶️ Счетчик = Счетчик + 1; //✏ КонецЦикла; Возврат Счетчик; //↩ КонецФункции #КонецОбласти #Область НавигацияПоТаблице /// ✦ First — Первая строка таблицы Функция First(Таблица) Экспорт //⚙ Если Таблица.Количество() = 0 Тогда //⚡ Возврат Неопределено; //↩ КонецЕсли; Возврат Таблица[0]; //↩ КонецФункции /// ✦ Last — Последняя строка таблицы Функция Last(Таблица) Экспорт //⚙ Количество = Таблица.Количество(); //✏ Если Количество = 0 Тогда //⚡ Возврат Неопределено; //↩ КонецЕсли; Возврат Таблица[Количество - 1]; //↩ КонецФункции /// ✦ Take — Первые N строк таблицы (TOP-N) Функция Take(Таблица, N) Экспорт //⚙ Результат = Таблица.Скопировать(); //✏ Пока Результат.Количество() > N Цикл //⟳ Результат.Удалить(Результат.Количество() - 1); КонецЦикла; Возврат Результат; //↩ КонецФункции /// ✦ Skip — Пропустить первые N строк таблицы Функция Skip(Таблица, N) Экспорт //⚙ Результат = Таблица.Скопировать(); //✏ Удалено = 0; //✏ Пока Удалено < N И Результат.Количество() > 0 Цикл //⟳ Результат.Удалить(0); Удалено = Удалено + 1; //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции /// ✦ Page — Получить страницу данных Функция Page(Таблица, НомерСтраницы, РазмерСтраницы) Экспорт //⚙ Пропустить = (НомерСтраницы - 1) * РазмерСтраницы; //✏ Возврат Take(Skip(Таблица, Пропустить), РазмерСтраницы); //↩ КонецФункции #КонецОбласти #Область АгрегатныеФункции /// ✦ Total — Сумма значений колонки Функция Total(Таблица, ИмяКолонки) Экспорт //⚙ Результат = 0; //✏ Для Каждого Строка Из Таблица Цикл //⟳ Значение = Строка[ИмяКолонки]; //✏ Если ТипЗнч(Значение) = Тип("Число") Тогда //⚡ Результат = Результат + Значение; //✏ КонецЕсли; КонецЦикла; Возврат Результат; //↩ КонецФункции /// ✦ Avg — Среднее значение колонки Функция Avg(Таблица, ИмяКолонки) Экспорт //⚙ Количество = Таблица.Количество(); //✏ Если Количество = 0 Тогда //⚡ Возврат 0; //↩ КонецЕсли; Возврат Total(Таблица, ИмяКолонки) / Количество; //↩ КонецФункции /// ✦ Minimum — Минимальное значение колонки Функция Minimum(Таблица, ИмяКолонки) Экспорт //⚙ Если Таблица.Количество() = 0 Тогда //⚡ Возврат Неопределено; //↩ КонецЕсли; Результат = Неопределено; //✏ Для Каждого Строка Из Таблица Цикл //⟳ Значение = Строка[ИмяКолонки]; //✏ Если Результат = Неопределено ИЛИ Значение < Результат Тогда //⚡ Результат = Значение; //✏ КонецЕсли; КонецЦикла; Возврат Результат; //↩ КонецФункции /// ✦ Maximum — Максимальное значение колонки Функция Maximum(Таблица, ИмяКолонки) Экспорт //⚙ Если Таблица.Количество() = 0 Тогда //⚡ Возврат Неопределено; //↩ КонецЕсли; Результат = Неопределено; //✏ Для Каждого Строка Из Таблица Цикл //⟳ Значение = Строка[ИмяКолонки]; //✏ Если Результат = Неопределено ИЛИ Значение > Результат Тогда //⚡ Результат = Значение; //✏ КонецЕсли; КонецЦикла; Возврат Результат; //↩ КонецФункции #КонецОбласти #Область МанипуляцияСтроками // // Находит первую строку таблицы по значениям колонок. ✦ // Функция FindRow(Таблица, КолонкиИЗначения) Экспорт //⚙ НайденныеСтроки = Таблица.НайтиСтроки(КолонкиИЗначения); //✏ Если НайденныеСтроки.Количество() > 0 Тогда //⚡ Возврат НайденныеСтроки[0]; //↩ Иначе Возврат Неопределено; //↩ КонецЕсли; КонецФункции // // Находит все строки таблицы по значениям колонок. ✦ // Функция Filter(Таблица, КолонкиИЗначения = Неопределено) Экспорт //⚙ Если КолонкиИЗначения = Неопределено Тогда //⚡ Возврат _ТаблицаВМассив(Таблица); //▶️ Иначе Возврат Таблица.НайтиСтроки(КолонкиИЗначения); //↩ КонецЕсли; КонецФункции // // Сортирует таблицу по указанным колонкам. ✦ // Функция Sort(Таблица, Колонки) Экспорт //⚙ Таблица.Сортировать(Колонки); //✏ Возврат Таблица; //↩ КонецФункции #КонецОбласти #Область МодификацияТаблицы // // Очищает все строки таблицы, сохраняя структуру колонок. ✦ // Функция Clear(Таблица) Экспорт //⚙ Таблица.Очистить(); //✏ Возврат Таблица; //↩ КонецФункции // // Удаляет строки таблицы по условию. ✦ // Функция DeleteRows(Таблица, КолонкиИЗначения) Экспорт //⚙ НайденныеСтроки = Таблица.НайтиСтроки(КолонкиИЗначения); //✏ Счетчик = НайденныеСтроки.Количество(); //✏ Для Каждого Строка Из НайденныеСтроки Цикл //⟳ Таблица.Удалить(Строка); //✏ КонецЦикла; Возврат Счетчик; //↩ КонецФункции #КонецОбласти #Область СериализацияИСанация // // Санация данных таблицы для JSON (вызывает A1sDS.Sanitize). ✦ // Таблица значений ➤ // Array — Массив структур ⬅ // &НаСервере Функция Sanitize(Знач Таблица) Экспорт Если ТипЗнч(Таблица) <> Тип("ТаблицаЗначений") Тогда Возврат Новый Массив; КонецЕсли; Возврат A1sDS.Sanitize(Таблица); КонецФункции // // Преобразует таблицу в JSON-строку (с автоматической санацией). ✦ // &НаСервере Функция ToJSON(Знач Таблица) Экспорт Если ТипЗнч(Таблица) <> Тип("ТаблицаЗначений") Тогда Возврат "[]"; КонецЕсли; // 1. Очистка данных от 1С-специфики ЧистыеДанные = A1sDS.Sanitize(Таблица); // 2. Сериализация Возврат A1sDS.ToJSON(ЧистыеДанные); КонецФункции #КонецОбласти #Область Преобразование // // Преобразует таблицу в массив структур. ✦ // Функция ToArray(Таблица, Колонки = "") Экспорт //⚙ Результат = Новый Массив; //✏ СписокКолонок = ?(ПустаяСтрока(Колонки), GetColumnNames(Таблица), СтрРазделить(Колонки, ",", Ложь)); //✏ Для Каждого Строка Из Таблица Цикл //⟳ СтруктураСтроки = Новый Структура; //✏ Для Каждого ИмяКолонки Из СписокКолонок Цикл //⟳ ИмяКолонки = СокрЛП(ИмяКолонки); //✏ СтруктураСтроки.Вставить(ИмяКолонки, Строка[ИмяКолонки]); //✏ КонецЦикла; Результат.Добавить(СтруктураСтроки); //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции // // Получает значения указанной колонки в виде массива. ✦ // Функция Column(Таблица, ИмяКолонки) Экспорт //⚙ Результат = Новый Массив; //✏ Для Каждого Строка Из Таблица Цикл //⟳ Результат.Добавить(Строка[ИмяКолонки]); //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции // // Выгружает колонку таблицы в массив (алиас для Column). ✦ // Функция UnloadColumn(Таблица, ИмяКолонки) Экспорт //⚙ Возврат Column(Таблица, ИмяКолонки); //▶️ КонецФункции #КонецОбласти #Область АнализИУтилиты // // Получает количество строк в таблице. ✦ // Функция Count(Таблица) Экспорт //⚙ Возврат Таблица.Количество(); //↩ КонецФункции // // Проверяет, пуста ли таблица. ✦ // Функция IsEmpty(Таблица) Экспорт //⚙ Возврат Таблица.Количество() = 0; //↩ КонецФункции // // Создает копию таблицы со всеми данными. ✦ // Функция Copy(Таблица, Колонки = "") Экспорт //⚙ Если ПустаяСтрока(Колонки) Тогда //⚡ Возврат Таблица.Скопировать(); //↩ Иначе Возврат Таблица.Скопировать(, Колонки); //↩ КонецЕсли; КонецФункции // // Группирует таблицу по указанным колонкам с агрегацией. ✦ // Функция GroupBy(Таблица, КолонкиГруппировки, КолонкиСуммирования = "") Экспорт //⚙ Если ПустаяСтрока(КолонкиСуммирования) Тогда //⚡ Возврат Таблица.Скопировать(КолонкиГруппировки); //↩ Иначе Результат = Таблица.Скопировать(); //✏ Результат.Свернуть(КолонкиГруппировки, КолонкиСуммирования); //✏ Возврат Результат; //↩ КонецЕсли; КонецФункции // // Получает список имён всех колонок таблицы. ✦ // Функция GetColumnNames(Таблица) Экспорт //⚙ Результат = Новый Массив; //✏ Для Каждого Колонка Из Таблица.Колонки Цикл //⟳ Результат.Добавить(Колонка.Имя); //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции // // Проверяет наличие колонки в таблице. ✦ // Функция HasColumn(Таблица, ИмяКолонки) Экспорт //⚙ Возврат Таблица.Колонки.Найти(ИмяКолонки) <> Неопределено; //↩ КонецФункции // // Создает индекс для быстрого поиска по колонкам. ✦ // Функция CreateIndex(Таблица, Колонки) Экспорт //⚙ Таблица.Индексы.Добавить(Колонки); //✏ Возврат Таблица; //↩ КонецФункции // // Объединяет строки из нескольких таблиц в одну (Union All). ✦ // Функция Union(ЦелеваяТаблица, ДобавляемыеТаблицы) Экспорт //⚙ Для Каждого Таблица Из ДобавляемыеТаблицы Цикл //⟳ Для Каждого Строка Из Таблица Цикл //⟳ ЗаполнитьЗначенияСвойств(ЦелеваяТаблица.Добавить(), Строка); //✏ КонецЦикла; КонецЦикла; Возврат ЦелеваяТаблица; //↩ КонецФункции // // Применяет функцию-обработчик к каждой строке таблицы. ✦ // Функция ForEach(Таблица, Обработчик) Экспорт //⚙ Для Каждого Строка Из Таблица Цикл //⟳ Выполнить(Обработчик + "(Строка)"); //▶️ КонецЦикла; Возврат Таблица; //↩ КонецФункции #КонецОбласти #Область СлужебныеПроцедурыИФункции // Добавляет колонки в таблицу из строкового описания Процедура _ДобавитьКолонкиИзСтроки(Таблица, СтрокаОписания) //✍ МассивКолонок = СтрРазделить(СтрокаОписания, ",", Ложь); //✏ Для Каждого ОписаниеКолонки Из МассивКолонок Цикл //⟳ ОписаниеКолонки = СокрЛП(ОписаниеКолонки); //✏ Если СтрНайти(ОписаниеКолонки, ":") > 0 Тогда //⚡ ЧастиОписания = СтрРазделить(ОписаниеКолонки, ":", Ложь); //✏ ИмяКолонки = СокрЛП(ЧастиОписания[0]); //✏ ИмяТипа = СокрЛП(ЧастиОписания[1]); //✏ ТипКолонки = _ПолучитьТипПоИмени(ИмяТипа); //▶️ Таблица.Колонки.Добавить(ИмяКолонки, ТипКолонки); //✏ Иначе Таблица.Колонки.Добавить(ОписаниеКолонки); //✏ КонецЕсли; КонецЦикла; КонецПроцедуры // Добавляет колонки в таблицу из структуры Процедура _ДобавитьКолонкиИзСтруктуры(Таблица, Структура) //✍ Для Каждого КлючЗначение Из Структура Цикл //⟳ ИмяКолонки = КлючЗначение.Ключ; //✏ Если ТипЗнч(КлючЗначение.Значение) = Тип("Строка") Тогда //⚡ ТипКолонки = _ПолучитьТипПоИмени(КлючЗначение.Значение); //▶️ Таблица.Колонки.Добавить(ИмяКолонки, ТипКолонки); //✏ ИначеЕсли ТипЗнч(КлючЗначение.Значение) = Тип("ОписаниеТипов") Тогда //⚡ Таблица.Колонки.Добавить(ИмяКолонки, КлючЗначение.Значение); //✏ Иначе Таблица.Колонки.Добавить(ИмяКолонки); //✏ КонецЕсли; КонецЦикла; КонецПроцедуры // Добавляет строку в таблицу с заполнением значений Процедура _ДобавитьСтрокуВТаблицу(Таблица, ДанныеСтроки) //✍ НоваяСтрока = Таблица.Добавить(); //✏ Если ТипЗнч(ДанныеСтроки) = Тип("Структура") Тогда //⚡ ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеСтроки); //✏ ИначеЕсли ТипЗнч(ДанныеСтроки) = Тип("Массив") Тогда //⚡ _ЗаполнитьСтрокуИзМассива(НоваяСтрока, ДанныеСтроки); //▶️ КонецЕсли; КонецПроцедуры // Заполняет строку таблицы значениями из массива по порядку Процедура _ЗаполнитьСтрокуИзМассива(Строка, МассивЗначений) //✍ Владелец = Строка.Владелец(); //✏ Индекс = 0; //✏ Для Каждого Колонка Из Владелец.Колонки Цикл //⟳ Если Индекс < МассивЗначений.Количество() Тогда //⚡ Строка[Колонка.Имя] = МассивЗначений[Индекс]; //✏ Индекс = Индекс + 1; //✏ КонецЕсли; КонецЦикла; КонецПроцедуры // Возвращает описание типа по его имени Функция _ПолучитьТипПоИмени(ИмяТипа) Экспорт //✍ ИмяТипа = ВРег(СокрЛП(ИмяТипа)); //✏ Если ИмяТипа = "СТРОКА" ИЛИ ИмяТипа = "STRING" Тогда //⚡ Возврат Новый ОписаниеТипов("Строка"); //↩ ИначеЕсли ИмяТипа = "ЧИСЛО" ИЛИ ИмяТипа = "NUMBER" Тогда //⚡ Возврат Новый ОписаниеТипов("Число"); //↩ ИначеЕсли ИмяТипа = "ДАТА" ИЛИ ИмяТипа = "DATE" Тогда //⚡ Возврат Новый ОписаниеТипов("Дата"); //↩ ИначеЕсли ИмяТипа = "БУЛЕВО" ИЛИ ИмяТипа = "BOOLEAN" Тогда //⚡ Возврат Новый ОписаниеТипов("Булево"); //↩ Иначе Попытка //⚡ Возврат Новый ОписаниеТипов(ИмяТипа); //↩ Исключение Возврат Новый ОписаниеТипов; //↩ КонецПопытки; КонецЕсли; КонецФункции // Получает массив имён колонок таблицы Функция _ПолучитьИменаКолонок(Таблица) //✍ Результат = Новый Массив; //✏ Для Каждого Колонка Из Таблица.Колонки Цикл //⟳ Результат.Добавить(Колонка.Имя); //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции // Преобразует таблицу в массив строк Функция _ТаблицаВМассив(Таблица) //✍ Результат = Новый Массив; //✏ Для Каждого Строка Из Таблица Цикл //⟳ Результат.Добавить(Строка); //✏ КонецЦикла; Возврат Результат; //↩ КонецФункции Функция _ПолучитьОписаниеТипаДляЗначения(Значение, МассивПроверки = Неопределено) ОписаниеТипа = Неопределено; Если Значение = Неопределено Тогда Возврат Новый ОписаниеТипов("Строка"); КонецЕсли; ТипЗначения = ТипЗнч(Значение); Если ТипЗначения = Тип("Строка") Тогда Возврат Новый ОписаниеТипов("Строка"); ИначеЕсли ТипЗначения = Тип("Число") Тогда ЕстьОтрицательные = Ложь; Если МассивПроверки <> Неопределено Тогда // ИСПРАВЛЕНИЕ: Перебираем все свойства структуры, так как мы не знаем точное имя колонки. // Если в таблице есть хотя бы одно отрицательное число в любой колонке, // мы разрешаем отрицательные значения для всех числовых колонок ради безопасности. Для Каждого Строка Из МассивПроверки Цикл Для Каждого КлючИЗначение Из Строка Цикл ЗначениеПоля = КлючИЗначение.Значение; Если ТипЗнч(ЗначениеПоля) = Тип("Число") И ЗначениеПоля < 0 Тогда ЕстьОтрицательные = Истина; Прервать; КонецЕсли; КонецЦикла; Если ЕстьОтрицательные Тогда Прервать; КонецЕсли; КонецЦикла; КонецЕсли; Если ЕстьОтрицательные Тогда Возврат Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Любой)); Иначе Возврат Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2, ДопустимыйЗнак.Неотрицательный)); КонецЕсли; ИначеЕсли ТипЗначения = Тип("Дата") Тогда Возврат Новый ОписаниеТипов("Дата"); ИначеЕсли ТипЗначения = Тип("Булево") Тогда Возврат Новый ОписаниеТипов("Булево"); ИначеЕсли ТипЗначения = Тип("УникальныйИдентификатор") Тогда Возврат Новый ОписаниеТипов("УникальныйИдентификатор"); Иначе // Для прочих типов (ссылки, etc.) используем строку как наиболее универсальный вариант. Возврат Новый ОписаниеТипов("Строка"); КонецЕсли; КонецФункции #КонецОбласти #Область FluentInterface // // Создаёт fluent-обёртку для работы с таблицами значений. ✦ // &НаСервере Функция On(Знач Исходная = Неопределено) Экспорт //⚙ Обработка = Обработки.A1sDP_VT.Создать(); Обработка.Init(Исходная); Возврат Обработка; //↩ КонецФункции #КонецОбласти #Область Тестирование // // Выполняет автоматическое тестирование всех функций модуля A1sVT. ✦ // Boolean — Истина если все тесты прошли успешно ⬅ // Функция SelfTest() Экспорт //⚙ TestsTotal = 0; //✏ TestsPassed = 0; //✏ Сообщить("=== Запуск тестирования A1sVT ==="); // Тест 1: Создание таблицы из строки с типами TestsTotal = TestsTotal + 1; //✏ Попытка Таб1 = Of("Код:Строка,Наименование:Строка,Цена:Число"); //▶️ Если Count(Таб1) = 0 И HasColumn(Таб1, "Код") И HasColumn(Таб1, "Цена") Тогда //⚡ TestsPassed = TestsPassed + 1; //✏ Сообщить("✓ Тест 1 (Of с типами): ПРОЙДЕН"); Иначе Сообщить("✗ Тест 1 (Of с типами): ПРОВАЛЕН"); КонецЕсли; Исключение Сообщить("✗ Тест 1 (Of с типами): ОШИБКА - " + ОписаниеОшибки()); КонецПопытки; // Тест 2: Добавление строк TestsTotal = TestsTotal + 1; //✏ Попытка Таб2 = OfColumns("Товар,Количество:Число"); //▶️ AddRow(Таб2, Новый Структура("Товар,Количество", "Молоко", 5)); //▶️ МассивЗначений = Новый Массив; //✏ МассивЗначений.Добавить("Хлеб"); //✏ МассивЗначений.Добавить(3); //✏ AddRow(Таб2, МассивЗначений); //▶️ Если Count(Таб2) = 2 Тогда //⚡ TestsPassed = TestsPassed + 1; //✏ Сообщить("✓ Тест 2 (AddRow): ПРОЙДЕН"); Иначе Сообщить("✗ Тест 2 (AddRow): ПРОВАЛЕН"); КонецЕсли; Исключение Сообщить("✗ Тест 2 (AddRow): ОШИБКА - " + ОписаниеОшибки()); КонецПопытки; // Итоги тестирования Если TestsPassed = TestsTotal Тогда //⚡ Сообщить("=== A1sVT.SelfTest() SUCCESS: " + Строка(TestsPassed) + "/" + Строка(TestsTotal) + " ==="); Возврат Истина; //↩ Иначе Сообщить("=== A1sVT.SelfTest() FAILED: " + Строка(TestsPassed) + "/" + Строка(TestsTotal) + " ==="); Возврат Ложь; //↩ КонецЕсли; КонецФункции #КонецОбласти //////////////////////////////////////////////////////////////////////////////// // МОДУЛЬ ЗАВЕРШЕН - A1sVT v2.1.0 ////////////////////////////////////////////////////////////////////////////////