Загрузка данных
### Основная причина, почему не меняется год
Год сбрасывается обратно при каждом изменении реквизита **`НомерТекущегоГода`** из-за логического тупика в процедуре `НастроитьПолеКалендаря()`:
1. При изменении поля на форме выставляется новое значение `НомерТекущегоГода` (например, `2027`).
2. Вызывается цепочка: `НомерТекущегоГодаПриИзменении()` $\rightarrow$ `ОбработатьИзменениеГода()` $\rightarrow$ `ЗаполнитьДаннымиТекущегоГода()` $\rightarrow$ **`НастроитьПолеКалендаря()`**.
3. В `НастроитьПолеКалендаря()` первой же строкой выполняется:
```bsl
Если ЗначениеЗаполнено(Объект.Год) Тогда
НомерТекущегоГода = Объект.Год; // Объект.Год всё еще хранит старый год (2026)!
КонецЕсли;
```
4. Поскольку значение реквизита `Объект.Год` еще не обновлялось, эта строка **мгновенно перезаписывает** значение `НомерТекущегоГода` обратно на старый год.
---
### Логические и функциональные ошибки в коде
#### 1. Затирание прочитанных данных при смене года
В `НомерТекущегоГодаПриИзменении`:
```bsl
ОбработатьИзменениеГода(ЗаписыватьДанныеГрафика);
Если ЗначениеЗаполнено(Объект.ПроизводственныйКалендарь) Тогда
ЗаполнитьДаннымиПоУмолчанию(); // Ошибка!
КонецЕсли;
```
* **Проблема:** `ОбработатьИзменениеГода()` считывает реальные данные за выбранный год из регистра и календаря. Сразу после этого вызов `ЗаполнитьДаннымиПоУмолчанию()` полностью стирает эти данные и перезаписывает их базовыми настройками по умолчанию.
#### 2. Ошибка расчёта времени при пустом `ВремяОкончания`
В `ПредставлениеРасписанияДня`:
```bsl
ИначеЕсли Не ЗначениеЗаполнено(СтрокаРасписания.ВремяОкончания) Тогда
СекундИнтервала = КонецДня(СтрокаРасписания.ВремяОкончания) - СтрокаРасписания.ВремяНачала + 1;
```
* **Проблема:** Если `ВремяОкончания` не заполнено (пустая дата `'0001-01-01 00:00:00'`), функция `КонецДня(СтрокаРасписания.ВремяОкончания)` вернет `'0001-01-01 23:59:59'`. Если у `ВремяНачала` дата отличается, разность в секундах посчитается некорректно или уйдет в минус. Брать конец дня нужно от `ВремяНачала`: `КонецДня(СтрокаРасписания.ВремяНачала)`.
#### 3. Попытка записи в БД незаписанного объекта
В `ОбработатьИзменениеГода`:
```bsl
ЗаписатьДанныеПроизводственногоКалендаряНаСервере(НомерТекущегоГода, РеквизитФормыВЗначение("Объект"));
```
* **Проблема:** Если новый справочник еще ни разу не сохранялся в базе, то у `РеквизитФормыВЗначение("Объект").Ссылка` будет значение `Справочник.ГрафикРаботы.ПустаяСсылка`. Запись в подчиненные регистры/таблицы с пустой ссылкой приведет к ошибке или "битым" записям.
#### 4. Неактуальное чтение при переносах дней
В `ВписатьДатуПереноса`:
```bsl
ИсточникДняДатыНазначения = ПереносыДней.Получить(ДатаНазначения);
```
* **Проблема:** В процедуре используется глобальное `ПереносыДней` (`ФиксированноеСоответствие`), а не локально формируемое `ПереносыДнейСоответствие`. Из-за этого повторные/цепочечные переносы дат считывают старое состояние до фиксации изменений.
#### 5. Отсутствие флага `Модифицированность`
В процедурах `ИзменитьВидыДней()` и `ПеренестиВидДня()` пользователь изменяет состав дней и переносов на форме, но не устанавливается `Модифицированность = Истина;`. В результате платформа не считает форму измененной и не предложит сохранить данные при закрытии.
#### 6. Дублирование кода
Функция `ПолучитьРасписаниеРаботыДня(НомерДня)` полностью дублирует функцию `РасписаниеРаботы(НомерДня)`.
#### 7. Отключенные обработчики событий
В `РабочиеДниДеньВключенВГрафикПриИзменении` и `УчитыватьПраздничныеДниПриИзменении` закомментированы вызовы `ЗаполнитьПоШаблонуНаСервере()`. Из-за этого при переключении флажков интерфейс формы не обновляет расчет дней.
---
### Исправленные ключевые блоки кода
#### Исправление логики года (`НастроитьПолеКалендаря` и `ОбработатьИзменениеГода`)
```bsl
&НаСервере
Процедура НастроитьПолеКалендаря()
Если НомерТекущегоГода = 0 Тогда
НомерТекущегоГода = ?(ЗначениеЗаполнено(Объект.Год), Объект.Год, Год(ТекущаяДатаСеанса()));
КонецЕсли;
Объект.Год = НомерТекущегоГода;
НомерПредыдущегоГода = НомерТекущегоГода;
Элементы.Календарь.НачалоПериодаОтображения = Дата(НомерТекущегоГода, 1, 1);
Элементы.Календарь.КонецПериодаОтображения = Дата(НомерТекущегоГода, 12, 31);
Объект.ДатаНачала = Дата(НомерТекущегоГода, 1, 1);
Объект.ДатаОкончания = Дата(НомерТекущегоГода, 12, 31);
КонецПроцедуры
&НаКлиенте
Процедура НомерТекущегоГодаПриИзменении(Элемент)
ЗаписыватьДанныеГрафика = Ложь;
Если Модифицированность Тогда
ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Записать измененные данные за %1 год?'"),
Формат(НомерПредыдущегоГода, "ЧГ=0")
);
Оповещение = Новый ОписаниеОповещения("НомерТекущегоГодаПриИзмененииЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, ТекстСообщения, РежимДиалогаВопрос.ДаНет);
Возврат;
КонецЕсли;
ОбработатьИзменениеГода(ЗаписыватьДанныеГрафика);
Модифицированность = Ложь;
Элементы.Календарь.Обновить();
КонецПроцедуры
&НаСервере
Процедура ОбработатьИзменениеГода(ЗаписыватьДанныеГрафика)
Если ЗаписыватьДанныеГрафика Тогда
Если Объект.Ссылка.Пустая() Тогда
Записать(Новый Структура("НомерГода", НомерПредыдущегоГода));
Иначе
ЗаписатьДанныеПроизводственногоКалендаряНаСервере(НомерПредыдущегоГода, РеквизитФормыВЗначение("Объект"));
КонецЕсли;
КонецЕсли;
// Синхронизируем год объекта с выбранным на форме
Объект.Год = НомерТекущегоГода;
ЗаполнитьДаннымиТекущегоГода();
КонецПроцедуры
```
#### Исправление функции расчета представления расписания
```bsl
&НаКлиенте
Функция ПредставлениеРасписанияДня(НомерДня)
ПредставлениеИнтервалов = "";
Секунд = 0;
Для Каждого СтрокаРасписания Из Объект.РасписаниеРаботы Цикл
Если СтрокаРасписания.НомерДня <> НомерДня Тогда
Продолжить;
КонецЕсли;
ПредставлениеИнтервала = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
"%1-%2, ",
ПредставлениеВремениСуток(СтрокаРасписания.ВремяНачала, ДвенадцатиЧасовойФорматВремени),
ПредставлениеВремениСуток(СтрокаРасписания.ВремяОкончания, ДвенадцатиЧасовойФорматВремени)
);
ПредставлениеИнтервалов = ПредставлениеИнтервалов + ПредставлениеИнтервала;
Если СтрокаРасписания.ВремяНачала = СтрокаРасписания.ВремяОкончания И ЗначениеЗаполнено(СтрокаРасписания.ВремяНачала) Тогда
СекундИнтервала = 86400;
ИначеЕсли СтрокаРасписания.ВремяНачала < СтрокаРасписания.ВремяОкончания Тогда
СекундИнтервала = СтрокаРасписания.ВремяОкончания - СтрокаРасписания.ВремяНачала;
ИначеЕсли Не ЗначениеЗаполнено(СтрокаРасписания.ВремяОкончания) Тогда
// Исправлено: КонецДня берется от ВремяНачала
СекундИнтервала = КонецДня(СтрокаРасписания.ВремяНачала) - СтрокаРасписания.ВремяНачала + 1;
Иначе
ОсновнаяЧасть = КонецДня(СтрокаРасписания.ВремяНачала) - СтрокаРасписания.ВремяНачала + 1;
ПереходящаяЧасть = СтрокаРасписания.ВремяОкончания - НачалоДня(СтрокаРасписания.ВремяОкончания);
СекундИнтервала = ОсновнаяЧасть + ПереходящаяЧасть;
КонецЕсли;
Секунд = Секунд + СекундИнтервала;
КонецЦикла;
СтроковыеФункцииКлиентСервер.УдалитьПоследнийСимволВСтроке(ПредставлениеИнтервалов, 2);
Если Секунд = 0 Тогда
Возврат НСтр("ru = 'Заполнить расписание'");
КонецЕсли;
Часов = Окр(Секунд / 3600, 1);
Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 ч. (%2)'"), Часов, ПредставлениеИнтервалов);
КонецФункции
```