СерияДиаграммыГанта.ШтриховкаМеждуИнтервалами

Свойства · BetweenIntervalsHatch

Тип: Булево. Если установлено Истина, то пространство между интервалами выводятся штриховкой, а не сплошным цветом. Цвет выбирается из свойства ЦветШтриховкиМеждуИнтервалами.

Полное описание, параметры и ограничения

СерияДиаграммыГанта.ШтриховкаМеждуИнтервалами (GanttChartSeries.BetweenIntervalsHatch)

СерияДиаграммыГанта (GanttChartSeries)

ШтриховкаМеждуИнтервалами (BetweenIntervalsHatch)

Доступен, начиная с версии 8.0.

Использование:
Чтение и запись.
Описание:
Тип: Булево.
Если установлено Истина, то пространство между интервалами выводятся штриховкой, а не сплошным цветом. Цвет выбирается из свойства ЦветШтриховкиМеждуИнтервалами.
Доступность:

Тонкий клиент, веб-клиент, сервер, толстый клиент, внешнее соединение.

Использование в версии:

Доступен, начиная с версии 8.0.

Методическая информация

Практические примеры

Бронирования станка: найти перекрытие, показать 6/5/1 и выделить конфликт

Полный учебный экран на своих данных в памяти: оформление Ганта не подменяет расчет часов, настоящие идентификаторы находят свой интервал, а выделение таблицы указывает две конфликтующие записи.

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

Весь MAIN в собственной общей управляемой форме ПланБронирований85. Только Windows, толстый управляемый клиент 8.5.1.1343 своей файловой базы. Создание и проверка путей данных на сервере; построение, численный readback и выделение на клиенте. Никаких записей в базу.

Подготовка

  1. Создайте новую пустую файловую базу платформой 8.5.1.1343 в C:\1c-labs\OwnGanttConflict15_3d50f63d-7a2c-4dc2-9adf-c4115897ea65\IB. Имя конфигурации УчебныйГантКонфликт85; режим совместимости НеИспользовать; основной режим запуска УправляемоеПриложение. Не используйте рабочую конфигурацию или копию с чужими обработчиками. Этот код здесь не запускался.

  2. Создайте одну собственную роль ГантКонфликт85Администратор с полными правами, включая Администрирование, и одного пользователя ГантКонфликт85Ученик с этой ролью. ПолучитьПользователей требует административных прав. В пустой базе нет справочников, документов, обработок, отчетов и регистров; записи и настройки этого урока не сохраняются в базе.

  3. Создайте неглобальный общий модуль ГантКонфликт85: Сервер=Да, ВызовСервера=Да, Клиент=Нет, ВнешнееСоединение=Нет, Привилегированный=Нет, повторное использование возвращаемых значений НеИспользовать. Вставьте supporting GUARD целиком в его модуль. Он только проверяет подготовку и не записывает данные.

  4. Создайте одну общую управляемую форму ПланБронирований85. Реквизиты формы: План85 типа ДиаграммаГанта; Итоги85 типа Диаграмма; Документ85 типа ТабличныйДокумент; ЧасыЗадач85, ЧасыЗанято85, ЧасыПерекрытия85 типа Число(8,2); Сообщение85 типа Строка неограниченной длины. ВосстанавливатьЗначенияПриОткрытии=Нет.

  5. На форме разместите ПолеПлана85 вида ПолеДиаграммыГанта с ПутьКДанным=План85, ПолеИтогов85 вида ПолеДиаграммы с ПутьКДанным=Итоги85 и ПолеДокумента85 вида ПолеТабличногоДокумента с ПутьКДанным=Документ85. Разрешите растягивание этих полей, дайте графику и таблице достаточную ширину. Три численных итога и Сообщение85 выведите отдельными полями только для просмотра.

  6. В командах формы создайте ВыделитьКонфликт85, действие ВыделитьКонфликт85, и собственную кнопку с этой командой. Обработчик собственной команды получает один параметр Команда; это не событие Нажатие с первым параметром Элемент. ПриСозданииНаСервере назначьте одноименной процедуре с Отказ, СтандартнаяОбработка; ПриОткрытии назначьте одноименной процедуре с Отказ. Вставьте MAIN целиком в модуль формы. Переменные MAIN объявлены только на клиенте.

  7. В модуль управляемого приложения вставьте supporting APPLICATION целиком: ПриНачалеРаботыСистемы без параметров открывает собственную общую форму. Обновите конфигурацию базы данных только этого стенда. Запустите его толстым управляемым клиентом Windows и указанным собственным пользователем. Тонкий клиент для этого урока не подходит: изменение коллекции выделенных областей документировано для толстого клиента и сервера.

  8. Три бронирования создаются самим кодом в памяти: Резка образца 09:00–11:00, Настройка станка 10:00–13:00, Обработка детали 15:00–16:00, все 10.10.2026 для одного учебного станка. Табличный макет не требуется: собственный ТабличныйДокумент и шесть настоящих числовых ячеек создаются программно. Не подставляйте реальные задания, файлы или исходный объект из чужой базы.

  9. Сначала сравните численные итоги 6, 5, 1 и значения ячеек Часы: 2, 3, 1, 6, 5, 1. Затем один раз нажмите Выделить конфликт: выделение своей таблицы заменяется двумя конфликтующими строками. Открытие новой формы создает новый независимый набор данных; повтор команды в той же форме честно отказывается.

