Загрузка данных
Ниже приведен разбор критических ошибок логики, антипаттернов и уязвимостей в представленном коде, а также исправленная и рефакторенная версия модулей.
---
### Анализ ошибок логики и антипаттернов
1. **Серверный вызов `ПоказатьВопрос` и вывод сообщений:**
* В процедуре `ПроизводственныйКалендарьПриИзмененииНаСервере` вызывался `Сообщить()`. Сообщения на сервере — антипаттерн, их лучше возвращать или выводить через `ВызватьИсключение` / `ОбщегоНазначенияКлиентСервер.СообщитьПользователю`.
2. **Работа с динамическими реквизитами формы (`ИзменитьРеквизиты`):**
* **Критическая ошибка:** При удалении сотрудника через `УдалитьКолонкуСотрудников` реквизит удалялся с формы, но динамический элемент формы (`Элементы.ДанныеОВремени`), созданный в `ДобавитьКолонкуСотрудника`, оставался «повисшим». Это приводит к обращению к несуществующему пути данных и падению платформы при перерисовке.
3. **Логика работы с коллекциями и массивами (`Сотрудники[0].Значение`):**
* В форме `РасписаниеРаботы` при `ПоОдномуСотруднику = Истина` происходило прямым ходом обращение к `Сотрудники[0]`. Если массив/список `Сотрудники` пустой — платформа выбрасывала исключение выхода за пределы массива.
4. **Запросы в цикле и неоптимальные операции со строками:**
* Удаление строк через `НайтиСтроки` в цикле `Для Каждого`: при удалении элементов коллекции во время итерации по найденным строкам индекс смещается, либо модифицируется коллекция. В коде применялся правильный шаблон удаления, но код был перегружен ручными циклами обращения `НомерСтроки = Количество() - 1`.
* Избыточный вызов `ОтрисоватьТаблицуДанныеОВремени()` при каждом действии, включая неоптимальную логику генерации таблицы дат на сервере.
5. **Использование контекстных вызовов без необходимости:**
* Функция `ВернутьУникальноеИмяКолонкиСотрудника` и работа с именами: в имени элемента использовались символы `С_` + `Код` + `УИД`. Если у сотрудника код содержит спецсимволы, имя реквизита становится недопустимым.
6. **Предупреждения и асинхронность:**
* В `ПередЗакрытием` не использовался режим работы с асинхронными вызовами стандартной библиотеки.
---
### Рефакторинг 1. Форма «РасписаниеРаботы»
```bsl
&НаКлиенте
Перем ЗакрытиеРазрешено;
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ПоОдномуСотруднику = Истина;
Сотрудники = Новый СписокЗначений;
Если Параметры.Свойство("ДатаСтроки") Тогда
Дата = НачалоДня(Параметры.ДатаСтроки);
КонецЕсли;
Если Параметры.Свойство("НеизменяемаяДата") И Параметры.НеизменяемаяДата Тогда
Элементы.Дата.Доступность = Ложь;
КонецЕсли;
Если Параметры.Свойство("МесяцГрафика") Тогда
МесяцГрафика = Параметры.МесяцГрафика;
КонецЕсли;
Если Параметры.Свойство("ПоОдномуСотруднику") Тогда
ПоОдномуСотруднику = Параметры.ПоОдномуСотруднику;
КонецЕсли;
Если Параметры.Свойство("КоллекцияСотрудников") И ЗначениеЗаполнено(Параметры.КоллекцияСотрудников) Тогда
Сотрудники = Параметры.КоллекцияСотрудников;
КонецЕсли;
Если Параметры.Свойство("ГрафикРаботыСотрудников") Тогда
График = Параметры.ГрафикРаботыСотрудников.Выгрузить();
Если ПоОдномуСотруднику И Сотрудники.Количество() > 0 Тогда
Отбор = Новый Структура("Сотрудник", Сотрудники[0].Значение);
НайденныеСтроки = График.НайтиСтроки(Отбор);
Для Каждого Элемент Из НайденныеСтроки Цикл
ЗаполнитьЗначенияСвойств(ГрафикРаботыПоКонкретномуСотруднику.Добавить(), Элемент);
КонецЦикла;
Иначе
ГрафикРаботыПоКонкретномуСотруднику.Загрузить(График);
КонецЕсли;
КонецЕсли;
Если ПоОдномуСотруднику Тогда
Элементы.НадписьСотрудников.Видимость = Ложь;
Элементы.НадписьСотрудника.Видимость = Истина;
НадписьСотрудника = ?(Сотрудники.Количество() > 0, Сотрудники[0].Значение, "");
Иначе
Элементы.НадписьСотрудников.Видимость = Истина;
Элементы.НадписьСотрудника.Видимость = Ложь;
Элементы.НадписьСотрудников.Заголовок = NStr("ru = 'Задать единое расписание для сотрудников'");
ТекстМассив = Новый Массив;
Для Каждого ЭлементСписка Из Сотрудники Цикл
ТекстМассив.Добавить(Строка(ЭлементСписка.Значение));
КонецЦикла;
Элементы.НадписьСотрудников.Высота = 3;
Элементы.НадписьСотрудников.МаксимальнаяВысота = 3;
Элементы.НадписьСотрудников.АвтоМаксимальнаяВысота = Ложь;
НадписьСотрудников = СтрСоединить(ТекстМассив, Символы.ПС);
КонецЕсли;
УточнитьФорматПолейВремени();
ЗагрузитьИнтервалы();
ЗаполнитьОбщееВремя(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
Если ЗакрытиеРазрешено <> Истина И Модифицированность Тогда
Отказ = Истина;
Обработчик = Новый ОписаниеОповещения("ПослеЗакрытияВопроса", ЭтотОбъект);
ТекстВопроса = NStr("ru = 'Закрыть окно без сохранения изменений?'");
ПоказатьВопрос(Обработчик, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияВопроса(Результат, Параметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ЗакрытиеРазрешено = Истина;
ЗакрытьДаннуюФорму();
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиСобытийЭлементовФормы
&НаКлиенте
Процедура РасписаниеРаботыПриОкончанииРедактирования(Элемент, НоваяСтрока, ОтменаРедактирования)
Если ОтменаРедактирования Или Элемент.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
ТекущаяСтрока = Элемент.ТекущиеДанные;
Если Не ПереходящиеСмены Тогда
ВосстановитьПорядокСтрокКоллекцииПослеРедактирования(РасписаниеРаботы, "ВремяНачала", ТекущаяСтрока);
КонецЕсли;
Если ТекущаяСтрока.ВремяОкончания = Дата(1, 1, 1, 0, 0, 0) Тогда
ТекущаяСтрока.ВремяОкончания = Дата(1, 1, 1, 23, 59, 0);
КонецЕсли;
ЗаполнитьОбщееВремя(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура РасписаниеРаботыПослеУдаления(Элемент)
ЗаполнитьОбщееВремя(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура ДатаПриИзменении(Элемент)
Если Год(Дата) <> Год(МесяцГрафика) Или Месяц(Дата) <> Месяц(МесяцГрафика) Тогда
ТекстСообщения = СтрШаблон(
NStr("ru = 'Выбранная дата не соответствует месяцу графика. Месяц графика: %1 года. Выберите дату из указанного месяца.'"),
Формат(МесяцГрафика, "ДФ='ММММ гггг'")
);
ОбщегоНазначенияКлиент.СообщитьПользователю(ТекстСообщения);
КонецЕсли;
Дата = Дата(Год(МесяцГрафика), Месяц(МесяцГрафика), День(Дата));
ЗагрузитьИнтервалы();
КонецПроцедуры
#КонецОбласти
#Область КомандыФормы
&НаКлиенте
Процедура Записать(Команда)
ОчиститьСообщения();
Если Не ИнтервалыКорректны() Тогда
Возврат;
КонецЕсли;
// Безопасное удаление старых строк по дате
Индекс = ГрафикРаботыПоКонкретномуСотруднику.Количество() - 1;
Пока Индекс >= 0 Цикл
Если ГрафикРаботыПоКонкретномуСотруднику[Индекс].Дата = Дата Тогда
ГрафикРаботыПоКонкретномуСотруднику.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// Заполнение обновленными интервалами
Для Каждого ЭлементСотрудник Из Сотрудники Цикл
Для Каждого СтрокаИнтервала Из РасписаниеРаботы Цикл
НоваяСтрока = ГрафикРаботыПоКонкретномуСотруднику.Добавить();
НоваяСтрока.Сотрудник = ЭлементСотрудник.Значение;
НоваяСтрока.Дата = Дата;
НоваяСтрока.ВремяНачала = СтрокаИнтервала.ВремяНачала;
НоваяСтрока.ВремяОкончания = СтрокаИнтервала.ВремяОкончания;
КонецЦикла;
КонецЦикла;
Результат = Новый Структура;
Если ПоОдномуСотруднику Тогда
Результат.Вставить("ПоОдномуСотруднику", Истина);
КонецЕсли;
Результат.Вставить("КоллекцияСотрудников", Сотрудники);
Результат.Вставить("ДатаРедактирования", Дата);
Результат.Вставить("ГрафикРаботыПоКонкретномуСотруднику", ГрафикРаботыПоКонкретномуСотруднику);
ЗакрытьДаннуюФорму(Результат);
КонецПроцедуры
&НаКлиенте
Процедура Отмена(Команда)
ЗакрытьДаннуюФорму();
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаКлиенте
Функция ИнтервалыКорректны()
Отказ = Ложь;
ОкончаниеДня = Неопределено;
Для ИндексСтроки = 0 По РасписаниеРаботы.Количество() - 1 Цикл
СтрокаРасписания = РасписаниеРаботы[ИндексСтроки];
Если СтрокаРасписания.ВремяНачала > СтрокаРасписания.ВремяОкончания
И ЗначениеЗаполнено(СтрокаРасписания.ВремяОкончания) Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(
NStr("ru = 'Время начала больше времени окончания'"), ,
СтрШаблон("РасписаниеРаботы[%1].ВремяОкончания", ИндексСтроки), ,
Отказ);
КонецЕсли;
Если СтрокаРасписания.ВремяНачала = СтрокаРасписания.ВремяОкончания Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(
NStr("ru = 'Длительность интервала не определена'"), ,
СтрШаблон("РасписаниеРаботы[%1].ВремяОкончания", ИндексСтроки), ,
Отказ);
КонецЕсли;
Если ОкончаниеДня <> Неопределено Тогда
Если ОкончаниеДня > СтрокаРасписания.ВремяНачала Или Не ЗначениеЗаполнено(ОкончаниеДня) Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(
NStr("ru = 'Обнаружены пересекающиеся интервалы'"), ,
СтрШаблон("РасписаниеРаботы[%1].ВремяНачала", ИндексСтроки), ,
Отказ);
КонецЕсли;
КонецЕсли;
ОкончаниеДня = СтрокаРасписания.ВремяОкончания;
КонецЦикла;
Возврат Не Отказ;
КонецФункции
&НаКлиенте
Процедура ЗакрытьДаннуюФорму(Результат = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
Модифицированность = Ложь;
ЗакрытиеРазрешено = Истина;
ОповеститьОВыборе(Результат);
КонецПроцедуры
&НаКлиентеНаСервереБезКонтекста
Процедура ЗаполнитьОбщееВремя(Форма)
Секунд = 0;
Для Каждого Строка Из Форма.РасписаниеРаботы Цикл
Если Строка.ВремяНачала = Строка.ВремяОкончания Тогда
СекундИнтервала = 0;
ИначеЕсли Строка.ВремяНачала < Строка.ВремяОкончания Тогда
СекундИнтервала = Строка.ВремяОкончания - Строка.ВремяНачала;
ИначеЕсли Не ЗначениеЗаполнено(Строка.ВремяОкончания) Тогда
СекундИнтервала = КонецДня(Строка.ВремяОкончания) - Строка.ВремяНачала + 1;
Иначе
ОсновнаяЧасть = КонецДня(Строка.ВремяНачала) - Строка.ВремяНачала + 1;
ПереходящаяЧасть = Строка.ВремяОкончания - НачалоДня(Строка.ВремяОкончания);
СекундИнтервала = ОсновнаяЧасть + ПереходящаяЧасть;
КонецЕсли;
Секунд = Секунд + СекундИнтервала;
КонецЦикла;
Часов = Окр(Секунд / 3600, 1);
Форма.Элементы.ОбщееВремя.Заголовок = СтрШаблон(NStr("ru = 'Всего: %1 ч.'"), Часов);
КонецПроцедуры
&НаСервере
Функция ДвенадцатиЧасовойФорматВремени()
ПредставлениеВремени = Формат(ТекущаяДатаСеанса(), "ДЛФ=T");
Возврат ВРег(Прав(ПредставлениеВремени, 1)) = "M";
КонецФункции
&НаКлиенте
Процедура ВосстановитьПорядокСтрокКоллекцииПослеРедактирования(КоллекцияСтрок, ПолеУпорядочивания, ТекущаяСтрока) Экспорт
Если КоллекцияСтрок.Количество() < 2 Тогда
Возврат;
КонецЕсли;
Если Не ЗначениеЗаполнено(ТекущаяСтрока[ПолеУпорядочивания]) Тогда
Возврат;
КонецЕсли;
ИндексИсходный = КоллекцияСтрок.Индекс(ТекущаяСтрока);
ИндексРезультат = ИндексИсходный;
Направление = 0;
Если ИндексИсходный = 0 Тогда
Направление = 1;
ИначеЕсли ИндексИсходный = КоллекцияСтрок.Количество() - 1 Тогда
Направление = -1;
Иначе
Если КоллекцияСтрок[ИндексИсходный][ПолеУпорядочивания] > КоллекцияСтрок[ИндексРезультат + 1][ПолеУпорядочивания] Тогда
Направление = 1;
ИначеЕсли КоллекцияСтрок[ИндексИсходный][ПолеУпорядочивания] < КоллекцияСтрок[ИндексРезультат - 1][ПолеУпорядочивания] Тогда
Направление = -1;
КонецЕсли;
КонецЕсли;
Если Направление = 0 Тогда
Возврат;
КонецЕсли;
Если Направление = 1 Тогда
Пока ИндексРезультат < КоллекцияСтрок.Количество() - 1
И КоллекцияСтрок[ИндексИсходный][ПолеУпорядочивания] > КоллекцияСтрок[ИндексРезультат + 1][ПолеУпорядочивания] Цикл
ИндексРезультат = ИндексРезультат + 1;
КонецЦикла;
Иначе
Пока ИндексРезультат > 0
И КоллекцияСтрок[ИндексИсходный][ПолеУпорядочивания] < КоллекцияСтрок[ИндексРезультат - 1][ПолеУпорядочивания] Цикл
ИндексРезультат = ИндексРезультат - 1;
КонецЦикла;
КонецЕсли;
Если ИндексИсходный <> ИндексРезультат Тогда
КоллекцияСтрок.Сдвинуть(ИндексИсходный, ИндексРезультат - ИндексИсходный);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьИнтервалы()
РасписаниеРаботы.Очистить();
Если ПоОдномуСотруднику Тогда
Отбор = Новый Структура("Дата", Дата);
НайденныеСтроки = ГрафикРаботыПоКонкретномуСотруднику.НайтиСтроки(Отбор);
Для Каждого СтрокаГрафика Из НайденныеСтроки Цикл
НоваяСтрока = РасписаниеРаботы.Добавить();
НоваяСтрока.ВремяНачала = СтрокаГрафика.ВремяНачала;
НоваяСтрока.ВремяОкончания = СтрокаГрафика.ВремяОкончания;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура УточнитьФорматПолейВремени()
Есть12Часов = ДвенадцатиЧасовойФорматВремени();
ФорматВремени = ?(Есть12Часов, NStr("ru = 'ДФ=''чч:мм вв''; ДП='"), NStr("ru = 'ДФ=ЧЧ:мм; ДП='"));
ФорматВремениРедактирования = ?(Есть12Часов, NStr("ru = 'ДФ=''чч:мм вв'''"), NStr("ru = 'ДФ=ЧЧ:мм'"));
Элементы.РасписаниеРаботыВремяНачала.Формат = ФорматВремени;
Элементы.РасписаниеРаботыВремяНачала.ФорматРедактирования = ФорматВремениРедактирования;
Элементы.РасписаниеРаботыВремяОкончания.Формат = ФорматВремени;
Элементы.РасписаниеРаботыВремяОкончания.ФорматРедактирования = ФорматВремениРедактирования;
КонецПроцедуры
#КонецОбласти
```
---
### Рефакторинг 2. Основная форма управления графиками
```bsl
#Область ОбработчикиСобытийФормы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Не ЗначениеЗаполнено(Объект.Ссылка) Тогда
Если ПараметрыСеанса.Свойство("ТекущийПользователь") И ЗначениеЗаполнено(ПараметрыСеанса.ТекущийПользователь) Тогда
Объект.Организация = ПараметрыСеанса.ТекущийПользователь.Организация;
КонецЕсли;
Объект.МесяцГрафика = НачалоМесяца(ТекущаяДатаСеанса());
КонецЕсли;
МесяцРегистрацииСтрокой = Формат(Объект.МесяцГрафика, "ДФ='ММММ гггг'");
ЗаполнитьДанныеОВремени();
Элементы.ЗаполнитьПоПроизводственномуГрафику.Заголовок = NStr("ru = 'Заполнить по производственному графику для всех сотрудников'");
Если Не ЗначениеЗаполнено(Объект.ПроизводственныйКалендарь) Тогда
Объект.ПроизводственныйКалендарь = ПолучитьКалендарь(Год(Объект.МесяцГрафика));
КонецЕсли;
ОбновитьДоступностьЭлементовИЗагрузитьГрафик();
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиСобытийЭлементовФормы
&НаКлиенте
Процедура МесяцРегистрацииСтрокойАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
Если Не ПустаяСтрока(Текст) Тогда
ДанныеВыбора = ДатаКакМесяцПодобратьДатуПоТексту(Текст);
СтандартнаяОбработка = Ложь;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура МесяцРегистрацииСтрокойНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
ЗадатьВопросПриИзмененииДаты();
КонецПроцедуры
&НаКлиенте
Процедура ПроизводственныйКалендарьНачалоВыбора(Элемент, ДанныеВыбора, ВыборДобавлением, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ГодВыбора = Год(Объект.МесяцГрафика);
СписокКалендарей = ПолучитьСписокЗначенийКалендарей(ГодВыбора);
Если СписокКалендарей.Количество() = 0 Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(СтрШаблон(NStr("ru = 'Отсутствует производственный календарь за %1 год.'"), Формат(ГодВыбора, "ЧГ=0")));
Возврат;
КонецЕсли;
ВыбранныйЭлемент = СписокКалендарей.ВыбратьЭлемент(СтрШаблон(NStr("ru = 'Выберите производственный календарь за %1'"), Формат(ГодВыбора, "ЧГ=0")));
Если ВыбранныйЭлемент <> Неопределено Тогда
Объект.ПроизводственныйКалендарь = ВыбранныйЭлемент.Значение;
ПроизводственныйКалендарьПриИзмененииНаКлиенте();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ДанныеОВремениПриАктивизацииСтроки(Элемент)
ТекущаяСтрокаПерем = Элемент.ТекущиеДанные;
СтруктураСтроки = Неопределено;
Если ТекущаяСтрокаПерем <> Неопределено И ТекущаяСтрокаПерем.Свойство("Дата") Тогда
СтруктураСтроки = Новый Структура("Дата", ТекущаяСтрокаПерем.Дата);
Для Каждого Сотрудник Из Объект.Сотрудники Цикл
ИмяРеквизита = ПолучитьИмяКолонкиПоСотруднику(Сотрудник.Сотрудник);
Если Не ПустаяСтрока(ИмяРеквизита) И ТекущаяСтрокаПерем.Свойство(ИмяРеквизита) Тогда
СтруктураСтроки.Вставить(ИмяРеквизита, ТекущаяСтрокаПерем[ИмяРеквизита]);
КонецЕсли;
КонецЦикла;
КонецЕсли;
ТекущаяСтрокаТаблицыДанныеОВремени = СтруктураСтроки;
КонецПроцедуры
&НаКлиенте
Процедура ДанныеОВремениВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
ТекущаяСтрока = Элемент.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
Сотрудник = ПолучитьСотрудникаПоИмениКолонки(Поле.Имя);
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("ДатаСтроки", ТекущаяСтрока.Дата);
ПараметрыФормы.Вставить("НеизменяемаяДата", Истина);
ПараметрыФормы.Вставить("МесяцГрафика", Объект.МесяцГрафика);
ПараметрыФормы.Вставить("ГрафикРаботыСотрудников", Объект.ГрафикРаботыСотрудников);
СписокСотрудников = Новый СписокЗначений;
Если ЗначениеЗаполнено(Сотрудник) Тогда
ПараметрыФормы.Вставить("ПоОдномуСотруднику", Истина);
СписокСотрудников.Добавить(Сотрудник);
Иначе
Для Каждого СтрокаСотрудник Из Объект.Сотрудники Цикл
СписокСотрудников.Добавить(СтрокаСотрудник.Сотрудник);
КонецЦикла;
КонецЕсли;
ПараметрыФормы.Вставить("КоллекцияСотрудников", СписокСотрудников);
Оповещение = Новый ОписаниеОповещения("ПослеЗаписиДанныхФормыРасписаниеРаботы", ЭтотОбъект);
ОткрытьФорму("Документ.УстановкаГрафиковРаботы.Форма.РасписаниеРаботы", ПараметрыФормы, ЭтотОбъект, , , , Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
Если Элемент.ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
Сотрудник = Элемент.ТекущиеДанные.Сотрудник;
ПараметрыФормы = Новый Структура;
ДатаСтроки = НачалоМесяца(Объект.МесяцГрафика);
Если ТекущаяСтрокаТаблицыДанныеОВремени <> Неопределено
И ТекущаяСтрокаТаблицыДанныеОВремени.Свойство("Дата")
И ЗначениеЗаполнено(ТекущаяСтрокаТаблицыДанныеОВремени.Дата) Тогда
ДатаСтроки = ТекущаяСтрокаТаблицыДанныеОВремени.Дата;
КонецЕсли;
ПараметрыФормы.Вставить("ДатаСтроки", ДатаСтроки);
ПараметрыФормы.Вставить("МесяцГрафика", Объект.МесяцГрафика);
ПараметрыФормы.Вставить("ГрафикРаботыСотрудников", Объект.ГрафикРаботыСотрудников);
СписокСотрудников = Новый СписокЗначений;
СписокСотрудников.Добавить(Сотрудник);
ПараметрыФормы.Вставить("КоллекцияСотрудников", СписокСотрудников);
ПараметрыФормы.Вставить("ПоОдномуСотруднику", Истина);
Оповещение = Новый ОписаниеОповещения("ПослеЗаписиДанныхФормыРасписаниеРаботы", ЭтотОбъект);
ОткрытьФорму("Документ.УстановкаГрафиковРаботы.Форма.РасписаниеРаботы", ПараметрыФормы, ЭтотОбъект, , , , Оповещение, РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиПриАктивизацииСтроки(Элемент)
ТекущаяСтрокаПерем = Элемент.ТекущиеДанные;
ТекущийСотрудникТаблицыСотрудник = Неопределено;
ДоступностьКнопок = Ложь;
Если ТекущаяСтрокаПерем <> Неопределено И ТекущаяСтрокаПерем.Свойство("Сотрудник")
И ЗначениеЗаполнено(ТекущаяСтрокаПерем.Сотрудник) Тогда
ТекущийСотрудникТаблицыСотрудник = ТекущаяСтрокаПерем.Сотрудник;
ДоступностьКнопок = Истина;
КонецЕсли;
Попытка
Элементы.СотрудникиКонтекстноеМенюЗаполнитьПоПроизводственномуГрафикуСотрудника.Доступность = ДоступностьКнопок;
Элементы.СотрудникиЗаполнитьПоПроизводственномуГрафикуСотрудника.Доступность = ДоступностьКнопок;
Исключение
// Пропуск ошибки при отсутствии кнопок в командной панели
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиПередУдалением(Элемент, Отказ)
СтрокаСотрудника = Элементы.Сотрудники.ТекущиеДанные;
Если СтрокаСотрудника <> Неопределено Тогда
УдалитьСотрудникаКомплексно(СтрокаСотрудника.Сотрудник);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиПослеУдаления(Элемент)
Если Объект.Сотрудники.Количество() = 0 Тогда
Объект.ГрафикРаботыСотрудников.Очистить();
Объект.СоответствиеКолонокСотрудников.Очистить();
КонецЕсли;
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
&НаКлиенте
Процедура СотрудникиОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Если ТипЗнч(ВыбранноеЗначение) <> Тип("Массив") Тогда
Возврат;
КонецЕсли;
Для Каждого Сотрудник Из ВыбранноеЗначение Цикл
Отбор = Новый Структура("Сотрудник", Сотрудник);
Если Объект.Сотрудники.НайтиСтроки(Отбор).Количество() > 0 Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = Объект.Сотрудники.Добавить();
НоваяСтрока.Сотрудник = Сотрудник;
ДобавитьКолонкуСотрудника(Сотрудник);
КонецЦикла;
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
#КонецОбласти
#Область КомандыФормы
&НаКлиенте
Процедура ЗаполнитьПоПроизводственномуГрафику(Команда)
ЗаполнитьПоПроизводственномуГрафикуНаСервере();
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьПоПроизводственномуГрафикуСотрудника(Команда)
Если ЗначениеЗаполнено(ТекущийСотрудникТаблицыСотрудник) Тогда
ЗаполнитьПоПроизводственномуГрафикуСотрудникаНаСервере(ТекущийСотрудникТаблицыСотрудник);
ОтрисоватьТаблицуДанныеОВремени();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура Подбор(Команда)
Если Не ЗначениеЗаполнено(Объект.Подразделение) Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(NStr("ru = 'Заполните подразделение'"));
Возврат;
КонецЕсли;
ПараметрыФормы = Новый Структура("Подразделение", Объект.Подразделение);
ОткрытьФорму("Справочник.Сотрудники.Форма.ФормаПодбора", ПараметрыФормы, Элементы.Сотрудники);
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
&НаКлиенте
Процедура ПроизводственныйКалендарьПриИзмененииНаКлиенте()
ЗагрузитьТаблицуПроизводственногоКалендаря();
УправлениеДоступностьюЭлементов();
КонецПроцедуры
&НаКлиенте
Процедура УправлениеДоступностьюЭлементов()
Заполнено = ЗначениеЗаполнено(Объект.ПроизводственныйКалендарь);
Элементы.Сотрудники.Доступность = Заполнено;
Элементы.ДанныеОВремени.Доступность = Заполнено;
Элементы.ЗаполнитьПоПроизводственномуГрафику.Доступность = Заполнено;
Если Не Заполнено Тогда
ОбщегоНазначенияКлиент.СообщитьПользователю(NStr("ru = 'Заполните производственный календарь'"));
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ОбновитьДоступностьЭлементовИЗагрузитьГрафик()
Заполнено = ЗначениеЗаполнено(Объект.ПроизводственныйКалендарь);
Элементы.Сотрудники.Доступность = Заполнено;
Элементы.ДанныеОВремени.Доступность = Заполнено;
Элементы.ЗаполнитьПоПроизводственномуГрафику.Доступность = Заполнено;
Если Заполнено Тогда
ЗагрузитьТаблицуПроизводственногоКалендаря();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ЗадатьВопросПриИзмененииДаты()
Обработчик = Новый ОписаниеОповещения("ОчиститьРасписаниеСотрудниковПослеЗакрытияВопроса", ЭтотОбъект);
ТекстВопроса = NStr("ru = 'При изменении месяца будет утеряно нынешнее расписание сотрудников. Продолжить?'");
ПоказатьВопрос(Обработчик, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ОчиститьРасписаниеСотрудниковПослеЗакрытияВопроса(Результат, Параметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ПродолжитьИзменениеМесяцаГрафика();
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПродолжитьИзменениеМесяцаГрафика()
Оповещение = Новый ОписаниеОповещения("ПослеВыбораПериода", ЭтотОбъект);
ПараметрыПериода = Новый Структура("УстановленныйПериод, РежимВыбораПериода", Объект.МесяцГрафика, "Месяц");
ОткрытьФорму("Документ.УстановкаГрафиковРаботы.Форма.ФормаВыборПериода", ПараметрыПериода, ЭтотОбъект, , , , Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораПериода(Результат, ДопПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
КонецЕсли;
Объект.МесяцГрафика = Результат;
МесяцРегистрацииСтрокой = Формат(Объект.МесяцГрафика, "ДФ='ММММ гггг'");
МесяцРегистрацииСтрокойПриИзменении();
КонецПроцедуры
&НаКлиенте
Процедура МесяцРегистрацииСтрокойПриИзменении()
ОчиститьГрафикРаботыСотрудников();
ЗаполнитьПроизводственныйКалендарь(Год(Объект.МесяцГрафика));
ПроизводственныйКалендарьПриИзмененииНаКлиенте();
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьПроизводственныйКалендарь(Год)
Объект.ПроизводственныйКалендарь = Неопределено;
ПроизводственныйГрафик.Очистить();
Календарь = ПолучитьКалендарь(Год);
Если Календарь <> Неопределено Тогда
Объект.ПроизводственныйКалендарь = Календарь;
Иначе
ОбщегоНазначенияКлиент.СообщитьПользователю(СтрШаблон(NStr("ru = 'Отсутствует производственный календарь за %1 год.'"), Формат(Год, "ЧГ=0")));
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗаписиДанныхФормыРасписаниеРаботы(Результат, ДопПараметры) Экспорт
Если Результат <> Неопределено Тогда
ПослеЗаписиДанныхФормыРасписаниеРаботыНаСервере(Результат, ДопПараметры);
КонецЕсли;
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
&НаСервере
Процедура ПослеЗаписиДанныхФормыРасписаниеРаботыНаСервере(Результат, ДопПараметры)
КоллекцияСотрудников = Результат.КоллекцияСотрудников;
ГрафикРаботыПоКонкретномуСотруднику = Результат.ГрафикРаботыПоКонкретномуСотруднику;
ДатаРедактирования = Результат.ДатаРедактирования;
Для Индекс = 0 По КоллекцияСотрудников.Количество() - 1 Цикл
Отбор = Новый Структура("Сотрудник, Дата", КоллекцияСотрудников[Индекс].Значение, ДатаРедактирования);
НайденныеСтроки = Объект.ГрафикРаботыСотрудников.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
Объект.ГрафикРаботыСотрудников.Удалить(Строка);
КонецЦикла;
КонецЦикла;
Отбор = Новый Структура("Дата", ДатаРедактирования);
НайденныеСтроки = ГрафикРаботыПоКонкретномуСотруднику.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
НоваяСтрока = Объект.ГрафикРаботыСотрудников.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ОчиститьГрафикРаботыСотрудников()
Объект.ГрафикРаботыСотрудников.Очистить();
ОтрисоватьТаблицуДанныеОВремени();
КонецПроцедуры
&НаСервере
Процедура ДобавитьКолонкуСотрудника(Сотрудник)
Если Не ЗначениеЗаполнено(Сотрудник) Тогда
Возврат;
КонецЕсли;
ИмяКолонки = ВернутьУникальноеИмяКолонкиСотрудника(Сотрудник);
РекРодителя = ПолучитьРеквизиты("ДанныеОВремени");
НаличиеРеквизита = Ложь;
Для Каждого ТекРек Из РекРодителя Цикл
Если ТекРек.Имя = ИмяКолонки Тогда
НаличиеРеквизита = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не НаличиеРеквизита Тогда
ЗаголовокКолонки = ВернутьФамилиюИИнициалы(Строка(Сотрудник));
НовыеРеквизиты = Новый Массив;
НовыйРеквизит = Новый РеквизитФормы(ИмяКолонки, Новый ОписаниеТипов("Строка"), "ДанныеОВремени", ЗаголовокКолонки, Ложь);
НовыеРеквизиты.Добавить(НовыйРеквизит);
ИзменитьРеквизиты(НовыеРеквизиты);
ТаблицаФормы = Элементы.ДанныеОВремени;
НоваяКолонка = Элементы.Добавить(ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
НоваяКолонка.Вид = ВидПоляФормы.ПолеВвода;
НоваяКолонка.ПутьКДанным = "ДанныеОВремени." + ИмяКолонки;
НоваяКолонка.Заголовок = ЗаголовокКолонки;
КонецЕсли;
ДобавитьСоответствиеИмениКолонкиИСотрудника(ИмяКолонки, Сотрудник);
ЗадатьФорматТЧДанныеОВремени();
КонецПроцедуры
&НаСервере
Процедура УдалитьСотрудникаКомплексно(Сотрудник)
Если Не ЗначениеЗаполнено(Сотрудник) Тогда
Возврат;
КонецЕсли;
ИмяКолонки = ВернутьУникальноеИмяКолонкиСотрудника(Сотрудник);
// 1. Удаление элемента формы во избежание ошибки «Не найден путь к данным»
ЭлементФормы = Элементы.Найти(ИмяКолонки);
Если ЭлементФормы <> Неопределено Тогда
Элементы.Удалить(ЭлементФормы);
КонецЕсли;
// 2. Удаление реквизита формы
ИмяУдаляемогоРеквизита = "ДанныеОВремени." + ИмяКолонки;
МассивУдаляемыхРеквизитов = Новый Массив;
РекРодителя = ПолучитьРеквизиты("ДанныеОВремени");
Для Каждого ТекРек Из РекРодителя Цикл
Если ТекРек.Имя = ИмяКолонки Тогда
МассивУдаляемыхРеквизитов.Добавить(ИмяУдаляемогоРеквизита);
Прервать;
КонецЕсли;
КонецЦикла;
Если МассивУдаляемыхРеквизитов.Количество() > 0 Тогда
ИзменитьРеквизиты(, МассивУдаляемыхРеквизитов);
КонецЕсли;
// 3. Безопасное удаление из ТЧ ГрафикРаботыСотрудников
Индекс = Объект.ГрафикРаботыСотрудников.Количество() - 1;
Пока Индекс >= 0 Цикл
Если Объект.ГрафикРаботыСотрудников[Индекс].Сотрудник = Сотрудник Тогда
Объект.ГрафикРаботыСотрудников.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
// 4. Безопасное удаление из ТЧ СоответствиеКолонокСотрудников
Индекс = Объект.СоответствиеКолонокСотрудников.Количество() - 1;
Пока Индекс >= 0 Цикл
Если Объект.СоответствиеКолонокСотрудников[Индекс].Сотрудник = Сотрудник Тогда
Объект.СоответствиеКолонокСотрудников.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ВернутьФамилиюИИнициалы(Знач ФИО)
ФИО = СокрЛП(ФИО);
МассивСлов = СтрРазделить(ФИО, " ", Ложь);
Если МассивСлов.Количество() >= 3 Тогда
Возврат СтрШаблон("%1 %2.%3.", МассивСлов[0], Лев(МассивСлов[1], 1), Лев(МассивСлов[2], 1));
Иначе
Возврат ФИО;
КонецЕсли;
КонецФункции
&НаСервере
Функция ВернутьУникальноеИмяКолонкиСотрудника(Сотрудник)
ГуидСтрокой = СтрЗаменить(Строка(Сотрудник.УникальныйИдентификатор()), "-", "_");
Возврат СтрШаблон("С_Колонка_%1", ГуидСтрокой);
КонецФункции
&НаСервере
Функция ПолучитьИмяКолонкиПоСотруднику(Сотрудник)
Отбор = Новый Структура("Сотрудник", Сотрудник);
НайденныеСтроки = Объект.СоответствиеКолонокСотрудников.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
Возврат НайденныеСтроки[0].ИмяКолонки;
КонецЕсли;
Возврат "";
КонецФункции
&НаСервере
Функция ПолучитьСотрудникаПоИмениКолонки(ИмяКолонки)
Отбор = Новый Структура("ИмяКолонки", ИмяКолонки);
НайденныеСтроки = Объект.СоответствиеКолонокСотрудников.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() > 0 Тогда
Возврат НайденныеСтроки[0].Сотрудник;
КонецЕсли;
Возврат Справочники.Сотрудники.ПустаяСсылка();
КонецФункции
&НаСервере
Процедура ДобавитьСоответствиеИмениКолонкиИСотрудника(ИмяКолонки, Сотрудник)
Отбор = Новый Структура("Сотрудник, ИмяКолонки", Сотрудник, ИмяКолонки);
НайденныеСтроки = Объект.СоответствиеКолонокСотрудников.НайтиСтроки(Отбор);
Если НайденныеСтроки.Количество() = 0 Тогда
НовоеСоответствие = Объект.СоответствиеКолонокСотрудников.Добавить();
НовоеСоответствие.ИмяКолонки = ИмяКолонки;
НовоеСоответствие.Сотрудник = Сотрудник;
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьТаблицуПроизводственногоКалендаря()
ТекущаяДатаГрафика = Объект.МесяцГрафика;
КалендарьСсылка = Объект.ПроизводственныйКалендарь;
Если Не ЗначениеЗаполнено(КалендарьСсылка) Тогда
Возврат;
КонецЕсли;
ПроизводственныйГрафик.Очистить();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеПК.Дата КАК Дата,
| ДЕНЬНЕДЕЛИ(ДанныеПК.Дата) КАК ДеньНедели,
| ДанныеПК.ВидДня КАК ВидДня,
| ДанныеПК.ПроизводственныйКалендарь.Ссылка КАК ПроизводственныйКалендарьСсылка
|ПОМЕСТИТЬ ВТ_ДанныеПК
|ИЗ
| РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПК
|ГДЕ
| ДанныеПК.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&Дата, МЕСЯЦ) И КОНЕЦПЕРИОДА(&Дата, МЕСЯЦ)
| И ДанныеПК.ПроизводственныйКалендарь = &ПроизводственныйКалендарь
|;
|
|ВЫБРАТЬ
| ЕСТЬНЕОПРЕДЕЛЕНО(Расписание.ВремяНачала, НЕОПРЕДЕЛЕНО) КАК ВремяНачала,
| ЕСТЬНЕОПРЕДЕЛЕНО(Расписание.ВремяОкончания, НЕОПРЕДЕЛЕНО) КАК ВремяОкончания,
| ВТ_ДанныеПК.Дата КАК Дата,
| ВТ_ДанныеПК.ДеньНедели КАК ДеньНедели,
| ВТ_ДанныеПК.ВидДня КАК ВидДня
|ИЗ
| ВТ_ДанныеПК КАК ВТ_ДанныеПК
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ГрафикРаботы.РасписаниеРаботы КАК Расписание
| ПО ВТ_ДанныеПК.ПроизводственныйКалендарьСсылка = Расписание.Ссылка
| И ВТ_ДанныеПК.ДеньНедели = Расписание.НомерДня
|УПОРЯДОЧИТЬ ПО
| Дата,
| ВремяНачала";
Запрос.УстановитьПараметр("Дата", ТекущаяДатаГрафика);
Запрос.УстановитьПараметр("ПроизводственныйКалендарь", КалендарьСсылка);
ПроизводственныйГрафик.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьДанныеОВремени()
Для Каждого СтрСотрудник Из Объект.Сотрудники Цикл
ДобавитьКолонкуСотрудника(СтрСотрудник.Сотрудник);
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьПоПроизводственномуГрафикуНаСервере()
Если Объект.Сотрудники.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Объект.ГрафикРаботыСотрудников.Очистить();
Для Каждого СтрокаСотрудник Из Объект.Сотрудники Цикл
Для Каждого СтрокаТЧ Из ПроизводственныйГрафик Цикл
Если ЗначениеЗаполнено(СтрокаТЧ.ВремяНачала) И ЗначениеЗаполнено(СтрокаТЧ.ВремяОкончания) Тогда
НоваяСтрока = Объект.ГрафикРаботыСотрудников.Добавить();
НоваяСтрока.Сотрудник = СтрокаСотрудник.Сотрудник;
НоваяСтрока.ВремяНачала = СтрокаТЧ.ВремяНачала;
НоваяСтрока.ВремяОкончания = ?(СтрокаТЧ.ВремяОкончания = Дата(1, 1, 1, 0, 0, 0), Дата(1, 1, 1, 23, 59, 0), СтрокаТЧ.ВремяОкончания);
НоваяСтрока.Дата = СтрокаТЧ.Дата;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьПоПроизводственномуГрафикуСотрудникаНаСервере(Сотрудник)
Индекс = Объект.ГрафикРаботыСотрудников.Количество() - 1;
Пока Индекс >= 0 Цикл
Если Объект.ГрафикРаботыСотрудников[Индекс].Сотрудник = Сотрудник Тогда
Объект.ГрафикРаботыСотрудников.Удалить(Индекс);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Для Каждого СтрокаТЧ Из ПроизводственныйГрафик Цикл
Если ЗначениеЗаполнено(СтрокаТЧ.ВремяНачала) И ЗначениеЗаполнено(СтрокаТЧ.ВремяОкончания) Тогда
НоваяСтрока = Объект.ГрафикРаботыСотрудников.Добавить();
НоваяСтрока.Сотрудник = Сотрудник;
НоваяСтрока.ВремяНачала = СтрокаТЧ.ВремяНачала;
НоваяСтрока.ВремяОкончания = ?(СтрокаТЧ.ВремяОкончания = Дата(1, 1, 1, 0, 0, 0), Дата(1, 1, 1, 23, 59, 0), СтрокаТЧ.ВремяОкончания);
НоваяСтрока.Дата = СтрокаТЧ.Дата;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
&НаСервере
Процедура ОтрисоватьТаблицуДанныеОВремени()
ТЗГрафикРаботыСотрудников = Объект.ГрафикРаботыСотрудников.Выгрузить();
Если ТЗГрафикРаботыСотрудников.Количество() = 0 И Объект.Сотрудники.Количество() = 0 Тогда
ДанныеОВремени.Очистить();
Возврат;
КонецЕсли;
СохраненнаяДата = Неопределено;
Если ТекущаяСтрокаТаблицыДанныеОВремени <> Неопределено
И ТипЗнч(ТекущаяСтрокаТаблицыДанныеОВремени) = Тип("Структура")
И ТекущаяСтрокаТаблицыДанныеОВремени.Свойство("Дата") Тогда
СохраненнаяДата = ТекущаяСтрокаТаблицыДанныеОВремени.Дата;
КонецЕсли;
ТЗТаблицаДатМесяца = Новый ТаблицаЗначений;
ТЗТаблицаДатМесяца.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ДатаДляТаблицы = НачалоМесяца(Объект.МесяцГрафика);
ДатаСледующегоМесяца = НачалоМесяца(ДобавитьМесяц(ДатаДляТаблицы, 1));
Пока ДатаДляТаблицы < ДатаСледующегоМесяца Цикл
ТЗТаблицаДатМесяца.Добавить().Дата = ДатаДляТаблицы;
ДатаДляТаблицы = ДатаДляТаблицы + 86400; // Прибавление 1 дня в секундах
КонецЦикла;
ДанныеОВремени.Очистить();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаДатМесяца.Дата КАК Дата
|ПОМЕСТИТЬ ВТТаблицаДатМесяца
|ИЗ
| &ТаблицаДатМесяца КАК ТаблицаДатМесяца
|;
|
|ВЫБРАТЬ
| ТаблицаСотрудников.Сотрудник КАК Сотрудник,
| ТаблицаСотрудников.ВремяНачала КАК ВремяНачала,
| ТаблицаСотрудников.ВремяОкончания КАК ВремяОкончания,
| ТаблицаСотрудников.Дата КАК Дата
|ПОМЕСТИТЬ ВТТаблицаСотрудников
|ИЗ
| &ТаблицаСотрудников КАК ТаблицаСотрудников
|;
|
|ВЫБРАТЬ
| ВТТаблицаДатМесяца.Дата КАК Дата,
| ВТТаблицаСотрудников.Сотрудник КАК Сотрудник,
| ВТТаблицаСотрудников.ВремяНачала КАК ВремяНачала,
| ВТТаблицаСотрудников.ВремяОкончания КАК ВремяОкончания
|ИЗ
| ВТТаблицаДатМесяца КАК ВТТаблицаДатМесяца
| ЛЕВОЕ СОЕДИНЕНИЕ ВТТаблицаСотрудников КАК ВТТаблицаСотрудников
| ПО ВТТаблицаДатМесяца.Дата = ВТТаблицаСотрудников.Дата
|УПОРЯДОЧИТЬ ПО
| Дата,
| Сотрудник,
| ВремяНачала";
Запрос.УстановитьПараметр("ТаблицаСотрудников", ТЗГрафикРаботыСотрудников);
Запрос.УстановитьПараметр("ТаблицаДатМесяца", ТЗТаблицаДатМесяца);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
СтрокиПоДатам = Новый Соответствие;
Пока Выборка.Следующий() Цикл
ТекДата = Выборка.Дата;
ДниМесяцаСтр = Формат(ТекДата, "ДФ='dd ddd'");
МассивСтрок = СтрокиПоДатам[ТекДата];
Если МассивСтрок = Неопределено Тогда
МассивСтрок = Новый Массив;
НоваяСтрока = ДанныеОВремени.Добавить();
НоваяСтрока.Дата = ТекДата;
НоваяСтрока.ДниМесяца = ДниМесяцаСтр;
Отбор = Новый Структура("Дата", ТекДата);
НайденныеСтроки = ПроизводственныйГрафик.НайтиСтроки(Отбор);
НоваяСтрока.ВидДня = ?(НайденныеСтроки.Количество() > 0, НайденныеСтроки[0].ВидДня, Неопределено);
МассивСтрок.Добавить(НоваяСтрока);
СтрокиПоДатам.Вставить(ТекДата, МассивСтрок);
КонецЕсли;
Если Не ЗначениеЗаполнено(Выборка.Сотрудник) Тогда
Продолжить;
КонецЕсли;
Сотрудник = Выборка.Сотрудник;
ИмяКолонки = ПолучитьИмяКолонкиПоСотруднику(Сотрудник);
Если ПустаяСтрока(ИмяКолонки) Тогда
Продолжить;
КонецЕсли;
Интервал = СтрШаблон("%1 - %2 (%3)",
?(ЗначениеЗаполнено(Выборка.ВремяНачала), Формат(Выборка.ВремяНачала, "ДФ=ЧЧ:мм"), "00:00"),
?(ЗначениеЗаполнено(Выборка.ВремяОкончания), Формат(Выборка.ВремяОкончания, "ДФ=ЧЧ:мм"), "00:00"),
ДниМесяцаСтр);
СтрокаДляЗаписи = Неопределено;
Для Каждого СтрокаДанных Из МассивСтрок Цикл
Если ПустаяСтрока(СтрокаДанных[ИмяКолонки]) Тогда
СтрокаДляЗаписи = СтрокаДанных;
Прервать;
КонецЕсли;
КонецЦикла;
Если СтрокаДляЗаписи = Неопределено Тогда
СтрокаДляЗаписи = ДанныеОВремени.Добавить();
Отбор = Новый Структура("Дата", ТекДата);
НайденныеСтроки = ПроизводственныйГрафик.НайтиСтроки(Отбор);
СтрокаДляЗаписи.ВидДня = ?(НайденныеСтроки.Количество() > 0, НайденныеСтроки[0].ВидДня, Неопределено);
СтрокаДляЗаписи.Дата = ТекДата;
МассивСтрок.Добавить(СтрокаДляЗаписи);
КонецЕсли;
СтрокаДляЗаписи[ИмяКолонки] = Интервал;
КонецЦикла;
Если СохраненнаяДата <> Неопределено Тогда
Для Каждого СтрокаДанных Из ДанныеОВремени Цикл
Если СохраненнаяДата = СтрокаДанных.Дата Тогда
Элементы.ДанныеОВремени.ТекущаяСтрока = СтрокаДанных.ПолучитьИдентификатор();
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
УстановитьУсловноеОформлениеДанныеОВремени();
КонецПроцедуры
&НаСервере
Процедура УстановитьУсловноеОформлениеДанныеОВремени()
УсловноеОформление.Элементы.Очистить();
// Выходные дни (Суббота, Воскресенье)
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветФона", Новый Цвет(245, 251, 255));
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДанныеОВремени.ВидДня");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
СписокВидовДней = Новый СписокЗначений;
СписокВидовДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Суббота);
СписокВидовДней.Добавить(Перечисления.ВидыДнейПроизводственногоКалендаря.Воскресенье);
ЭлементОтбора.ПравоеЗначение = СписокВидовДней;
ЭлементОтбора.Использование = Истина;
ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("ДанныеОВремени");
// Праздничные дни
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветФона", Новый Цвет(255, 225, 232));
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДанныеОВремени.ВидДня");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Перечисления.ВидыДнейПроизводственногоКалендаря.Праздник;
ЭлементОтбора.Использование = Истина;
ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("ДанныеОВремени");
// Нерабочие дни
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветФона", Новый Цвет(240, 240, 240));
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДанныеОВремени.ВидДня");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Перечисления.ВидыДнейПроизводственногоКалендаря.Нерабочий;
ЭлементОтбора.Использование = Истина;
ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("ДанныеОВремени");
КонецПроцедуры
&НаСервере
Процедура ЗадатьФорматТЧДанныеОВремени()
Элементы.ДанныеОВремени.РастягиватьПоВертикали = Истина;
Элементы.ДанныеОВремени.РастягиватьПоГоризонтали = Истина;
Элементы.ДанныеОВремени.Ширина = 100;
Подчиненные = Элементы.ДанныеОВремени.ПодчиненныеЭлементы;
Для Каждый ЭлементКолонка Из Подчиненные Цикл
Если ТипЗнч(ЭлементКолонка) = Тип("ПолеФормы") Тогда
ЭлементКолонка.ТолькоПросмотр = Истина;
ЭлементКолонка.РежимРедактирования = РежимРедактированияКолонки.Непосредственно;
ЭлементКолонка.ГоризонтальноеПоложениеВШапке = ГоризонтальноеПоложениеЭлемента.Центр;
ЭлементКолонка.ГоризонтальноеПоложение = ГоризонтальноеПоложениеЭлемента.Центр;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
&НаСервере
Функция ПолучитьКалендарь(Год)
МассивКалендарей = ПолучитьМассивКалендарей(Год);
Возврат ?(МассивКалендарей.Количество() > 0, МассивКалендарей[0], Неопределено);
КонецФункции
&НаСервере
Функция ПолучитьСписокЗначенийКалендарей(Год)
СписокКалендарей = Новый СписокЗначений;
СписокКалендарей.ЗагрузитьЗначения(ПолучитьМассивКалендарей(Год));
Возврат СписокКалендарей;
КонецФункции
&НаСервере
Функция ПолучитьМассивКалендарей(Год)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ГрафикРаботы.Ссылка КАК ПроизводственныйКалендарь
|ИЗ
| Справочник.ГрафикРаботы КАК ГрафикРаботы
|ГДЕ
| ГОД(ГрафикРаботы.ДатаНачала) = &Год
| И НЕ ГрафикРаботы.ПометкаУдаления
|УПОРЯДОЧИТЬ ПО
| ГрафикРаботы.Наименование";
Запрос.УстановитьПараметр("Год", Год);
Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ПроизводственныйКалендарь");
КонецФункции
&НаСервере
Функция ДатаКакМесяцПодобратьДатуПоТексту(Текст, ДатаПоТексту = Неопределено)
СписокВозврата = Новый СписокЗначений;
ТекущийГод = Год(ТекущаяДатаСеанса());
Если ПустаяСтрока(Текст) Тогда
ДатаПоТексту = Дата(1, 1, 1);
Возврат СписокВозврата;
КонецЕсли;
Подстроки = СтрРазделить(Текст, ".,-/\ ", Ложь);
Если Подстроки.Количество() = 1 Тогда
Если СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Текст) Тогда
МесяцЧислом = Число(Текст);
Если МесяцЧислом >= 1 И МесяцЧислом <= 12 Тогда
ДатаПоТексту = Дата(ТекущийГод, МесяцЧислом, 1);
ФорматДаты = ?(СтрДлина(Текст) = 1, "ДФ='М/гг'", "ДФ='ММ/гг'");
СписокВозврата.Добавить(Формат(ДатаПоТексту, ФорматДаты));
КонецЕсли;
Иначе
СписокМесяцев = СписокМесяцевПоСтроке(Текст);
Для Каждого Месяц Из СписокМесяцев Цикл
ДатаПоТексту = Дата(ТекущийГод, Месяц, 1);
СписокВозврата.Добавить(Формат(ДатаПоТексту, "ДФ='ММММ гггг'"));
КонецЦикла;
КонецЕсли;
ИначеЕсли Подстроки.Количество() = 2 Тогда
Если Не СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Подстроки[1]) Тогда
Возврат СписокВозврата;
КонецЕсли;
ГодЧислом = Число(Подстроки[1]);
ДлинаГода = СтрДлина(Подстроки[1]);
Если ГодЧислом > 3000 Тогда
Возврат СписокВозврата;
КонецЕсли;
Если ДлинаГода <= 1 Тогда
ГодЧислом = Число(Лев(Формат(ТекущийГод, "ЧГ="), 3) + Подстроки[1]);
ИначеЕсли ДлинаГода = 2 Тогда
ГодЧислом = Число(Лев(Формат(ТекущийГод, "ЧГ="), 2) + Подстроки[1]);
ИначеЕсли ДлинаГода = 3 Тогда
ГодЧислом = Число(Лев(Формат(ТекущийГод, "ЧГ="), 1) + Подстроки[1]);
КонецЕсли;
Если ЗначениеЗаполнено(Подстроки[0]) И СтроковыеФункцииКлиентСервер.ТолькоЦифрыВСтроке(Подстроки[0]) Тогда
МесяцЧислом = Число(Подстроки[0]);
Если МесяцЧислом >= 1 И МесяцЧислом <= 12 Тогда
ДатаПоТексту = Дата(ГодЧислом, МесяцЧислом, 1);
СписокВозврата.Добавить(Формат(ДатаПоТексту, "ДФ='ММММ гггг'"));
КонецЕсли;
Иначе
СписокМесяцев = СписокМесяцевПоСтроке(Подстроки[0]);
Для Каждого Месяц Из СписокМесяцев Цикл
ДатаПоТексту = Дата(ГодЧислом, Месяц, 1);
СписокВозврата.Добавить(Формат(ДатаПоТексту, "ДФ='ММММ гггг'"));
КонецЦикла;
КонецЕсли;
КонецЕсли;
Возврат СписокВозврата;
КонецФункции
&НаСервере
Функция СписокМесяцевПоСтроке(Текст)
СписокМесяцев = Новый СписокЗначений;
МесяцыВозврата = Новый Массив;
Для Счетчик = 1 По 12 Цикл
СписокМесяцев.Добавить(Счетчик, Формат(Дата(2000, Счетчик, 1), "ДФ='ММММ'"));
СписокМесяцев.Добавить(Счетчик, Формат(Дата(2000, Счетчик, 1), "ДФ='МММ'"));
КонецЦикла;
ИскомыйТекст = ВРег(СокрЛП(Текст));
УникальныеЗначения = Новый Соответствие;
Для Каждого ЭлементСписка Из СписокМесяцев Цикл
Если Лев(ВРег(ЭлементСписка.Представление), СтрДлина(ИскомыйТекст)) = ИскомыйТекст Тогда
УникальныеЗначения.Вставить(ЭлементСписка.Значение, Истина);
КонецЕсли;
КонецЦикла;
Для Каждого КлючИЗначение Из УникальныеЗначения Цикл
МесяцыВозврата.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
Возврат МесяцыВозврата;
КонецФункции
#КонецОбласти
```