Имя: Пароль:
1C
1С v8
Расширение и хранение файлов
0 Yogurt
 
14.09.22
13:04
Столкнулся с задачей.
УНФ, делаем свой справочник в расширении, к нему нужно прицеплять файлы (сканы документов).
Типовым способом через БСП "Определяемые типы" "ВладелецПрисоединенныхФайлов" добавить в расширение свой справочник можно либо с 20 платформы, либо тот еще квест.
Как еще можно просто и быстро организовать хранение файлов в справочник из расширения?
1 Greeen
 
14.09.22
13:49
Мой совет - обновить платформу и не париться (ИМХО - это из самого простого решение)
2 H A D G E H O G s
 
14.09.22
13:54
(0) Я запиливал регистр - копию и отдельные справочники
3 DrZombi
 
гуру
14.09.22
13:55
(0) Быстро, используя БСП - "ВладелецПрисоединенныхФайлов".
Имя справочника "ВладелецПрисоединенныхФайлов" - должно соответствовать правилу.

Придется снять с поддержки. :)
4 Yogurt
 
14.09.22
13:56
(1) Что бы добавить свой объект в "Определяемые типы", это надо добавить в расширение все объекты, которые там есть в изначальном виде?
Иначе мне свой не добавить, в расширении тип у "ВладелецПрисоединенныхФайлов" - "Произвольный"
5 Ryzeman
 
14.09.22
14:02
(0) Можно неБСПшное сделать. Тебе по сути надо в реквизите хранить ссылку в хранилище да логику загрузки и получения прописать. В БСПшном просто всё красиво и универсально. Картиночки вон смотреть и редактировать можно)
6 Ryzeman
 
14.09.22
14:02
ну или снять с поддержки. Или затащить в расширение миллиард объектов)
7 DrZombi
 
гуру
14.09.22
14:59
(0) В общем описывать все долго, но через расширение тоже можно дописывать хранение файлов вида "ВладелецПрисоединенныхФайлов"

Список всего, что  у меня потребовалось для доработок.
Но так может, у вас это не предел...
Там где "Форма", то надо все реквизиты формы с соответствующими типами так же менять. (и не забыть про вкладку Параметры формы "ПрисоединенныйФайл")
И помни, от основной конфигурации все можно определить, как Любая ссылка, а ваш тип из расширения, указать как обычно, ибо увы тип расширения надо указывать

Объект "Справочник.ВключениеВКадровыйРезервПрисоединенныеФайлы" использован в:
ПодпискаНаСобытие.ОпределитьФормуПрисоединенногоФайлаВзаимодействия.Источник
ПодпискаНаСобытие.СОГ_ВыполнитьДействияПередЗаписьюПрисоединенногоФайла.Источник
ПодпискаНаСобытие.СОГ_ВыполнитьДействияПередУдалениемПрисоединенногоФайла.Источник
ПодпискаНаСобытие.СОГ_ВыполнитьДействияПриЗаписиПрисоединенногоФайла.Источник
Обработка.РаботаСФайлами.Форма.ПрисоединенныйФайл.Форма
РегистрСведений.СОГ_ДвоичныеДанныеФайлов.Измерение.Файл.Тип
РегистрСведений.СОГ_СведенияОФайлах.Измерение.Файл.Тип

Общие модули:
    Создать общий модуль, с данными на время сеанса "СОГ_ДопФункцииПовторИсп"

И там же функции:

     - ПолучитьОписаниеТипов_ОпределяемыеТипы_ВладелецПрисоединенныхФайлов
     - ПолучитьОписаниеТипов_ОпределяемыеТипы_ПредметНапоминания

(Эти функции нужны для определения, какие Элементы принадлежат расширению и требуют индивидуальной обработки
Ведь, тот же Регистр "СОГ_СведенияОФайлах" содержит ваш тип из расширения и не должен содержать типы из основной конфигурации)


Кусочки коду, которые пришлось перетащить

ОбщийМодуль.РаботаСФайлами


// Возвращает двоичные данные файла из информационной базы.
//
// Параметры:
//   ФайлСсылка - ссылка на файл или его версию.
//
// Возвращаемое значение:
//   ХранилищеЗначения - двоичные данные файла.
//
&Вместо("ХранилищеФайлаИзИнформационнойБазы")
Функция СОГ_ХранилищеФайлаИзИнформационнойБазы(ФайлСсылка) Экспорт
    
    УстановитьОтключениеБезопасногоРежима(Истина);
    УстановитьПривилегированныйРежим(Истина);
    
    ТипЗначенияРасширения = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_Расширения("АдаптацияКонфигурации");
    
    //Типовой код, иначе не типовой...
    Если ТипЗначенияРасширения.СодержитТип(ТипЗнч(ФайлСсылка)) <> Истина Тогда
        Возврат ПродолжитьВызов(ФайлСсылка);
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ДвоичныеДанныеФайлов.Файл,
    |    ДвоичныеДанныеФайлов.ДвоичныеДанныеФайла
    |ИЗ
    |    РегистрСведений.СОГ_ДвоичныеДанныеФайлов КАК ДвоичныеДанныеФайлов
    |ГДЕ
    |    ДвоичныеДанныеФайлов.Файл = &ФайлСсылка";
    
    Запрос.УстановитьПараметр("ФайлСсылка", ФайлСсылка);
    Выборка = Запрос.Выполнить().Выбрать();
    
    Возврат ?(Выборка.Следующий(), Выборка.ДвоичныеДанныеФайла, Неопределено);
    