&НаКлиенте
Перем СвоиБрони85, Готово85, Остановка85, ВыделениеГотово85;

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Попытка
        ГантКонфликт85.ПроверитьСвойСтенд85();
        Если ИмяФормы <> "ОбщаяФорма.ПланБронирований85"
            Или Элементы.ПолеПлана85.Вид <> ВидПоляФормы.ПолеДиаграммыГанта
            Или Элементы.ПолеПлана85.ПутьКДанным <> "План85"
            Или Элементы.ПолеИтогов85.Вид <> ВидПоляФормы.ПолеДиаграммы
            Или Элементы.ПолеИтогов85.ПутьКДанным <> "Итоги85"
            Или Элементы.ПолеДокумента85.Вид <> ВидПоляФормы.ПолеТабличногоДокумента
            Или Элементы.ПолеДокумента85.ПутьКДанным <> "Документ85"
            Или Команды.ВыделитьКонфликт85.Действие <> "ВыделитьКонфликт85" Тогда
            ВызватьИсключение "Проверьте имена, виды, пути данных полей и действие собственной команды";
        КонецЕсли;
        Элементы.ПолеПлана85.ТолькоПросмотр = Истина;
        Элементы.ПолеИтогов85.ТолькоПросмотр = Истина;
        Элементы.ПолеДокумента85.ТолькоПросмотр = Истина;
    Исключение
        Отказ = Истина;
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ПроверитьБазуНаСервере85()
    ГантКонфликт85.ПроверитьСвойСтенд85();
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    Если Готово85 <> Неопределено Или Остановка85 <> Неопределено Тогда
        Остановка85 = Истина;
        Отказ = Истина;
        ВызватьИсключение "Нельзя повторно инициализировать эту форму или сбросить её отказ";
    КонецЕсли;
    Остановка85 = Ложь;
    Попытка
#Если ТолстыйКлиентУправляемоеПриложение Тогда
        ПроверитьБазуНаСервере85();
        СвоиБрони85 = СоздатьСвоиБрони85();
        Часы = РассчитатьЧасы85(СвоиБрони85);
        НовыйПлан = СоздатьПлан85(СвоиБрони85);
        НовыйИтог = СоздатьИтоги85(Часы);
        НовыйДокумент = СоздатьДокумент85(СвоиБрони85, Часы);
        ПроверитьПлан85(НовыйПлан, СвоиБрони85);
        ПроверитьИтоги85(НовыйИтог, Часы);
        ПроверитьДокумент85(НовыйДокумент, СвоиБрони85, Часы);
        План85 = НовыйПлан;
        Итоги85 = НовыйИтог;
        Документ85 = НовыйДокумент;
        ЧасыЗадач85 = Часы.Задачи;
        ЧасыЗанято85 = Часы.Занято;
        ЧасыПерекрытия85 = Часы.Перекрытие;
        ВыделениеГотово85 = Ложь;
        ПроверитьТекущееСостояние85();
        Готово85 = Истина;
        Сообщение85 = "6 часов бронирований, 5 часов занятости, 1 час перекрытия. Нажмите «Выделить конфликт».";
#Иначе
        ВызватьИсключение "Нужен толстый управляемый клиент";
#КонецЕсли
    Исключение
        Остановка85 = Истина;
        Отказ = Истина;
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Функция СоздатьСвоиБрони85()
    Брони = Новый Массив;
    Брони.Добавить(Новый ФиксированнаяСтруктура("Код,Название,Начало,Конец", "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/A", "Резка образца", Дата(2026, 10, 10, 9), Дата(2026, 10, 10, 11)));
    Брони.Добавить(Новый ФиксированнаяСтруктура("Код,Название,Начало,Конец", "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/B", "Настройка станка", Дата(2026, 10, 10, 10), Дата(2026, 10, 10, 13)));
    Брони.Добавить(Новый ФиксированнаяСтруктура("Код,Название,Начало,Конец", "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/C", "Обработка детали", Дата(2026, 10, 10, 15), Дата(2026, 10, 10, 16)));
    Возврат Новый ФиксированныйМассив(Брони);
КонецФункции

