При переносе внешних обработок и алгоритмов из конфигурации «Зарплата и управление персоналом» редакции 2.5 в современную редакцию 3.1 разработчики часто сталкиваются с ситуацией, когда привычный код перестает компилироваться и выдает ошибки синтаксиса вида «Переменная не определена (Документы)» или «Переменная не определена (Справочники)». Проанализируем, почему возникает эта проблема, чем отличается работа кода в управляемом приложении и как правильно переписать алгоритм загрузки данных из внешнего DBF-файла.
В конфигурациях на обычных формах (таких как ЗУП 2.5) весь код по умолчанию выполнялся на клиенте с полным доступом к объектной модели базы данных. В современной платформе 1С:Предприятие 8.3 при использовании тонкого клиента и управляемых форм (ЗУП 3.1) применяется четкое разделение на клиентский и серверный контексты.
На стороне тонкого клиента (в процедурах с директивой компиляции &НаКлиенте) недоступны менеджеры прикладных объектов:
ДокументыСправочникиПланыВидовРасчетаРегистрыСведений и другие глобальные коллекции манипулирования данными.Попытка обратиться к ним на клиенте прямо приводит к ошибке компиляции. Любые действия по созданию, изменению, поиску и записи документов или справочников должны выполняться исключительно на стороне сервера в процедурах с директивой компиляции &НаСервере или &НаСервереБезКонтекста.
Рассмотрим два основных способа решения этой задачи при переносе обработки на управляемые формы.
Способ 1. Разделение логики на клиентскую и серверную части.
На клиенте выполняется чтение файла и подготовка данных в виде структуры или массива структур, после чего сформированный пакет данных передается серверной процедуре для записи объектов базы данных.
Посмотрим на концептуальный пример реализации:
&НаСервере
Процедура СозданиеДокументаНачисления(ДанныеДокумента)
Док = Документы.НачислениеЗаработнойПлаты.СоздатьДокумент();
ЗаполнитьЗначенияСвойств(Док, ДанныеДокумента);
Док.Записать();
КонецПроцедуры
&НаКлиенте
Процедура ИмпортДанныхДляНачисления()
ДанныеДокумента = Новый Структура;
ДанныеДокумента.Вставить("Комментарий", "Импорт документа");
// Подготовка структуры на клиенте...
СозданиеДокументаНачисления(ДанныеДокумента);
КонецПроцедуры
Способ 2. Выполнение полного цикла обработки на сервере.
В этом случае открытие файла DBF, чтение строк, поиск элементов в справочниках и создание документа выполняются целиком в серверной процедуре. Этот вариант проще в переносе, однако требует учесть нюансы работы с файловой системой на сервере.
При использовании объекта XBase на стороне сервера важно учитывать следующую специфику:
\\Server\ZAR_PLAT\ZARVAL\Sd_Nar.DBF) недоступен учетной записи службы 1CV8 Service, открыть файл на сервере не удастся.ДвоичныеДанные на клиенте, помещение его во временное хранилище через ПоместитьВоВременноеХранилище() и дальнейшая распаковка во временный файл сервера с помощью ПолучитьИмяВременногоФайла("dbf").Рассмотрим готовый пример серверной процедуры Команда2, в которой выполняется чтение файла DBF, поиск видов расчета и сотрудников, а также наполнение табличной части документа начисления.
&НаСервере
Процедура Команда2(Команда)
Перем ТипРасчета;
ДБФ = Новый XBase;
ДБФ.Кодировка = КодировкаXBase.OEM;
ДБФ.ОткрытьФайл("\\Server\ZAR_PLAT\ZARVAL\Sd_Nar.DBF");
Если Не ДБФ.Открыта() Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Не удалось открыть файл базы данных DBF!");
Возврат;
КонецЕсли;
ДБФ.Индексы.Добавить("Shifr", "Shifr", Ложь, Ложь);
ДБФ.СоздатьИндексныйФайл("i_Sd_Nar.cdx");
Сотр = Справочники.Сотрудники;
СпрВидРасчета = ПланыВидовРасчета.Начисления;
Док = Документы.НачислениеЗаработнойПлаты.СоздатьДокумент();
Док.Дата = ТекущаяДата();
ДБФ.Первая();
Пока НЕ ДБФ.ВКонце() Цикл
ВидОплаты = ДБФ.Shifr;
Расчет1 = Неопределено;
Если ДБФ.Shifr = "001" Тогда
Шифр001 = СпрВидРасчета.НайтиПоНаименованию("Сдельные наряды(суммой)", Истина);
Если Не Шифр001.Пустая() Тогда
Расчет1 = Шифр001;
КонецЕсли;
ИначеЕсли ДБФ.Shifr = "005" Тогда
Шифр005 = СпрВидРасчета.НайтиПоНаименованию("Сверхурочные(суммой)", Истина);
Если Не Шифр005.Пустая() Тогда
Расчет1 = Шифр005;
КонецЕсли;
ИначеЕсли ДБФ.Shifr = "050" Тогда
Шифр050 = СпрВидРасчета.НайтиПоНаименованию("Премия месячная(суммой)", Истина);
Если Не Шифр050.Пустая() Тогда
Расчет1 = Шифр050;
КонецЕсли;
КонецЕсли;
КолСтрок = 0;
Пока (НЕ ДБФ.ВКонце()) И (ВидОплаты = ДБФ.Shifr) Цикл
ТекСтрока = Док.Начисления.Добавить();
КолСтрок = КолСтрок + 1;
ТекСтрока.ВидРасчета = Расчет1;
Сотр1 = Сотр.НайтиПоКоду(ДБФ.TabNom);
Если Не Сотр1.Пустая() Тогда
ТекСтрока.Сотрудник = Сотр1;
КонецЕсли;
ТекСтрока.Результат = ДБФ.Summa;
ТекСтрока.ДатаНачала = ДБФ.Dat_Beg;
ТекСтрока.ДатаОкончания = ДБФ.Dat_End;
ДБФ.Следующая();
КонецЦикла;
КонецЦикла;
ДБФ.ЗакрытьФайл();
Попытка
Док.Записать(РежимЗаписиДокумента.Запись);
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Документ успешно создан и записан!");
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка при записи документа: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
При переносе кода на сервер обратите внимание на вызовы интерактивных функций:
Предупреждение(), Вопрос()) **недопустимы** в серверном коде. Их нужно выносить на клиент либо отключать.ОбщегоНазначенияКлиентСервер.СообщитьПользователю(). Обычный метод Сообщить() на сервере также работает, однако функции БСП обеспечивают корректную привязку сообщений к элементам формы.ПланыВидовРасчета.Начисления, а основным документом расчета зарплаты за месяц — Документы.НачислениеЗаработнойПлаты (или специализированные документы выработки, такие как Документы.СдельныйНаряд).