КонецФункции


// Возвращает двоичные данные файла.
//
// Параметры:
//  ПрисоединенныйФайл - ОпределяемыйТип.ПрисоединенныйФайл - ссылка на элемент справочника с файлом.
//
//  ВызыватьИсключение - Булево - если указать Ложь, то функция будет возвращать Неопределено
//                                   вместо вызова исключений, уровень записи журнала регистрации будет понижен до "Предупреждение".
//                                Значение по умолчанию - Истина.
//
// Возвращаемое значение:
//  ДвоичныеДанные, Неопределено - двоичные данные присоединенного файла. Если двоичные данные файла не найдены
//                               в информационной базе или в томах, вызывает исключение. Если двоичные данные не
//                               найдены и параметр ВызыватьИсключение принимает значение Ложь, тогда
//                               возвращаемое значение - Неопределено.
//
// Пример:
//  Сохранение данных файла на сервере:
//    ДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(Файл, Ложь);
//    Если ДанныеФайла <> Неопределено Тогда
//        ДанныеФайла.Записать(ПутьКФайлу);
//    КонецЕсли;
//
&Вместо("ДвоичныеДанныеФайла")
Функция СОГ_ДвоичныеДанныеФайла(Знач ПрисоединенныйФайл, Знач ВызыватьИсключение = Истина) Экспорт
    
    ТипЗначенияРасширения = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_Расширения("АдаптацияКонфигурации");
    
    //Типовой код, иначе не типовой...
    Если ТипЗначенияРасширения.СодержитТип(ТипЗнч(ПрисоединенныйФайл)) <> Истина Тогда
        Возврат ПродолжитьВызов(ПрисоединенныйФайл, ВызыватьИсключение);
    КонецЕсли;
    
    ПрисоединенныйФайл_Тип = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_ОпределяемыеТипы_ПрисоединенныйФайл(); //АдаптацияКонфигурации
    
    ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("РаботаСФайлами.ДвоичныеДанныеФайла", "ПрисоединенныйФайл",
        ПрисоединенныйФайл, ПрисоединенныйФайл_Тип);
    
    ФайлОбъект = РаботаСФайламиСлужебный.ФайлОбъект(ПрисоединенныйФайл);
    Если (ФайлОбъект = Неопределено Или ФайлОбъект.ЭтоГруппа) И Не ВызыватьИсключение Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    ОбщегоНазначенияКлиентСервер.Проверить(ФайлОбъект <> Неопределено,
        СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
            НСтр("ru = 'Недопустимое значение параметра %1'"), "ПрисоединенныйФайл"),
        "РаботаСФайлами.ДвоичныеДанныеФайла");
    ОбщегоНазначенияКлиентСервер.Проверить(Не ФайлОбъект.ЭтоГруппа,
        СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
            НСтр("ru = 'Недопустимое значение параметра %1 (папка файлов ""%2"")'"),
            "ПрисоединенныйФайл", ОбщегоНазначения.ПредметСтрокой(ПрисоединенныйФайл)),
        "РаботаСФайлами.ДвоичныеДанныеФайла");
    
    УстановитьОтключениеБезопасногоРежима(Истина);
    УстановитьПривилегированныйРежим(Истина);
    
    Если ФайлОбъект.ПометкаУдаления Тогда
        РаботаСФайламиСлужебный.СообщитьОбОшибкеФайлНеНайден(ФайлОбъект, ВызыватьИсключение);
        Возврат Неопределено;
    КонецЕсли;
    
    Если ФайлОбъект.ТипХраненияФайла = Перечисления.ТипыХраненияФайлов.ВИнформационнойБазе Тогда
        
        Результат = ХранилищеФайлаИзИнформационнойБазы(ФайлОбъект.Ссылка);
        Если Результат <> Неопределено Тогда
            Результат = Результат.Получить();
            Если Результат <> Неопределено Тогда
                Возврат Результат;
            КонецЕсли;
        КонецЕсли;
        
        РаботаСФайламиСлужебный.СообщитьОбОшибкеФайлНеНайден(ФайлОбъект, ВызыватьИсключение);
        Возврат Неопределено;
            
    Иначе
        Возврат РаботаСФайламиВТомахСлужебный.ДанныеФайла(ПрисоединенныйФайл, ВызыватьИсключение);
    КонецЕсли;
    