&НаКлиенте
Процедура ПроверитьБрони85(Снимок)
    Если ТипЗнч(Снимок) <> Тип("ФиксированныйМассив") Или Снимок.Количество() <> 3 Тогда
        ВызватьИсключение "Нужны ровно три собственных бронирования";
    КонецЕсли;
    Эталон = СоздатьСвоиБрони85();
    Для Индекс = 0 По 2 Цикл
        Бронь = Снимок.Получить(Индекс);
        Ожидаемая = Эталон.Получить(Индекс);
        Если ТипЗнч(Бронь) <> Тип("ФиксированнаяСтруктура") Или Бронь.Количество() <> 4
            Или ТипЗнч(Бронь.Код) <> Тип("Строка") Или ТипЗнч(Бронь.Название) <> Тип("Строка")
            Или ТипЗнч(Бронь.Начало) <> Тип("Дата") Или ТипЗнч(Бронь.Конец) <> Тип("Дата")
            Или Бронь.Код <> Ожидаемая.Код Или Бронь.Название <> Ожидаемая.Название
            Или Бронь.Начало <> Ожидаемая.Начало Или Бронь.Конец <> Ожидаемая.Конец Тогда
            ВызватьИсключение "Изменились код, название или даты собственной исходной строки";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Функция РассчитатьЧасы85(Снимок)
    ПроверитьБрони85(Снимок);
    СекундыЗадач = 0;
    СекундыЗанято = 0;
    НачалоОбъединения = Снимок.Получить(0).Начало;
    КонецОбъединения = Снимок.Получить(0).Конец;
    Для Каждого Бронь Из Снимок Цикл
        СекундыЗадач = СекундыЗадач + (Бронь.Конец - Бронь.Начало);
        Если Бронь.Начало > КонецОбъединения Тогда
            СекундыЗанято = СекундыЗанято + (КонецОбъединения - НачалоОбъединения);
            НачалоОбъединения = Бронь.Начало;
            КонецОбъединения = Бронь.Конец;
        ИначеЕсли Бронь.Конец > КонецОбъединения Тогда
            КонецОбъединения = Бронь.Конец;
        КонецЕсли;
    КонецЦикла;
    СекундыЗанято = СекундыЗанято + (КонецОбъединения - НачалоОбъединения);
    Часы = Новый ФиксированнаяСтруктура("Задачи,Занято,Перекрытие", СекундыЗадач / 3600, СекундыЗанято / 3600, (СекундыЗадач - СекундыЗанято) / 3600);
    Если Часы.Задачи <> 6 Или Часы.Занято <> 5 Или Часы.Перекрытие <> 1 Тогда
        ВызватьИсключение "Должны получиться 6 часов задач, 5 часов занятости и 1 час перекрытия";
    КонецЕсли;
    Возврат Часы;
КонецФункции

&НаКлиенте
Функция СоздатьПлан85(Снимок)
    ПроверитьБрони85(Снимок);
    График = Новый ДиаграммаГанта;
    График.АвтоОпределениеПолногоИнтервала = Ложь;
    График.УстановитьПолныйИнтервал(Дата(2026, 10, 10), Дата(2026, 10, 10, 23, 59, 59));
    График.АвтоУстановкаТекстаТочек = Ложь;
    График.АвтоУстановкаТекстаСерий = Ложь;
    График.ОтображениеТекстаИнтервала = ОтображениеТекстаИнтервалаДиаграммыГанта.Отображать;
    СерияПлана = График.УстановитьСерию("3d50f63d-7a2c-4dc2-9adf-c4115897ea65/Брони");
    СерияПлана.Текст = "Бронирования своего станка";
    СерияПлана.Расшифровка = "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/ОписаниеСерии";
    СерияПлана.ПриоритетЦвета = Истина;
    СерияПлана.Цвет = Новый Цвет(30, 100, 150);
    СерияПлана.ДополнительныйЦвет = Новый Цвет(220, 80, 30);
    СерияПлана.ЦветШтриховкиМеждуИнтервалами = Новый Цвет(110, 110, 110);
    СерияПлана.ШтриховкаМеждуИнтервалами = Истина;
    СерияПлана.ШтриховкаПерекрывающихсяИнтервалов = Истина;
    ТочкаПлана = График.УстановитьТочку("3d50f63d-7a2c-4dc2-9adf-c4115897ea65/Станок");
    ТочкаПлана.Текст = "Собственный учебный станок";
    ТочкаПлана.ПриоритетЦвета = Ложь;
    ЗначениеПлана = График.ПолучитьЗначение(ТочкаПлана, СерияПлана);
    ЗначениеПлана.Редактирование = Ложь;
    ЗначениеПлана.Цвет = Новый Цвет();
    ЗначениеПлана.ДополнительныйЦвет = Новый Цвет();
    Для Каждого Бронь Из Снимок Цикл
        Интервал = ЗначениеПлана.Добавить();
        Интервал.Начало = Бронь.Начало;
        Интервал.Конец = Бронь.Конец;
        Интервал.Текст = Бронь.Название;
        Интервал.Расшифровка = Бронь.Код;
        Интервал.Цвет = Новый Цвет();
        Интервал.ЦветТекста = Новый Цвет(20, 20, 20);
    КонецЦикла;
    Шкала = График.ОбластьПостроения.ШкалаВремени;
    Пока Шкала.Элементы.Количество() > 0 Цикл
        Шкала.Элементы.Удалить(Шкала.Элементы.Получить(0));
    КонецЦикла;
    День = Шкала.Элементы.Добавить();
    День.Единица = ТипЕдиницыШкалыВремени.День;
    День.Кратность = 1;
    День.ФорматДня = ФорматДняШкалыВремени.ДеньМесяцаДеньНедели;
    День.ЛинииДелений = Новый Линия(ТипЛинииДиаграммы.Сплошная, 1);
    Час = Шкала.Элементы.Добавить();
    Час.Единица = ТипЕдиницыШкалыВремени.Час;
    Час.Кратность = 1;
    Час.Формат = "ДФ=HH:mm";
    Возврат График;
КонецФункции

