Тип: Булево. Если установлено Истина, то пространство между интервалами выводятся штриховкой, а не сплошным цветом. Цвет выбирается из свойства ЦветШтриховкиМеждуИнтервалами.
Полное описание, параметры и ограничения
СерияДиаграммыГанта.ШтриховкаМеждуИнтервалами (GanttChartSeries.BetweenIntervalsHatch)
СерияДиаграммыГанта (GanttChartSeries)
ШтриховкаМеждуИнтервалами (BetweenIntervalsHatch)
Доступен, начиная с версии 8.0.
Использование:
Чтение и запись.
Описание:
Тип: Булево.
Если установлено Истина, то пространство между интервалами выводятся штриховкой, а не сплошным цветом. Цвет выбирается из свойства ЦветШтриховкиМеждуИнтервалами.
Доступность:
Тонкий клиент, веб-клиент, сервер, толстый клиент, внешнее соединение.
Использование в версии:
Доступен, начиная с версии 8.0.
Методическая информация
Практические примеры
Бронирования станка: найти перекрытие, показать 6/5/1 и выделить конфликт
Полный учебный экран на своих данных в памяти: оформление Ганта не подменяет расчет часов, настоящие идентификаторы находят свой интервал, а выделение таблицы указывает две конфликтующие записи.
Где и как запускать
Весь MAIN в собственной общей управляемой форме ПланБронирований85. Только Windows, толстый управляемый клиент 8.5.1.1343 своей файловой базы. Создание и проверка путей данных на сервере; построение, численный readback и выделение на клиенте. Никаких записей в базу.
Подготовка
Создайте новую пустую файловую базу платформой 8.5.1.1343 в C:\1c-labs\OwnGanttConflict15_3d50f63d-7a2c-4dc2-9adf-c4115897ea65\IB. Имя конфигурации УчебныйГантКонфликт85; режим совместимости НеИспользовать; основной режим запуска УправляемоеПриложение. Не используйте рабочую конфигурацию или копию с чужими обработчиками. Этот код здесь не запускался.
Создайте одну собственную роль ГантКонфликт85Администратор с полными правами, включая Администрирование, и одного пользователя ГантКонфликт85Ученик с этой ролью. ПолучитьПользователей требует административных прав. В пустой базе нет справочников, документов, обработок, отчетов и регистров; записи и настройки этого урока не сохраняются в базе.
Создайте неглобальный общий модуль ГантКонфликт85: Сервер=Да, ВызовСервера=Да, Клиент=Нет, ВнешнееСоединение=Нет, Привилегированный=Нет, повторное использование возвращаемых значений НеИспользовать. Вставьте supporting GUARD целиком в его модуль. Он только проверяет подготовку и не записывает данные.
Создайте одну общую управляемую форму ПланБронирований85. Реквизиты формы: План85 типа ДиаграммаГанта; Итоги85 типа Диаграмма; Документ85 типа ТабличныйДокумент; ЧасыЗадач85, ЧасыЗанято85, ЧасыПерекрытия85 типа Число(8,2); Сообщение85 типа Строка неограниченной длины. ВосстанавливатьЗначенияПриОткрытии=Нет.
На форме разместите ПолеПлана85 вида ПолеДиаграммыГанта с ПутьКДанным=План85, ПолеИтогов85 вида ПолеДиаграммы с ПутьКДанным=Итоги85 и ПолеДокумента85 вида ПолеТабличногоДокумента с ПутьКДанным=Документ85. Разрешите растягивание этих полей, дайте графику и таблице достаточную ширину. Три численных итога и Сообщение85 выведите отдельными полями только для просмотра.
В командах формы создайте ВыделитьКонфликт85, действие ВыделитьКонфликт85, и собственную кнопку с этой командой. Обработчик собственной команды получает один параметр Команда; это не событие Нажатие с первым параметром Элемент. ПриСозданииНаСервере назначьте одноименной процедуре с Отказ, СтандартнаяОбработка; ПриОткрытии назначьте одноименной процедуре с Отказ. Вставьте MAIN целиком в модуль формы. Переменные MAIN объявлены только на клиенте.
В модуль управляемого приложения вставьте supporting APPLICATION целиком: ПриНачалеРаботыСистемы без параметров открывает собственную общую форму. Обновите конфигурацию базы данных только этого стенда. Запустите его толстым управляемым клиентом Windows и указанным собственным пользователем. Тонкий клиент для этого урока не подходит: изменение коллекции выделенных областей документировано для толстого клиента и сервера.
Три бронирования создаются самим кодом в памяти: Резка образца 09:00–11:00, Настройка станка 10:00–13:00, Обработка детали 15:00–16:00, все 10.10.2026 для одного учебного станка. Табличный макет не требуется: собственный ТабличныйДокумент и шесть настоящих числовых ячеек создаются программно. Не подставляйте реальные задания, файлы или исходный объект из чужой базы.
Сначала сравните численные итоги 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";
#КонецЕсли
КонецПроцедурыРазбор по шагам
При создании сервер проверяет свою отдельную базу и пути данных трех полей. При открытии клиент сначала строит и проверяет три новых объекта, затем размещает их в своей форме и перечитывает фактически принятые значения. Повторная инициализация не снимает отказ.
Неизменяемый снимок содержит код, название и две даты каждой из трех строк. 2+3+1=6 часов — сумма бронирований; объединение [09:00,13:00] и [15:00,16:00] дает 5 часов занятости. 6−5=1 час учитывался дважды: 10:00–11:00.
В Ганте серия задает основной цвет, дополнительный цвет перекрытия и штриховку. Приоритет серии включен, приоритет точки выключен, цвета значения и интервалов автоматические. ЦветТекста относится к подписи интервала. Все заданные свойства перечитываются; реальный контраст и пиксели нужно оценить вручную в 1С.
Дневная шкала получает предпочтительный формат ДеньМесяцаДеньНедели и свою сплошную линию делений. Часовая шкала показывает формат HH:mm. ФорматДня действует для единицы День; фактический вид подписи также зависит от масштаба.
Итоговая гистограмма с накоплением содержит три отдельные точки 6/5/1 и одну серию. РежимРедактирования каждой точки НеИспользовать: рассчитанные часы нельзя незаметно заменить перетаскиванием. Числа перечитываются как числа; наличие запрета само по себе не является исполненной проверкой интерфейса.
Коллекция интервалов Ганта неупорядоченная. Свое бронирование Резка образца находится настоящим обходом Для каждого по уникальному коду, а не методом Получить или номером интервала. Команда получает идентификаторы у принятого формой значения и найденного интервала. Поиск по этим идентификаторам должен вернуть именно те же собственные объекты. Затем поле получает ТекущееЗначение и ТекущийИнтервал и читает их обратно. Новых идентификаторов код не конструирует.
После очистки 1С выбирает ячейку R1C1. Код заново получает коллекцию своей таблицы и принимает только отсутствие явных областей либо единственную R1C1. Затем добавляет строку Резка образца и вставляет строку Настройка станка на позицию 0. Если R1C1 осталась третьей областью, все три области сначала строго проверяются и удаляется только эта прочитанная R1C1. Проверяется порядок 3,2; сдвиг Резки на −1 дает 2,3. Каждая проверка заново получает коллекцию у родительского ТабличногоДокумента и читает реальные границы.
После выделения заново проверяются все три исходные строки, интервалы, защищенные итоговые точки, численные ячейки и числовые реквизиты формы. Изменение выделения не должно менять бронирования или итоги. При любом неизвестном исходе форма остается остановленной; частичные изменения выделения автоматически не откатываются.
Как проверить результат
В собственной форме значения часов задач, занятости и перекрытия должны быть 6, 5, 1. В таблице длительности трех строк — числовые 2, 3, 1, а не текстовые строки. Автор подготовил код и статические проверки, но не запускал этот стенд.
Нажмите Выделить конфликт ровно один раз. Две выделенные области охватывают строки 2 и 3, колонки 1–4, в порядке 2,3; сообщение указывает 10:00–11:00. Проверьте выделение и активный интервал вручную; readback коллекции не является доказательством рисунка на экране.
Сравните внешний вид обычных интервалов, перекрытия и промежутка 13:00–15:00. Штриховка и цвета проверяются кодом как настройки; точные оттенки, ширина подписи и отрисовка зависят от клиента и размеров поля.
Повтор команды в той же форме приводит к отказу без сброса Остановка85. Закройте форму и откройте новую после исправления подготовки. Не вызывайте обработчики вручную и не передавайте самодельный идентификатор или чужую команду.
Статические отрицательные проверки должны отвергать неизвестные методы и свойства, запись в readonly ID/границы, неверные слоты Вставить/Сдвинуть, потерю исходной строки или числового readback, тонкий контекст и прямой вызов native обработчика.
Ожидаемый результат
Один самостоятельный экран объясняет различие 6 часов бронирований, 5 часов занятости и 1 часа перекрытия. Собственные непрозрачные идентификаторы находят нужный интервал, оформление и запрет редактирования перечитываются, выделение таблицы получает проверяемый порядок без изменения исходных данных.
На что обратить внимание
График Ганта, его значение, интервал, поле формы и коллекция выделенных областей — разные объекты. ЗначениеДиаграммыГанта — неупорядоченная коллекция; у нее нет метода Получить. Интервалы обходятся оператором Для каждого и сопоставляются с исходными строками по собственному уникальному коду. Дубликат или отсутствие кода приводят к отказу. Идентификатор не создается через Новый и не заменяется номером строки, строкой UUID или номером задачи.
У серии и точки есть отдельный приоритет цвета; при равном приоритете выигрывает серия. Здесь серия имеет приоритет, а цвета значения и интервала автоматические. Проверка свойств не доказывает конечные RGB пиксели или точный внешний вид штриховки.
ФорматДня — предпочтительный формат только дневного элемента шкалы. Подпись зависит от масштаба. Линия создается конструктором с ТипЛинииДиаграммы; ее readonly ТипЛинии и Толщина не присваиваются.
ВыделенныеОбласти у документа readonly как свойство: изменяется полученная собственная коллекция, а не присваивается новая коллекция. После Очистить выделяется R1C1; нельзя без проверки считать коллекцию пустой. Вставить принимает Область первым аргументом, Позиция вторым. Удалить получает только проверенную фактическую область R1C1, когда рядом уже существуют две нужные области; чужая или неожиданная область приводит к отказу. После операций получайте коллекцию у родителя заново.
ГистограммаСНакоплением поддерживает режим редактирования значений; для произвольного другого вида диаграммы это предположение неверно. Этот пример блокирует редактирование рассчитанных точек, а не имитирует native событие перетаскивания.
Часы и подписи принадлежат только своей форме и памяти. Здесь нет записи бронирований, изменения настройки анимации, чтения рабочих документов, сети, генерации PNG или запуска 1С. Имя, путь и код стенда не дают криптографической гарантии происхождения конфигурации.
Начальное размещение трех объектов и последующее выделение не обещаются атомарными. При сбое состояние может быть частично изменено, поэтому установлен sticky-отказ. Исправление выполняется подготовкой стенда и открытием новой формы, не скрытым сбросом флага.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.