КонецФункции


Общий модуль = "СОГ_ДопФункцииПовторИсп"

#Область Описание_Типа_Расширения

Функция ПолучитьОписаниеТипов_Расширения(Знач ИмяРасширения=Неопределено) Экспорт
    
    МассивТипов = Новый Массив;
    
    Для Каждого Мета Из Метаданные.Справочники Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        МассивТипов.Добавить(Тип("СправочникСсылка."+Мета.Имя));
    КонецЦикла;
    
    Для Каждого Мета Из Метаданные.Документы Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        МассивТипов.Добавить(Тип("ДокументСсылка."+Мета.Имя));
    КонецЦикла;
    
    Для Каждого Мета Из Метаданные.ПланыОбмена Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        МассивТипов.Добавить(Тип("ПланОбменаСсылка."+Мета.Имя));
    КонецЦикла;
    
    Для Каждого Мета Из Метаданные.Перечисления Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        МассивТипов.Добавить(Тип("ПеречислениеСсылка."+Мета.Имя));
    КонецЦикла;
    
    Для Каждого Мета Из Метаданные.ПланыВидовХарактеристик Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        МассивТипов.Добавить(Тип("ПланВидовХарактеристикСсылка."+Мета.Имя));
    КонецЦикла;
    
    ОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
    
    Возврат ОписаниеТипов;
КонецФункции

#КонецОбласти

//Метаданные.ОпределяемыеТипы.ПрисоединенныйФайл.Тип
Функция ПолучитьОписаниеТипов_ОпределяемыеТипы_ПрисоединенныйФайл() Экспорт
    
    МассивТипов = Новый Массив;
    
    //МассивТипов.Добавить(Тип("СправочникСсылка.БроньПрисоединенныеФайлы"));
    ИмяРасширения = "АдаптацияКонфигурации";
    
    Для Каждого Мета Из Метаданные.Справочники Цикл
        ИспользРасш = Мета.РасширениеКонфигурации();
        Если ИспользРасш = Неопределено Тогда Продолжить; КонецЕсли;
        Если ИмяРасширения <> Неопределено и ИспользРасш.Имя <> ИмяРасширения Тогда Продолжить; КонецЕсли;
        
        Если Прав(Мета.Имя,19) <> "ПрисоединенныеФайлы" Тогда Продолжить; КонецЕсли;
        
        МассивТипов.Добавить(Тип("СправочникСсылка."+Мета.Имя));
    КонецЦикла;
    
    ОписТипа = Новый ОписаниеТипов(МассивТипов);
    
    Возврат ОписТипа;
КонецФункции


Общий модуль = СОГ_РаботаСФайлами


#Область СлужебныеПроцедурыИФункции