&НаКлиенте
Функция НайтиСвойИнтервал85(ЗначениеПлана, СвойКод)
    Если ТипЗнч(ЗначениеПлана) <> Тип("ЗначениеДиаграммыГанта") Или ЗначениеПлана.Количество() <> 3
        Или ТипЗнч(СвойКод) <> Тип("Строка") Тогда
        ВызватьИсключение "Нужны свое значение Ганта из трех интервалов и строковый код бронирования";
    КонецЕсли;
    Если СвойКод <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/A"
        И СвойКод <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/B"
        И СвойКод <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/C" Тогда
        ВызватьИсключение "Код не принадлежит трем собственным учебным бронированиям";
    КонецЕсли;
    НайденныйИнтервал = Неопределено;
    Для Каждого Интервал Из ЗначениеПлана Цикл
        Если ТипЗнч(Интервал) <> Тип("ИнтервалДиаграммыГанта") Тогда
            ВызватьИсключение "Коллекция должна возвращать настоящий интервал Ганта";
        КонецЕсли;
        Если Интервал.Расшифровка = СвойКод Тогда
            Если НайденныйИнтервал <> Неопределено Тогда
                ВызватьИсключение "Собственный код повторяется в коллекции интервалов";
            КонецЕсли;
            НайденныйИнтервал = Интервал;
        КонецЕсли;
    КонецЦикла;
    Если НайденныйИнтервал = Неопределено Тогда
        ВызватьИсключение "Собственное бронирование не найдено в фактической коллекции";
    КонецЕсли;
    Возврат НайденныйИнтервал;
КонецФункции

&НаКлиенте
Процедура ПроверитьПлан85(График, Снимок)
    ПроверитьБрони85(Снимок);
    Если ТипЗнч(График) <> Тип("ДиаграммаГанта") Или График.Точки.Количество() <> 1 Или График.Серии.Количество() <> 1 Тогда
        ВызватьИсключение "В диаграмме должны быть одна своя точка и одна серия";
    КонецЕсли;
    ТочкаПлана = График.Точки.Получить(0);
    СерияПлана = График.Серии.Получить(0);
    Если ТочкаПлана.Значение <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/Станок" Или ТочкаПлана.ПриоритетЦвета
        Или СерияПлана.Значение <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/Брони"
        Или СерияПлана.Расшифровка <> "3d50f63d-7a2c-4dc2-9adf-c4115897ea65/ОписаниеСерии"
        Или Не СерияПлана.ПриоритетЦвета Или СерияПлана.Цвет <> Новый Цвет(30, 100, 150)
        Или СерияПлана.ДополнительныйЦвет <> Новый Цвет(220, 80, 30)
        Или СерияПлана.ЦветШтриховкиМеждуИнтервалами <> Новый Цвет(110, 110, 110)
        Или Не СерияПлана.ШтриховкаМеждуИнтервалами Или Не СерияПлана.ШтриховкаПерекрывающихсяИнтервалов Тогда
        ВызватьИсключение "Изменились принадлежность или заданное оформление серии";
    КонецЕсли;
    ЗначениеПлана = График.ПолучитьЗначение(ТочкаПлана, СерияПлана);
    Если ЗначениеПлана.Количество() <> 3 Или ЗначениеПлана.Редактирование
        Или ЗначениеПлана.Цвет <> Новый Цвет() Или ЗначениеПлана.ДополнительныйЦвет <> Новый Цвет() Тогда
        ВызватьИсключение "Изменилось собственное значение диаграммы Ганта";
    КонецЕсли;
    Прочитанные = Новый Массив;
    Для Индекс = 0 По 2 Цикл
        Бронь = Снимок.Получить(Индекс);
        Интервал = НайтиСвойИнтервал85(ЗначениеПлана, Бронь.Код);
        Если Интервал.Начало <> Бронь.Начало Или Интервал.Конец <> Бронь.Конец
            Или Интервал.Текст <> Бронь.Название Или Интервал.Расшифровка <> Бронь.Код
            Или Интервал.Цвет <> Новый Цвет() Или Интервал.ЦветТекста <> Новый Цвет(20, 20, 20) Тогда
            ВызватьИсключение "Изменились даты, название, свой код или оформление интервала";
        КонецЕсли;
        Прочитанные.Добавить(Новый ФиксированнаяСтруктура("Код,Название,Начало,Конец", Интервал.Расшифровка, Интервал.Текст, Интервал.Начало, Интервал.Конец));
    КонецЦикла;
    РассчитатьЧасы85(Новый ФиксированныйМассив(Прочитанные));
    Если График.ОбластьПостроения.ШкалаВремени.Элементы.Количество() <> 2 Тогда
        ВызватьИсключение "Нужны ровно две собственные шкалы до чтения по индексу";
    КонецЕсли;
    День = График.ОбластьПостроения.ШкалаВремени.Элементы.Получить(0);
    Час = График.ОбластьПостроения.ШкалаВремени.Элементы.Получить(1);
    Если День.Единица <> ТипЕдиницыШкалыВремени.День Или День.Кратность <> 1
        Или День.ФорматДня <> ФорматДняШкалыВремени.ДеньМесяцаДеньНедели
        Или День.ЛинииДелений.ТипЛинии <> ТипЛинииДиаграммы.Сплошная Или День.ЛинииДелений.Толщина <> 1
        Или Час.Единица <> ТипЕдиницыШкалыВремени.Час Или Час.Кратность <> 1 Или Час.Формат <> "ДФ=HH:mm" Тогда
        ВызватьИсключение "Изменились дневная или часовая шкалы";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция СоздатьИтоги85(Часы)
    Диаграмма = Новый Диаграмма;
    Диаграмма.ТипДиаграммы = ТипДиаграммы.ГистограммаСНакоплением;
    Диаграмма.АвтоТранспонирование = Ложь;
    СерияИтога = Диаграмма.УстановитьСерию("Часы85");
    Показатели = Новый Массив;
    Показатели.Добавить(Новый ФиксированнаяСтруктура("Имя,Часы", "Все бронирования", Часы.Задачи));
    Показатели.Добавить(Новый ФиксированнаяСтруктура("Имя,Часы", "Фактически занято", Часы.Занято));
    Показатели.Добавить(Новый ФиксированнаяСтруктура("Имя,Часы", "Перекрытие", Часы.Перекрытие));
    Для Каждого Показатель Из Показатели Цикл
        ТочкаИтога = Диаграмма.УстановитьТочку(Показатель.Имя);
        Диаграмма.УстановитьЗначение(ТочкаИтога, СерияИтога, Показатель.Часы);
        ЗначениеИтога = Диаграмма.ПолучитьЗначение(ТочкаИтога, СерияИтога);
        ЗначениеИтога.РежимРедактирования = РежимРедактированияЗначенийДиаграммы.НеИспользовать;
    КонецЦикла;
    Возврат Диаграмма;