// Обработчик подписки на событие ПередЗаписью владельца присоединенного файла.
// Помечает на удаление связанные файлы.
//
// Параметры:
//  Источник        - ДокументОбъект - владелец присоединенного файла.
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//  РежимЗаписи     - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//  РежимПроведения - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
Процедура УстановитьПометкуУдаленияПрисоединенныхФайловДокументов(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    
    РаботаСФайлами.УстановитьПометкуУдаленияПрисоединенныхФайловДокументов(Источник, Отказ, РежимЗаписи, РежимПроведения);
    
КонецПроцедуры

// Обработчик подписки на событие ПередЗаписью владельца присоединенного файла.
// Помечает на удаление связанные файлы.
//
// Параметры:
//  Источник - ОпределяемыйТип.ВладелецПрисоединенныхФайловОбъект - владелец присоединенного файла, кроме ДокументОбъект.
//  Отказ    - Булево - признак отказа от записи.
//
Процедура УстановитьПометкуУдаленияПрисоединенныхФайлов(Источник, Отказ) Экспорт
    
    РаботаСФайлами.УстановитьПометкуУдаленияПрисоединенныхФайлов(Источник, Отказ);
    
КонецПроцедуры

#КонецОбласти


#Область СлужебныеПроцедурыИФункции

////////////////////////////////////////////////////////////////////////////////
// Обработчики подписок на события.

// Обработчик подписки на событие ПередЗаписью для заполнения авто реквизитов присоединенного файла.
//
// Параметры:
//  Источник   - СправочникОбъект - объект справочника с именем "*ПрисоединенныеФайлы".
//  Отказ      - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
Процедура ВыполнитьДействияПередЗаписьюПрисоединенногоФайла(Источник, Отказ) Экспорт
    
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    Если Источник.ДополнительныеСвойства.Свойство("КонвертацияФайлов") Тогда
        Возврат;
    КонецЕсли;
    
    //В расширении этот механизм не предусмотрен, так что при включении, автор, думай как быть :)
    Если ТипЗнч(Источник) = Тип("СправочникОбъект.ВерсииФайлов") Тогда
        Если Не Источник.ЭтоНовый() И Не Пользователи.ЭтоПолноправныйПользователь() Тогда
            ПрежниеЗначения = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.Ссылка, "Автор");
            ПроверитьИзменениеАвтораФайла(ПрежниеЗначения, Источник);
        КонецЕсли;
        Возврат;
    КонецЕсли;
    
    Если Источник.ЭтоНовый() Тогда
        // Проверка права "Добавление".
        Если НЕ РаботаСФайламиСлужебный.ЕстьПраво("ДобавлениеФайлов", Источник.ВладелецФайла) Тогда
            ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
                НСтр("ru = 'Недостаточно прав для добавления файлов в папку ""%1"".'"),
                Строка(Источник.ВладелецФайла));
        КонецЕсли;
    Иначе
        
        //Проверим, может передали ошибочно объект из БД...
        //    Тут обрабатывается только объект из хранилища...
        ТипЗначенияРасширения = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_ОпределяемыеТипы_ПрисоединенныйФайл();
        Если ТипЗначенияРасширения.СодержитТип(ТипЗнч(Источник.Ссылка)) <> Истина Тогда
            Возврат;
        КонецЕсли;
        //...
        
        Если Пользователи.ЭтоПолноправныйПользователь() Тогда
            ПрежниеЗначения = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.Ссылка, "ПометкаУдаления");
        Иначе    
            ПрежниеЗначения = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.Ссылка, "ПометкаУдаления, Автор, Редактирует, Изменил");
            ПроверитьИзменениеАвтораФайла(ПрежниеЗначения, Источник);
        КонецЕсли;
        
        ИзмененаПометкаУдаления = Источник.ПометкаУдаления <> ПрежниеЗначения.ПометкаУдаления;
        Если ИзмененаПометкаУдаления Тогда
            // Проверка права "Пометка на удаление".
            Если НЕ РаботаСФайламиСлужебный.ЕстьПраво("ПометкаУдаленияФайлов", Источник.ВладелецФайла) Тогда
                ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
                    НСтр("ru = 'Недостаточно прав для пометки файлов на удаление в папке ""%1"".'"),
                    Строка(Источник.ВладелецФайла));
            КонецЕсли;
        КонецЕсли;
        
        Если ИзмененаПометкаУдаления И ЗначениеЗаполнено(Источник.Редактирует) Тогда
                
            Если Источник.Редактирует = Пользователи.АвторизованныйПользователь() Тогда
                ТекстОшибки = НСтр("ru = 'Действие недоступно, так как файл ""%1"" занят для редактирования.'");
                ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки, Источник.Наименование);
            Иначе
                ТекстОшибки = НСтр("ru = 'Действие недоступно, так как файл ""%1"" занят для редактирования
                    |пользователем %2.'");
                ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстОшибки,
                    Источник.Наименование, Строка(Источник.Редактирует));
            КонецЕсли;
            
        КонецЕсли;
        
        ЗаписьПодписанногоОбъекта = Ложь;
        Если Источник.ДополнительныеСвойства.Свойство("ЗаписьПодписанногоОбъекта") Тогда
            ЗаписьПодписанногоОбъекта = Источник.ДополнительныеСвойства.ЗаписьПодписанногоОбъекта;
        КонецЕсли;
        
        Если ЗаписьПодписанногоОбъекта <> Истина Тогда
            
            СтруктураРеквизитов = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.Ссылка,
                "ПодписанЭП, Зашифрован, Редактирует");
            
            СсылкаПодписан    = СтруктураРеквизитов.ПодписанЭП;
            СсылкаЗашифрован  = СтруктураРеквизитов.Зашифрован;
            СсылкаЗанят       = ЗначениеЗаполнено(СтруктураРеквизитов.Редактирует);
            Занят = ЗначениеЗаполнено(Источник.Редактирует);
            
            Если Не Источник.ЭтоГруппа И Источник.ПодписанЭП И СсылкаПодписан И Занят И Не СсылкаЗанят Тогда
                ВызватьИсключение НСтр("ru = 'Подписанный файл нельзя редактировать.'");
            КонецЕсли;
            
            Если Не Источник.ЭтоГруппа И Источник.Зашифрован И СсылкаЗашифрован И Источник.ПодписанЭП И НЕ СсылкаПодписан Тогда
                ВызватьИсключение НСтр("ru = 'Зашифрованный файл нельзя подписывать.'");
            КонецЕсли;
            
        КонецЕсли;
        
        СправочникПоддерживаетВозможностьХранитьВерсии = ОбщегоНазначения.ЕстьРеквизитОбъекта("ТекущаяВерсия", Метаданные.НайтиПоТипу(ТипЗнч(Источник)));
        
        Если Не Источник.ЭтоГруппа И СправочникПоддерживаетВозможностьХранитьВерсии И ЗначениеЗаполнено(Источник.ТекущаяВерсия) Тогда
            
            РеквизитыТекущейВерсии = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Источник.ТекущаяВерсия, "Наименование");
            
            // Проверим равенство имени файла и его текущей версии.
            // Если имена отличаются - имя у версии должно стать как у карточки с файлом.
            Если РеквизитыТекущейВерсии.Наименование <> Источник.Наименование
               И ЗначениеЗаполнено(Источник.ТекущаяВерсия) Тогда
                
                БлокировкаДанных = Новый БлокировкаДанных;
                ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(
                    Метаданные.НайтиПоТипу(ТипЗнч(Источник.ТекущаяВерсия)).ПолноеИмя());
                
                ЭлементБлокировкиДанных.УстановитьЗначение("Ссылка", Источник.ТекущаяВерсия);
                БлокировкаДанных.Заблокировать();
                
                Объект = Источник.ТекущаяВерсия.ПолучитьОбъект();
                
                Если Объект <> Неопределено Тогда
                    УстановитьПривилегированныйРежим(Истина);
                    Объект.Наименование = Источник.Наименование;
                    // Чтобы не сработала подписка СкопироватьРеквизитыВерсииФайловВФайл.
                    Объект.ДополнительныеСвойства.Вставить("ПереименованиеФайла", Истина);
                    Объект.Записать();
                    УстановитьПривилегированныйРежим(Ложь);
                КонецЕсли;
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЕсли;
    
    Если Не ЗначениеЗаполнено(Источник.ВладелецФайла) Тогда
        
        ОписаниеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Не заполнен владелец в файле
            |""%1"".'"), Источник.Наименование);
        
        Если ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы() Тогда
            ЗаписьЖурналаРегистрации(НСтр("ru = 'Файлы.Ошибка записи файла при обновлении ИБ'", ОбщегоНазначения.КодОсновногоЯзыка()),
                УровеньЖурналаРегистрации.Ошибка,, Источник.Ссылка, ОписаниеОшибки);
        Иначе
            ВызватьИсключение ОписаниеОшибки;
        КонецЕсли;
        
    КонецЕсли;
    
    Если Источник.ЭтоГруппа Тогда
        Источник.ИндексКартинки = 2;
    Иначе
        Источник.ИндексКартинки = РаботаСФайламиСлужебныйКлиентСервер.ПолучитьИндексПиктограммыФайла(Источник.Расширение);
    КонецЕсли;
    
    Если Источник.ЭтоНовый() И Не ЗначениеЗаполнено(Источник.Автор) Тогда
        Источник.Автор = Пользователи.АвторизованныйПользователь();
    КонецЕсли;
    