КонецФункции

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

&НаКлиенте
Функция СоздатьДокумент85(Снимок, Часы)
    Документ = Новый ТабличныйДокумент;
    Документ.Область(1, 1).Текст = "Бронирование";
    Документ.Область(1, 2).Текст = "Начало";
    Документ.Область(1, 3).Текст = "Конец";
    Документ.Область(1, 4).Текст = "Часы";
    Для Индекс = 0 По 2 Цикл
        Бронь = Снимок.Получить(Индекс);
        СтрокаДокумента = Индекс + 2;
        Документ.Область(СтрокаДокумента, 1).Текст = Бронь.Название;
        Документ.Область(СтрокаДокумента, 2).Текст = Формат(Бронь.Начало, "ДФ=HH:mm");
        Документ.Область(СтрокаДокумента, 3).Текст = Формат(Бронь.Конец, "ДФ=HH:mm");
        ЗаписатьЧисло85(Документ, СтрокаДокумента, (Бронь.Конец - Бронь.Начало) / 3600);
    КонецЦикла;
    Документ.Область(6, 1).Текст = "Сумма длительностей";
    Документ.Область(7, 1).Текст = "Занято без повторного счета";
    Документ.Область(8, 1).Текст = "Перекрытие";
    ЗаписатьЧисло85(Документ, 6, Часы.Задачи);
    ЗаписатьЧисло85(Документ, 7, Часы.Занято);
    ЗаписатьЧисло85(Документ, 8, Часы.Перекрытие);
    Возврат Документ;
КонецФункции

&НаКлиенте
Процедура ЗаписатьЧисло85(Документ, НомерСтроки, ЧислоЧасов)
    Ячейка = Документ.Область(НомерСтроки, 4);
    Ячейка.СодержитЗначение = Истина;
    Ячейка.ТипЗначения = Новый ОписаниеТипов("Число");
    Ячейка.Значение = ЧислоЧасов;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьДокумент85(Документ, Снимок, Часы)
    Если ТипЗнч(Документ) <> Тип("ТабличныйДокумент") Или Документ.ВысотаТаблицы <> 8 Или Документ.ШиринаТаблицы <> 4 Тогда
        ВызватьИсключение "Изменились размер или тип собственной таблицы";
    КонецЕсли;
    Для Индекс = 0 По 2 Цикл
        Бронь = Снимок.Получить(Индекс);
        СтрокаДокумента = Индекс + 2;
        Если Документ.Область(СтрокаДокумента, 1).Текст <> Бронь.Название
            Или Документ.Область(СтрокаДокумента, 2).Текст <> Формат(Бронь.Начало, "ДФ=HH:mm")
            Или Документ.Область(СтрокаДокумента, 3).Текст <> Формат(Бронь.Конец, "ДФ=HH:mm") Тогда
            ВызватьИсключение "Изменились подписи или время исходной строки таблицы";
        КонецЕсли;
        ПроверитьЧисло85(Документ, СтрокаДокумента, (Бронь.Конец - Бронь.Начало) / 3600);
    КонецЦикла;
    ПроверитьЧисло85(Документ, 6, Часы.Задачи);
    ПроверитьЧисло85(Документ, 7, Часы.Занято);
    ПроверитьЧисло85(Документ, 8, Часы.Перекрытие);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьЧисло85(Документ, НомерСтроки, ОжидаемыеЧасы)
    Ячейка = Документ.Область(НомерСтроки, 4);
    Если Не Ячейка.СодержитЗначение Или Ячейка.ТипЗначения.Типы().Количество() <> 1
        Или Не Ячейка.ТипЗначения.СодержитТип(Тип("Число"))
        Или ТипЗнч(Ячейка.Значение) <> Тип("Число") Или Ячейка.Значение <> ОжидаемыеЧасы Тогда
        ВызватьИсключение "Часы должны оставаться настоящим числом с прежним значением";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьТекущееСостояние85()
    Часы = РассчитатьЧасы85(СвоиБрони85);
    ПроверитьПлан85(План85, СвоиБрони85);
    ПроверитьИтоги85(Итоги85, Часы);
    ПроверитьДокумент85(Документ85, СвоиБрони85, Часы);
    Если ЧасыЗадач85 <> Часы.Задачи Или ЧасыЗанято85 <> Часы.Занято Или ЧасыПерекрытия85 <> Часы.Перекрытие
        Или Не Элементы.ПолеПлана85.ТолькоПросмотр Или Не Элементы.ПолеИтогов85.ТолькоПросмотр
        Или Не Элементы.ПолеДокумента85.ТолькоПросмотр Тогда
        ВызватьИсключение "Изменились итоги или режим просмотра собственных полей";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ВыделитьКонфликт85(Команда)
    Если Остановка85 Или Готово85 <> Истина Или ВыделениеГотово85 <> Ложь
        Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.ВыделитьКонфликт85 Тогда
        Остановка85 = Истина;
        ВызватьИсключение "Форма остановлена, команда чужая или выделение уже выполнено";
    КонецЕсли;
    Попытка