КонецПроцедуры

// Обработчик подписки на событие ПередУдалением для удаления данных, связанных с присоединенным файлом.
//
// Параметры:
//  Источник   - СправочникОбъект - объект справочника с именем "*ПрисоединенныеФайлы".
//  Отказ      - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
Процедура ВыполнитьДействияПередУдалениемПрисоединенногоФайла(Источник, Отказ) Экспорт
    
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    Если ТипЗнч(Источник) = Тип("СправочникОбъект.ВерсииФайлов") Тогда
        Возврат;
    КонецЕсли;
    
    //Проверим, может передали ошибочно объект из БД...
    //    Тут обрабатывается только объект из хранилища...
    ТипЗначенияРасширения = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_ОпределяемыеТипы_ПрисоединенныйФайл();
    Если ТипЗначенияРасширения.СодержитТип(ТипЗнч(Источник.Ссылка)) <> Истина Тогда
        Возврат;
    КонецЕсли;
    //...
        
    СОГ_РаботаСФайламиСлужебный.ПередУдалениемПрисоединенногоФайлаСервер(
        Источник.Ссылка,
        Источник.ВладелецФайла,
        Источник.Том,
        Источник.ТипХраненияФайла,
        Источник.ПутьКФайлу);
    
КонецПроцедуры