#Если ТолстыйКлиентУправляемоеПриложение Тогда
        ПроверитьБазуНаСервере85();
        ПроверитьТекущееСостояние85();
        График = План85;
        ТочкаПлана = График.Точки.Получить(0);
        СерияПлана = График.Серии.Получить(0);
        ЗначениеПлана = График.ПолучитьЗначение(ТочкаПлана, СерияПлана);
        Интервал = НайтиСвойИнтервал85(ЗначениеПлана, СвоиБрони85.Получить(0).Код);
        КодЗначения = ЗначениеПлана.Идентификатор;
        КодИнтервала = Интервал.Идентификатор;
        Если ТипЗнч(КодЗначения) <> Тип("ИдентификаторЗначенияДиаграммыГанта")
            Или ТипЗнч(КодИнтервала) <> Тип("ИдентификаторИнтервалаДиаграммыГанта") Тогда
            ВызватьИсключение "Нужны идентификаторы, полученные от своего настоящего графика";
        КонецЕсли;
        НайденноеЗначение = График.НайтиЗначениеПоИдентификатору(КодЗначения);
        НайденныйИнтервал = График.НайтиИнтервалПоИдентификатору(КодИнтервала);
        Если НайденноеЗначение = Неопределено Или НайденноеЗначение <> ЗначениеПлана
            Или НайденныйИнтервал = Неопределено Или НайденныйИнтервал <> Интервал
            Или НайденныйИнтервал.Расшифровка <> СвоиБрони85.Получить(0).Код Тогда
            ВызватьИсключение "Идентификатор должен найти именно собственный исходный интервал";
        КонецЕсли;
        Элементы.ПолеПлана85.ТекущееЗначение = КодЗначения;
        Элементы.ПолеПлана85.ТекущийИнтервал = КодИнтервала;
        Если Элементы.ПолеПлана85.ТекущееЗначение <> КодЗначения Или Элементы.ПолеПлана85.ТекущийИнтервал <> КодИнтервала Тогда
            ВызватьИсключение "Текущее значение или интервал не прочитались обратно";
        КонецЕсли;
        Цель = Документ85;
        ОбластьА = Цель.Область(2, 1, 2, 4);
        ОбластьБ = Цель.Область(3, 1, 3, 4);
        Выделения = Цель.ВыделенныеОбласти;
        Выделения.Очистить();
        Выделения = Цель.ВыделенныеОбласти;
        Если Выделения.Количество() <> 0 И Выделения.Количество() <> 1 Тогда
            ВызватьИсключение "После очистки допустимы только пустая коллекция или исходная ячейка R1C1";
        КонецЕсли;
        Если Выделения.Количество() = 1 Тогда
            ИсходнаяОбласть = Выделения.Получить(0);
            Если ТипЗнч(ИсходнаяОбласть) <> Тип("ОбластьЯчеекТабличногоДокумента")
                Или ИсходнаяОбласть.Верх <> 1 Или ИсходнаяОбласть.Низ <> 1
                Или ИсходнаяОбласть.Лево <> 1 Или ИсходнаяОбласть.Право <> 1 Тогда
                ВызватьИсключение "Начальное выделение должно быть ровно своей ячейкой R1C1";
            КонецЕсли;
        КонецЕсли;
        Выделения.Добавить(ОбластьА);
        Выделения = Цель.ВыделенныеОбласти;
        Выделения.Вставить(ОбластьБ, 0);
        Выделения = Цель.ВыделенныеОбласти;
        Если Выделения.Количество() = 3 Тогда
            ПерваяОбласть = Выделения.Получить(0);
            ВтораяОбласть = Выделения.Получить(1);
            ИсходнаяОбласть = Выделения.Получить(2);
            Если ТипЗнч(ПерваяОбласть) <> Тип("ОбластьЯчеекТабличногоДокумента")
                Или ТипЗнч(ВтораяОбласть) <> Тип("ОбластьЯчеекТабличногоДокумента")
                Или ТипЗнч(ИсходнаяОбласть) <> Тип("ОбластьЯчеекТабличногоДокумента")
                Или ПерваяОбласть.Верх <> 3 Или ПерваяОбласть.Низ <> 3
                Или ПерваяОбласть.Лево <> 1 Или ПерваяОбласть.Право <> 4 Тогда
                ВызватьИсключение "Первой должна оставаться своя строка Настройка станка";
            КонецЕсли;
            Если ВтораяОбласть.Верх = 1 И ВтораяОбласть.Низ = 1
                И ВтораяОбласть.Лево = 1 И ВтораяОбласть.Право = 1
                И ИсходнаяОбласть.Верх = 2 И ИсходнаяОбласть.Низ = 2
                И ИсходнаяОбласть.Лево = 1 И ИсходнаяОбласть.Право = 4 Тогда
                ИсходнаяОбласть = ВтораяОбласть;
            ИначеЕсли ИсходнаяОбласть.Верх = 1 И ИсходнаяОбласть.Низ = 1
                И ИсходнаяОбласть.Лево = 1 И ИсходнаяОбласть.Право = 1
                И ВтораяОбласть.Верх = 2 И ВтораяОбласть.Низ = 2
                И ВтораяОбласть.Лево = 1 И ВтораяОбласть.Право = 4 Тогда
                ИсходнаяОбласть = Выделения.Получить(2);
            Иначе
                ВызватьИсключение "Удалять можно только прочитанную R1C1 рядом с двумя своими строками";
            КонецЕсли;
            Выделения.Удалить(ИсходнаяОбласть);
        КонецЕсли;
        ПроверитьВыделение85(Цель, 3, 2);
        Выделения = Цель.ВыделенныеОбласти;
        Выделения.Сдвинуть(ОбластьА, -1);
        ПроверитьВыделение85(Цель, 2, 3);
        ПроверитьТекущееСостояние85();
        ВыделениеГотово85 = Истина;
        Сообщение85 = "Конфликт: 10:00–11:00. Выделены строки Резка образца и Настройка станка; данные и итоги 6/5/1 не изменились.";
#Иначе
        ВызватьИсключение "Изменение выделенных областей здесь разрешено только толстому управляемому клиенту";
#КонецЕсли
    Исключение
        Остановка85 = Истина;
        Сообщение85 = "СТОП: закройте форму, проверьте подготовку и откройте новую форму. Частичное выделение автоматически не откатывается.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

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

Своя CF → ОбщиеМодули → ГантКонфликт85 → Модуль целиком

Неглобальный общий модуль, Сервер Да, ВызовСервера Да, Клиент Нет, ВнешнееСоединение Нет, Привилегированный Нет. Только чтение метаданных и пользователя.

Процедура ПроверитьСвойСтенд85() Экспорт
    Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnGanttConflict15_3d50f63d-7a2c-4dc2-9adf-c4115897ea65\IB"";" Тогда
        ВызватьИсключение "Используйте только свой отдельный файловый стенд и платформу 8.5.1.1343";
    КонецЕсли;
    Если Метаданные.ОбщиеФормы.Количество() <> 1
        Или Метаданные.ОбщиеФормы.Найти("ПланБронирований85") = Неопределено
        Или Метаданные.ОбщиеМодули.Количество() <> 1
        Или Метаданные.ОбщиеМодули.Найти("ГантКонфликт85") = Неопределено
        Или Метаданные.Роли.Количество() <> 1
        Или Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0
        Или Метаданные.Обработки.Количество() <> 0 Или Метаданные.Отчеты.Количество() <> 0
        Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0
        Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Тогда
        ВызватьИсключение "Нужна новая пустая конфигурация с одной своей формой, модулем и ролью";
    КонецЕсли;
    Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
    Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
    Если Текущий = Неопределено Или Пользователи.Количество() <> 1 Тогда
        ВызватьИсключение "Нужен один собственный учебный пользователь";
    КонецЕсли;
    Если Текущий.Имя <> "ГантКонфликт85Ученик"
        Или Текущий.УникальныйИдентификатор <> Пользователи[0].УникальныйИдентификатор Тогда
        ВызватьИсключение "Сеанс принадлежит другому пользователю";
    КонецЕсли;
КонецПроцедуры
Запуск своей формы толстым управляемым клиентом

Своя CF → Модуль управляемого приложения целиком

Модуль управляемого приложения; native ПриНачалеРаботыСистемы без параметров. Неподходящий клиент отказывается; собственная форма открывается только после проверки точной версии и File-пути.

Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентУправляемоеПриложение Тогда
    Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnGanttConflict15_3d50f63d-7a2c-4dc2-9adf-c4115897ea65\IB"";" Тогда
        ВызватьИсключение "Запускайте собственную учебную базу толстым управляемым клиентом";
    КонецЕсли;
    ОткрытьФорму("ОбщаяФорма.ПланБронирований85", , , Истина);
#Иначе
    ВызватьИсключение "Этот урок рассчитан на толстый управляемый клиент Windows";