// Обработчик подписки на событие ПриЗаписи для обновления данных, связанных с присоединенным файлом.
//
// Параметры:
//  Источник   - СправочникОбъект - объект справочника с именем "*ПрисоединенныеФайлы".
//  Отказ      - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
Процедура ВыполнитьДействияПриЗаписиПрисоединенногоФайла(Источник, Отказ) Экспорт
    
    Если Источник.ОбменДанными.Загрузка Тогда
        ЗаписатьДанныеФайлаВРегистрПриОбмене(Источник);
        Возврат;
    КонецЕсли;
    
    Если ТипЗнч(Источник) = Тип("СправочникОбъект.ВерсииФайлов") Тогда
        Возврат;
    КонецЕсли;
    
    //Проверим, может передали ошибочно объект из БД...
    //    Тут обрабатывается только объект из хранилища...
    ТипЗначенияРасширения = СОГ_ДопФункцииПовторИсп.ПолучитьОписаниеТипов_ОпределяемыеТипы_ПрисоединенныйФайл();
    Если ТипЗначенияРасширения.СодержитТип(ТипЗнч(Источник.Ссылка)) <> Истина Тогда
        Возврат;
    КонецЕсли;
    //...
        
    РаботаСФайламиСлужебный.ПриЗаписиПрисоединенногоФайлаСервер(Источник.ВладелецФайла, Источник);
        
    РаботаСФайламиСлужебный.ОбновитьСостояниеОчередиИзвлеченияТекста(
        Источник.Ссылка, Источник.СтатусИзвлеченияТекста);
    
КонецПроцедуры

#КонецОбласти


#Область Дополнительные_Функции

Процедура ЗаписатьДанныеФайлаВРегистрПриОбмене(Знач Источник)
    
    Перем ДвоичныеДанныеФайла;
    
    Если Источник.ДополнительныеСвойства.Свойство("ДвоичныеДанныеФайла", ДвоичныеДанныеФайла) Тогда
        НаборЗаписей = РегистрыСведений.СОГ_ДвоичныеДанныеФайлов.СоздатьНаборЗаписей();
        НаборЗаписей.Отбор.Файл.Использование = Истина;
        НаборЗаписей.Отбор.Файл.Значение = Источник.Ссылка;
        
        Запись = НаборЗаписей.Добавить();
        Запись.Файл = Источник.Ссылка;
        Запись.ДвоичныеДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанныеФайла, Новый СжатиеДанных(9));
        
        НаборЗаписей.ОбменДанными.Загрузка = Истина;
        НаборЗаписей.Записать();
        
        Источник.ДополнительныеСвойства.Удалить("ДвоичныеДанныеФайла");
    КонецЕсли;
    
КонецПроцедуры

Функция НедопустимыйАвтор(АвторСсылка, ТекущийПользователь)
    
    // Можно изменять только с пустого значения на себя и наоборот.
    Возврат АвторСсылка <> Неопределено И Не АвторСсылка.Пустая()
        И ТипЗнч(АвторСсылка) <> Тип("СправочникСсылка.УчетныеЗаписиСинхронизацииФайлов")
        И АвторСсылка <> ТекущийПользователь;
    
КонецФункции    

Процедура ПроверитьИзменениеАвтораФайла(Знач ПрежниеЗначения, Знач Источник)
    
    ИзмененАвтор = Источник.Автор <> ПрежниеЗначения.Автор;
    Если ИзмененАвтор Тогда
        ВызватьИсключение НСтр("ru = 'Недостаточно прав для изменения автора файла.'");
    КонецЕсли;
    
    Если ТипЗнч(Источник) = Тип("СправочникОбъект.ВерсииФайлов") Тогда
        Возврат;
    КонецЕсли;
        
    ТекущийПользователь = Пользователи.АвторизованныйПользователь();
    Если Источник.Редактирует <> ПрежниеЗначения.Редактирует
        И (НедопустимыйАвтор(Источник.Редактирует, ТекущийПользователь)
            Или НедопустимыйАвтор(ПрежниеЗначения.Редактирует, ТекущийПользователь)) Тогда
        ВызватьИсключение НСтр("ru = 'Недостаточно прав для редактирования файла.'");
    КонецЕсли;
    
    Если Источник.Изменил <> ПрежниеЗначения.Изменил
        И (НедопустимыйАвтор(Источник.Изменил, ТекущийПользователь)
            Или НедопустимыйАвтор(ПрежниеЗначения.Изменил, ТекущийПользователь)) Тогда
        ВызватьИсключение НСтр("ru = 'Недостаточно прав для редактирования файла.'");
    КонецЕсли;

КонецПроцедуры

#КонецОбласти



Общий модуль = СОГ_РаботаСФайламиСлужебный


#Область ОбработчикиПодписокНаСобытия

// Обработчик подписки на событие "Перед удалением" присоединенного файла.
Процедура ПередУдалениемПрисоединенногоФайлаСервер(Знач Ссылка,
                                                   Знач ВладелецФайлов,
                                                   Знач Том,
                                                   Знач ТипХраненияФайла,
                                                   Знач ПутьКФайлу) Экспорт
    
    УстановитьПривилегированныйРежим(Истина);
    
    Если ВладелецФайлов <> Неопределено И Не ЕстьФайлыУВладельца(ВладелецФайлов, Ссылка) Тогда
        
        НачатьТранзакцию();
        Попытка
            БлокировкаДанных = Новый БлокировкаДанных;
            ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.РегистрыСведений.СОГ_НаличиеФайлов.ПолноеИмя());
            ЭлементБлокировкиДанных.УстановитьЗначение("ОбъектСФайлами", ВладелецФайлов);
            БлокировкаДанных.Заблокировать();
            
            МенеджерЗаписи = РегистрыСведений.СОГ_НаличиеФайлов.СоздатьМенеджерЗаписи();
            МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
            МенеджерЗаписи.Прочитать();
            Если МенеджерЗаписи.Выбран() Тогда
                МенеджерЗаписи.ЕстьФайлы = Ложь;
                МенеджерЗаписи.Записать();
            КонецЕсли;
            
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
            ВызватьИсключение;
        КонецПопытки;
        
    КонецЕсли;
    