#КонецЕсли
КонецПроцедуры

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

  1. При создании сервер проверяет свою отдельную базу и пути данных трех полей. При открытии клиент сначала строит и проверяет три новых объекта, затем размещает их в своей форме и перечитывает фактически принятые значения. Повторная инициализация не снимает отказ.

  2. Неизменяемый снимок содержит код, название и две даты каждой из трех строк. 2+3+1=6 часов — сумма бронирований; объединение [09:00,13:00] и [15:00,16:00] дает 5 часов занятости. 6−5=1 час учитывался дважды: 10:00–11:00.

  3. В Ганте серия задает основной цвет, дополнительный цвет перекрытия и штриховку. Приоритет серии включен, приоритет точки выключен, цвета значения и интервалов автоматические. ЦветТекста относится к подписи интервала. Все заданные свойства перечитываются; реальный контраст и пиксели нужно оценить вручную в 1С.

  4. Дневная шкала получает предпочтительный формат ДеньМесяцаДеньНедели и свою сплошную линию делений. Часовая шкала показывает формат HH:mm. ФорматДня действует для единицы День; фактический вид подписи также зависит от масштаба.

  5. Итоговая гистограмма с накоплением содержит три отдельные точки 6/5/1 и одну серию. РежимРедактирования каждой точки НеИспользовать: рассчитанные часы нельзя незаметно заменить перетаскиванием. Числа перечитываются как числа; наличие запрета само по себе не является исполненной проверкой интерфейса.

  6. Коллекция интервалов Ганта неупорядоченная. Свое бронирование Резка образца находится настоящим обходом Для каждого по уникальному коду, а не методом Получить или номером интервала. Команда получает идентификаторы у принятого формой значения и найденного интервала. Поиск по этим идентификаторам должен вернуть именно те же собственные объекты. Затем поле получает ТекущееЗначение и ТекущийИнтервал и читает их обратно. Новых идентификаторов код не конструирует.

  7. После очистки 1С выбирает ячейку R1C1. Код заново получает коллекцию своей таблицы и принимает только отсутствие явных областей либо единственную R1C1. Затем добавляет строку Резка образца и вставляет строку Настройка станка на позицию 0. Если R1C1 осталась третьей областью, все три области сначала строго проверяются и удаляется только эта прочитанная R1C1. Проверяется порядок 3,2; сдвиг Резки на −1 дает 2,3. Каждая проверка заново получает коллекцию у родительского ТабличногоДокумента и читает реальные границы.

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

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

  1. В собственной форме значения часов задач, занятости и перекрытия должны быть 6, 5, 1. В таблице длительности трех строк — числовые 2, 3, 1, а не текстовые строки. Автор подготовил код и статические проверки, но не запускал этот стенд.

  2. Нажмите Выделить конфликт ровно один раз. Две выделенные области охватывают строки 2 и 3, колонки 1–4, в порядке 2,3; сообщение указывает 10:00–11:00. Проверьте выделение и активный интервал вручную; readback коллекции не является доказательством рисунка на экране.

  3. Сравните внешний вид обычных интервалов, перекрытия и промежутка 13:00–15:00. Штриховка и цвета проверяются кодом как настройки; точные оттенки, ширина подписи и отрисовка зависят от клиента и размеров поля.

  4. Повтор команды в той же форме приводит к отказу без сброса Остановка85. Закройте форму и откройте новую после исправления подготовки. Не вызывайте обработчики вручную и не передавайте самодельный идентификатор или чужую команду.

  5. Статические отрицательные проверки должны отвергать неизвестные методы и свойства, запись в readonly ID/границы, неверные слоты Вставить/Сдвинуть, потерю исходной строки или числового readback, тонкий контекст и прямой вызов native обработчика.

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

Один самостоятельный экран объясняет различие 6 часов бронирований, 5 часов занятости и 1 часа перекрытия. Собственные непрозрачные идентификаторы находят нужный интервал, оформление и запрет редактирования перечитываются, выделение таблицы получает проверяемый порядок без изменения исходных данных.

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

  1. График Ганта, его значение, интервал, поле формы и коллекция выделенных областей — разные объекты. ЗначениеДиаграммыГанта — неупорядоченная коллекция; у нее нет метода Получить. Интервалы обходятся оператором Для каждого и сопоставляются с исходными строками по собственному уникальному коду. Дубликат или отсутствие кода приводят к отказу. Идентификатор не создается через Новый и не заменяется номером строки, строкой UUID или номером задачи.

  2. У серии и точки есть отдельный приоритет цвета; при равном приоритете выигрывает серия. Здесь серия имеет приоритет, а цвета значения и интервала автоматические. Проверка свойств не доказывает конечные RGB пиксели или точный внешний вид штриховки.

  3. ФорматДня — предпочтительный формат только дневного элемента шкалы. Подпись зависит от масштаба. Линия создается конструктором с ТипЛинииДиаграммы; ее readonly ТипЛинии и Толщина не присваиваются.

  4. ВыделенныеОбласти у документа readonly как свойство: изменяется полученная собственная коллекция, а не присваивается новая коллекция. После Очистить выделяется R1C1; нельзя без проверки считать коллекцию пустой. Вставить принимает Область первым аргументом, Позиция вторым. Удалить получает только проверенную фактическую область R1C1, когда рядом уже существуют две нужные области; чужая или неожиданная область приводит к отказу. После операций получайте коллекцию у родителя заново.

  5. ГистограммаСНакоплением поддерживает режим редактирования значений; для произвольного другого вида диаграммы это предположение неверно. Этот пример блокирует редактирование рассчитанных точек, а не имитирует native событие перетаскивания.

  6. Часы и подписи принадлежат только своей форме и памяти. Здесь нет записи бронирований, изменения настройки анимации, чтения рабочих документов, сети, генерации PNG или запуска 1С. Имя, путь и код стенда не дают криптографической гарантии происхождения конфигурации.

  7. Начальное размещение трех объектов и последующее выделение не обещаются атомарными. При сбое состояние может быть частично изменено, поэтому установлен sticky-отказ. Исправление выполняется подготовкой стенда и открытием новой формы, не скрытым сбросом флага.

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

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

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

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

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