КонецПроцедуры

// Обработчик подписки "при записи" присоединенного файла.
//
Процедура ПриЗаписиПрисоединенногоФайлаСервер(ВладелецФайлов, Источник) Экспорт
    
    УстановитьПривилегированныйРежим(Истина);
    НачатьТранзакцию();
    Попытка
    
        ЗаписьИзменилась = Ложь;
        
        БлокировкаДанных = Новый БлокировкаДанных;
        ЭлементБлокировкиДанных = БлокировкаДанных.Добавить(Метаданные.РегистрыСведений.СОГ_НаличиеФайлов.ПолноеИмя());
        ЭлементБлокировкиДанных.УстановитьЗначение("ОбъектСФайлами", ВладелецФайлов);
        БлокировкаДанных.Заблокировать();
        
        МенеджерЗаписи = РегистрыСведений.СОГ_НаличиеФайлов.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
        МенеджерЗаписи.Прочитать();
        
        Если НЕ ЗначениеЗаполнено(МенеджерЗаписи.ОбъектСФайлами) Тогда
            МенеджерЗаписи.ОбъектСФайлами = ВладелецФайлов;
            ЗаписьИзменилась = Истина;
        КонецЕсли;
        
        ЕстьФайлы = НЕ Источник.ПометкаУдаления ИЛИ ЕстьФайлыУВладельца(ВладелецФайлов);
        Если МенеджерЗаписи.ЕстьФайлы <> ЕстьФайлы Тогда
            МенеджерЗаписи.ЕстьФайлы = ЕстьФайлы;
            ЗаписьИзменилась = Истина;
        КонецЕсли;
        
        Если ПустаяСтрока(МенеджерЗаписи.ИдентификаторОбъекта) Тогда
            МенеджерЗаписи.ИдентификаторОбъекта = ПолучитьОчереднойИдентификаторОбъекта();
            ЗаписьИзменилась = Истина;
        КонецЕсли;
        
        Если ЗаписьИзменилась Тогда
            МенеджерЗаписи.Записать();
        КонецЕсли;
        
        Если Не Источник.ЭтоГруппа Тогда
            МенеджерЗаписи = РегистрыСведений.СОГ_СведенияОФайлах.СоздатьМенеджерЗаписи();
            ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Источник);
            МенеджерЗаписи.Файл = Источник.Ссылка;
            Если Источник.ПодписанЭП И Источник.Зашифрован Тогда
                МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 2;
            ИначеЕсли Источник.Зашифрован Тогда
                МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 1;
            ИначеЕсли Источник.ПодписанЭП Тогда
                МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = 0;
            Иначе
                МенеджерЗаписи.НомерКартинкиПодписанЗашифрован = -1;
            КонецЕсли;
            
            МенеджерЗаписи.Записать();
        КонецЕсли;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
    
КонецПроцедуры

#КонецОбласти


#Область Дополнительные_Функции

Функция ЕстьФайлыУВладельца(Знач ВладелецФайлов, Знач ФайлИсключение = Неопределено)
    
    Запрос = Новый Запрос;
    ТекстЗапроса =
    "ВЫБРАТЬ
    |    ПрисоединенныеФайлы.Ссылка
    |ИЗ
    |    &ИмяСправочника КАК ПрисоединенныеФайлы
    |ГДЕ
    |    НЕ ПрисоединенныеФайлы.ПометкаУдаления
    |    И ПрисоединенныеФайлы.ВладелецФайла = &ВладелецФайлов
    |    И &ФайлИсключение";
    
    Запрос.Параметры.Вставить("ВладелецФайлов", ВладелецФайлов);
    Если ФайлИсключение <> Неопределено Тогда
        ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФайлИсключение", "ПрисоединенныеФайлы.Ссылка <> &ФайлИсключение"); // @Query-part-2
        Запрос.Параметры.Вставить("ФайлИсключение", ФайлИсключение);
    Иначе
        ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ФайлИсключение", "ИСТИНА"); // @Query-part-2
    КонецЕсли;
    
    ИменаСправочников = РаботаСФайламиСлужебный.ИменаСправочниковХраненияФайлов(ВладелецФайлов);
    
    Для каждого КлючИЗначение Из ИменаСправочников Цикл
        Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&ИмяСправочника", "Справочник." + КлючИЗначение.Ключ);
        
        УстановитьПривилегированныйРежим(Истина);
        Если НЕ Запрос.Выполнить().Пустой() Тогда
            Возврат Истина;
        КонецЕсли;
        УстановитьПривилегированныйРежим(Ложь);
    КонецЦикла;
    
    Возврат Ложь;
    
КонецФункции

// Возвращает новый идентификатор объекта.
//  Для получения нового идентификатора выбирает последний идентификатор объекта
// из регистра НаличиеПрисоединенныхФайлов увеличивает его значение
// на одну единицу и возвращает полученный результат.
//
// Возвращаемое значение:
//  Строка - строка (10) - новый идентификатор объекта.
//
Функция ПолучитьОчереднойИдентификаторОбъекта() Экспорт
    
    // Вычисление нового идентификатора объекта.
    Результат = "0000000000"; // По длине ресурса ИдентификаторОбъекта.
    
    ТекстЗапроса =
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    НаличиеФайлов.ИдентификаторОбъекта КАК ИдентификаторОбъекта
    |ИЗ
    |    РегистрСведений.СОГ_НаличиеФайлов КАК НаличиеФайлов
    |
    |УПОРЯДОЧИТЬ ПО
    |    ИдентификаторОбъекта УБЫВ";
    
    Запрос = Новый Запрос;
    Запрос.Текст = ТекстЗапроса;
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Идентификатор = Выборка.ИдентификаторОбъекта;
        
        Если ПустаяСтрока(Идентификатор) Тогда
            Возврат Результат;
        КонецЕсли;
        
        // Правила вычисления, как в обычном сложении: при
        // заполнении текущего разряда следующий разряд увеличивается
        // на единицу, при этом, в текущем разряде значение становится
        // равным нулю. Значениями разрядов выступают символы
        // [0..9] и [a..z]. Таким образом один разряд может содержать
        // 36 значений.
        
        Позиция = 10; // 9- индекс 10-го символа
        Пока Позиция > 0 Цикл
            
            Символ = Сред(Идентификатор, Позиция, 1);
            
            Если Символ = "z" Тогда
                Идентификатор = Лев(Идентификатор, Позиция-1) + "0" + Прав(Идентификатор, 10 - Позиция);
                Позиция = Позиция - 1;
                Продолжить;
                
            ИначеЕсли Символ = "9" Тогда
                НовыйСимвол = "a";
            Иначе
                НовыйСимвол = Символ(КодСимвола(Символ)+1);
            КонецЕсли;
            
            Идентификатор = Лев(Идентификатор, Позиция-1) + НовыйСимвол + Прав(Идентификатор, 10 - Позиция);
            Прервать;
        КонецЦикла;
        
        Результат = Идентификатор;
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

#КонецОбласти
8 DrZombi
 
гуру
14.09.22
15:01
+(7) "ПолучитьОписаниеТипов_ОпределяемыеТипы_ПредметНапоминания" это лишнее, вам же только фалы ;)
9 DrZombi
 
гуру
14.09.22
15:04
+ ПолучитьОписаниеТипов_ОпределяемыеТипы_ВладелецПрисоединенныхФайлов - для примера

//ОпределяемыйТип.ВладелецПрисоединенныхФайлов
Функция ПолучитьОписаниеТипов_ОпределяемыеТипы_ВладелецПрисоединенныхФайлов() Экспорт
    
    МассивТипов = Новый Массив;
    
    МассивТипов.Добавить(Тип("СправочникСсылка.ШаблоныСообщений"));
    //
    МассивТипов.Добавить(Тип("ДокументСсылка.СообщениеSMS"));
    //
    МассивТипов.Добавить(Тип("ДокументСсылка.ВключениеВКадровыйРезерв"));
    //

    ОписТипа = Новый ОписаниеТипов(МассивТипов);
    
    Возврат ОписТипа;
КонецФункции
10 sitex
 
naïve
14.09.22
15:07
(0) Если не хочешь снимать с поддержки обновляй платформу и далее создать и хранить в расширении.
11 DrZombi
 
гуру
14.09.22
15:40
+(0) Для самого вашего объекта "Справочника" или "Документа" так же надо подписку на удаление файлов.

ПодпискаНаСобытие.СОГ_УстановитьПометкуУдаленияПрисоединенныхФайловДокументов.Источник //см. в общий модуль "СОГ_РаботаСФайлами.УстановитьПометкуУдаленияПрисоединенныхФайловДокументов"

или Для справочника в "СОГ_РаботаСФайлами.УстановитьПометкуУдаленияПрисоединенныхФайлов"
Ошибка? Это не ошибка, это системная функция.