Бартеньев О. В. 1С:Предприятие: программирование для всех. Базовые объекты и расчеты на одной дискете. - М.: Диалог-МИФИ...
421 downloads
338 Views
6MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Бартеньев О. В. 1С:Предприятие: программирование для всех. Базовые объекты и расчеты на одной дискете. - М.: Диалог-МИФИ, 2005. - 464 с.
Рассматриваются элементы встроенного языка.программирования 1С:Предприятие и методы написания программ на этом языке. Также на примере компонента Расчеты демонстрируется технология построения заказных, использующих модели 1С оптимальных систем. В качестве критериев оптимизации выступают быстродействие, надежность программ, уровень защищенно сти данных и др. Дополнительно приводятся справочные сведения об использованных в пособии объектах 1С и их методах. Включенные в пособие примеры программ и компоненты предложен ной облегченной версии системы размещены на дискете, которую можно приобрести в издательстве. Предназначено для всех лиц, желающих освоить или усовершенствовать технику разработки программ на основе моделей 1С:Предприятие, а также для руководящих работников, имеющих потребность расширить свои знания относительно характера и способов построения систем ав томатизации деятельности административно-хозяйственных подразделений организаций различ ного профиля.
Учебно-справочное издание Бартеньев Олег Васильевич 1С:Предприятие: программирование для всех Базовые объекты и расчеты на одной дискете
ПРЕДИСЛОВИЕ Широкое распространение системы 1С:Предприятие (далее вместо длинного на звания "Система 1С:Предприятие" будем, где это возможно, употреблять более корот кие названия 1С и система) вызывает к жизни разнообразную литературу, освещаю щую различные стороны программы. Имеющаяся на прилавках магазинов литература содержит преимущественно сведения для пользователей системы - работников бухгал терии, отделов снабжения, сбыта и иных административно-хозяйственных подразде лений предприятия. Однако для функционирования системы одних пользователей недостаточно: с сис темой должен постоянно работать ее сопровождающий специалист, имеющий обшир ный круг обязанностей как по администрированию программы, так и по ее усовершен ствованию. Возможно, вы хотите приобрести такие навыки - тогда эта книга для вас. Возмож но, вы озабочены поиском такого специалиста, тогда в этой книге есть ответ на воп рос, какими профессиональными навыками должен обладать подбираемый вами ра ботник. Если же вы реализуете средствами 1С некоторый проект, вам тоже полезна эта книга, поскольку всегда не будет лишним сопоставить различные подходы и поста раться выбрать в результате наилучший. В книге решаются, по сути, три задачи. Первая - оснастить лиц, желающих нау читься писать программы в 1С, необходимыми приемами и сведениями. Вторая - рас смотреть оптимизационный подход к построению или модификации системы и, реали зуя его, решать первую задачу. И третья - снабдить ответственных лиц информацией об объеме и характере проблем, которые приходится улаживать при настройке систе мы и ее сопровождении. Характер изложения таков, что после описания базовых понятий раскрываются этапы реализации облегченной системы расчета зарплаты на основе моделей 1С (дру гие задачи, например бухгалтерии, строятся на аналогичных моделях). Далее обсуж даются вопросы эффективности системы и комплекс мер по ее повышению. Попутно излагаются справочные сведения о встроенном языке программирования системы 1С:Предприятие 7.7. На вопрос, зачем взамен поставляемых фирмой средств разрабатывать альтернати вы, ответ известен. Практически на любом предприятии при внедрении системы стоят задачи ее адаптации и оптимизации, собственно для решения которых и приспособлен встроенный язык программирования 1С. Как правило, предприятие, внедряющее систему, по причине недостаточной под готовки персонала возлагает решение этих задач на сторонних лиц и организации так называемые франчайзи* (партнеры) 1С. Иногда это экономически обременительно (час работы представителя партнера оценивается в 20-30 у. е.). Поэтому зачастую шаги
Франчайз, или франшиза, - это право на создание коммерческого предприятия и на торговлю продукцией старшего партнера. Франчайзи, или франшизы, - это лицо или организация, поку пающие франчайз (франшизу).
по настройке программы являются половинчатыми и система не выходит на соответ ствующий ее возможностям уровень функционирования. Согласитесь, что во многих случаях выгоднее иметь в штате специалиста (или двух), сопровождающего систему, знающего ее особенности и понимающего за дачи ее развития, чем полагаться на структуры массового обслуживания. С выходом этой книги подготовка таких специалистов упростится, а их число увеличится. По существу, при надлежащем прилежании им может стать любой имеющий склон ность к программированию сотрудник предприятия, привлекаемый руководством к внедрению и сопровождению системы. Включенные в пособие примеры программ и компоненты предложенной об легченной версии системы размещены на дискете, которую можно приобрести в издательстве. Этот же материал находится в Интернете на сервере издательства (www.bitex.ru/~dialog). Эта дискета позволит вам посмотреть в работе представ ленные в пособии объекты, отдельные обработки, а также реализованную облег ченную версию конфигурации системы. Это станет возможным (если вам доступна программа 1С:Предприятие версии 7.7) сразу после выполнения изложенных в прил. 2 инструкций.
1. НАЧАЛЬНЫЕ СВЕДЕНИЯ 1.1. ВВЕДЕНИЕ Система 1С:Предприятие, версии 7.7 эксплуатируется на большом числе рос сийских предприятий. Комплексная поставка включает следующие компоненты: Бухгалтерия, Торговля, Склад, Заработная плата, Кадры, Услуги и Производство. Субъекты хозяйственной деятельности (предприятия, фирмы и др.) нередко ис пользуют часть возможностей системы, например подсистему Бухгалтерия, или Торговля и Склад, или Зарплата и Кадры. Такие подсистемы в 1С называются кон фигурациями. Приспособление системы к потребностям предприятия осуществляется средства ми встроенного языка программирования, первые шаги по освоению которого делают ся в этой главе. Встроенный язык программирования 1С оперирует большим числом сложных объектов (около 30), предлагая для них исчерпывающий набор методов и предопреде ленных процедур. По существу, освоение языка и состоит в изучении этих объектов и приобретении навыков их употребления в алгоритмах и реализующих их програм мах. Однако подойти к этой цели мы сможем, лишь пройдя необходимые этапы освое ния языка. На первом этапе мы научимся запускать тривиальные программы. На втором. вкладывать в эти программы некоторый смысл. На третьем - оснащать их базовы ми конструкциями языка, с пониманием строить числовые, символьные, логиче ские выражения и выражения с датами, употребляя в них имеющиеся в языке опе рации и встроенные функции. И так далее. И тогда любой имеющий тягу к программированию пользователь познает язык системы, что пойдет на благо как ему самому, так и организации, в которой он служит. Для освоения материала читателю не требуется иметь специальных навыков программирования, а достаточно обладать знаниями в пределах школьного курса информатики или первого курса высшего учебного заведения. Это обусловлено тем, что из ложение предмета носит, как правило, неформальный характер, строится по принципу от простого к сложному и иллюстрируется большим числом примеров.
1.2. ИНТЕРФЕЙС УЧЕНИК Создадим для учебных целей простой, не обремененный избыточными кнопками и пунктами меню пользовательский интерфейс, дав ему имя Ученик. Запустим для этого систему в режиме Конфигуратор и выберем некоторую информационную базу, например Расчет зарплаты и кадровый учет (рис. 1.1), или любую иную.
Рис. I. 1. Запуск системы Откроем после запуска системы ее конфигурацию, выбрав пункты меню Конфигу рация - Открыть конфигурацию (рис. 1.2).
Рис. 1.2. Открываем конфигурацию системы В отобразившемся окне выберем закладку Интерфейсы. Ударим по правой кнопке мыши и в появившемся меню выберем пункт Новый интерфейс (рис. 1.3).
Рис. 1.3. Ввод нового интерфейса В окне Свойства пользовательского интерфейса в поле Название введем слово Ученик и отключим меню Операции (рис. 1.4).
Рис. 1.4. Задание основных свойств интерфейса
Нажав на кнопку ОК, получим окно, содержащее интерфейс Ученик (рис. 1.5).
Рис. 1.5. Закладка с интерфейсом Ученик Добавим теперь в меню интерфейса Ученик колонку Проба. Разместим для этого на элементе Ученик курсор, нажмем правую кнопку мыши или дважды ударим по ее ле вой кнопке и выберем в появившемся списке возможностей пункт Редактировать меню. В появившемся окне разместим курсор на элементе Ученик, задействуем правую кнопку мыши и в раскрывшемся списке выберем пункт Свойства (рис. 1.6, а). В окне задания свойств в поле Название введем имя Проба (рис. 1.6, б) и нажмем на кнопку ОК.
Рис. 1.6. Изменение имени колонки меню: а - вызов окна ввода свойств колонки меню; б - общие свойства колонки Вставим далее в колонку Проба меню интерфейса Ученик пункт Пуск. Ударим для этого мышью по крестику, расположенному перед элементом Проба, разместим кур сор на пункте <новый...> и вновь приведем в действие правую кнопку мыши. В поя вившемся списке выберем пункт Свойства (рис. 1.7, а) и в поля окна задания свойств введем указанные на рис. 1.7, б значения.
а
б Рис. 1.7. Добавление пункта Пуск: а - вызов окна ввода свойств элемента меню; б - общие свойства элемента
Замечание. Для задания объекта Отчет достаточно разместить курсор на поле Объ ект и нажать на клавиатуре на русскую букву О (строчную или прописную). На следующем шаге выберем закладку Акселератор, нажмем на кнопку Выбрать акселератор и задействуем сочетание клавиш, например Alt+1, которое затем будем использовать для вызова связанного с пунктом меню Пуск файла. После нажатия на кнопку ОК просмотрим созданное меню, нажав на правую кноп ки мыши и выбрав пункт Тест. Результат тестирования приведем на рис. 1.8.
Рис. 1.8. Просмотр меню интерфейса Ученик Замечание. На самом деле в соответствии с рис. 1.4 колонка Операции при исполь зовании интерфейса Ученик в 1С:Предприятии будет отсутствовать. Имя файла, который будет связан с пунктом меню Пуск, зададим несколько позже.
1.3. ПОЛЬЗОВАТЕЛЬ ПО ИМЕНИ УЧЕНИК Чтобы создать такого пользователя, выберем пункты меню Администрирование Пользователь и в появившемся окне (рис. 1.9), нажав на правую клавишу мыши, выбе рем пункт Новый и зададим в окне свойств атрибуты (рис. 1.10, а) И роль (рис. 1.10, б) пользователя.
Рис. 1.9. Список пользователей
а
б
Рис. 1.10. Характеристики пользователя Ученик: а - атрибуты; б - роль Замечания: 1. В поле Рабочий каталог рис. 1.10, а установлен путь к папке, содержащей инфор мационную базу данных системы. 2.
Вопросы задания прав доступа пользователей рассматриваются в разд. 8,5. Пока что установим пользователю Ученик полные права (рис. 1.10, б).
При необходимости для пользователя Ученик в окне, изображенном на рис. 1.9, можно установить пароль, который придется вводить при входе в систему. Для зада ния или смены пароля воспользуйтесь иконкой с изображением замка или нажмите на правую кнопку мыши и выверите пункт изменить пароль. Введенные данные, разумеется, надо сохранить. Теперь при входе в систему нам нужно будет выбирать пользователя с именем Ученик и вводить пароль, если он задан (рис. 1.11).
Рис. 1.11. Вход в систему Добавим, что при желании на закладке Права интерфейса Ученик (см. рис. 1.5) можно в набор прав включить новый элемент, например права Ученика, и задать соот ветствующие права, отличающиеся от установленных нами, а затем установить эти права нашему пользователю на закладке Роль, приведенной на рис. 1.10, б.
1.4. ОБРАБОТКА ПО ИМЕНИ ПРОБА 1.4.1. ДИАЛОГ ОБРАБОТКИ Обработкой называется программа, запускаемая в результате ее вызова из некото рой экранной формы. Под формой в общем случае понимается совокупность следующих компонентов: • диалога формы; • модуля формы; • таблиц формы (по умолчанию в форме задана одна таблица); • описания формы. Доступ к компонентам формы осуществляется в результате выбора соответствую щей закладки в окне создания и редактирования формы (рис. 1.12).
Рис. 1.12. Закладки окна создания и редактирования формы Для наших учебных целей мы создадим внешнюю обработку, назвав ее Проба, и будем размещать в ней наши программы. Доступ к обработке обеспечим из пункте .меню Пуск (рис. 1.8). Также обработка может быть вызвана в результате набора соче тания клавиш Alt+1, если для пункта меню Пуск задан такой акселератор (механизм задания акселератора описан выше).
Создание обработки выполним в конфигураторе, выбрав пункт меню Новый отчет колонки Конструкторы (специального конструктора обработок в 1С не предусмотре но). Затем активизируем кнопку Внешний отчет и зададим имя файла обработки, ис пользовав расширение ERT (рис. 1.13).
Рис. 1.13. Задание внешней обработки Заметим, что внешние отчеты и обработки система предлагает сохранить в папке ExtForms (внешние формы). Нажмем, не производя иных действий, дважды на кнопку Далее и в конце на кноп ку Готово. В появившемся окне Внешний отчет (обработка) - npoбa.ert, оставаясь на закладке Диалог, во-первых, полностью раскроем окно, во-вторых, захватив мы шью нижний правый угол диалога, уменьшим его размеры и, в-третьих, перенесем кнопки Сформировать и Закрыть в центр преобразованного диалога (рис. 1.14, а).
а
б
Рис. 1.14. Диалог обработки Проба: а - заданные по умолчанию кнопки; б - кнопки диалога после их редактирования Для выполнения последних действий следует нажать левую кнопку мыши и за ключить при помощи мыши, не отпуская кнопку, названные элементы диалога в пря моугольную область. Затем отпустить нажатую кнопку мыши, переместить мышь в не которую точку выделенной области и вновь, задействовав левую кнопку мыши, пере местить теперь уже выделенную область в требуемое место. Произведем теперь действия, которые, в общем-то, не нужны для запуска обработ ки, но полезны для приобретения навыков по редактирования диалогов. Присвоим левой кнопке диалога имя Пуск, установив на ней мышь, ударив вслед по ее правой кнопке, выбрав в появившемся меню пункт Свойства и введя в поле За головок текст Пуск (рис. 1.15, а).
а
б
Рис. 1.15. Задание свойств элемента диалога: а - общих; б - дополнительных Откроем затем закладку Дополнительно и в поле Формула взамен текста Сформи ровать( ) разместим текст Выполнить( ) (рис. 1.15, б). Это изменение означает, что по сле нажатия на кнопку Пуск будет запускаться процедура обработки под именем Вы полнить (если таковая определена в модуле обработки). На закладке Описание введем текст Запуск учебной программы. Чтобы использовать этот текст в качестве подсказ ки, проставим флажок Использовать описание (рис. 1.16) и сохраним, нажав наОК, изменения.
Рис. 1.16. Описание и подсказ.л элемента диалога Приведем диалог к виду, представленному т. рис. 1.14. б, употребив для центри рования по горизонтали кнопок Пуск и Закрыть иконку инструментов Редактор диалога (рис. 1.17).
, расположенную на панели
Рис. 1.17. Панель инструментов Редактор диалога Для просмотра диалога можно нажать Ctrl+R или выбрать иконку веденной панели инструментов.
на вышепри
Замечания: 1. Перечень имеющихся в системе панелей инструментов выводится в окне рис. 1.18, а, отображаемом после выбора пункта меню Панели инструментов в колонке Сервис.
а
б
Рис. 1.18. Панели инструментов: а - выбор отображаемых панелей; б - настройка панелей 2. Рассматривая компоненты системы, мы не будем без необходимости подробно ос танавливаться на всех их возможностях. Так, в случае панели инструментов Редак тор диалога мы обратили внимание на две его иконки: Назначение осталь ных иконок этой панели легко понять, испытав их воздействие на выделенные эле менты диалога и их группы или обратившись к закладке Модификация окна управ ления панелями инструментов (рис. 1.18,6). Не забудем связать файл Проба.ert с пунктом меню Пуск колонки Проба из меню интерфейса Ученик. Для этого откроем конфигурацию, выберем закладку Интерфей сы, в ней перейдем на интерфейс Ученик, вызовем редактор меню, дойдем до элемента Пуск и дважды ударим по нему мышью. Выберем в появившемся окне закладку Пара метры и определим в ней полное имя файла, связанного с пунктом меню Пуск (рис. 1.19). Заметим, что имя файла можно задать, указав в нем путь относительно ка талога с базами данных.
Рис. 1.19. Ввод имени файла Сохраним измененную конфигурацию. При этом система 1C:Предприятие должна быть закрыта.
1.4.2. ПЕРВАЯ ПРОГРАММА Перейдем на закладку Модуль (см. рис. 1.12). На этой закладке располагаются программы, выполняющие обработку. В нашей первой программе мы объявим одну числовую переменную, присвоим ей некоторое значение и выведем его в окно сообщений. Здесь следует отметить, что программы (диалоги, таблицы) создаются в конфигу раторе системы, запустить их на выполнение в конфигураторе нет возможности (мож но, правда, проверить программу на наличие синтаксических ошибок, выбирая иконку
на панели инструментов Текстовый редактор). Для запуска обработки потребуется вызвать 1С:Предприятие, нажав, например, на иконку или клавишу F11. Итак, перейдя на закладку Модуль, мы обнаружим там следующую заготовку: процедура Сформировать( ) конецПроцедуры Поскольку в диалоге на закладке Дополнительно текст Сформировать( ) был заме нен на текст Выполнить( ), то те же изменения произведем и в имеющейся заготовке, затем добавим код, выполняющий ранее намеченные действия. Все пояснения будем размещать в создаваемом коде в виде комментария, который располагается после сим волов //. процедура Выполнить( ) // Выполнить - имя процедуры перем а; // Объявляем локальную переменную с именем а а = 5.1; // = - знак оператора присваивания Сообщить(а); // Выводим значение переменной а в окно сообщений конецПроцедуры // Выполнить (комментарий с именем законченной процедуры) Замечание. Запись а=5.1; является оператором присваивания. В результате его выполнения переменная а полу чит значение 5.1. В общем случае оператор присваивания имеет следующий вид: имяПеременной = выражение;
//
Завершаем оператор присваивания точкой с запятой
Также операторами в приведенной процедуре являются неисполняемый оператор объявления переменной а: перем а;
//
Объявляем локальную переменную с именем а
и исполняемый оператор вызова встроенной процедуры: Сообщить(а);
//
Выводим значение переменной а в окно сообщений
Операторы, если они не являются составными элементами конструкции, например конструкции "если - то - иначе", завершаются точкой с запятой. Выполним проверку синтаксиса программы и, убедившись в ее корректности, соххраним обработку, нажав Ctrl+S или воспользовавшись пунктом меню Сохранить колонки Файл. Откроем описанным выше способом 1С:Предприятие и в появившемся меню найдем колонку Проба и ее пункт Пуск или воспользуемся для вызова обработки Проба сочетанием клавиш Alt+1. Нажмем на кнопку Пуск и просмотрим окно сообщени В нем выведено число 5.1. Закроем обработку Ученик. Замечание. Имена переменных, процедур и функций в программах 1С могут с держать буквы русского и английского алфавитов (строчные и прописные), символ подчеркивания и символы цифр, например _новоеИмя_23. Имя не может начинать с цифры и содержать пробелы.
1.4.3. ОБСУЖДЕНИЕ ПЕРВОЙ ПРОГРАММЫ Программа состоит из одной пользовательской процедуры, имеющей имя Выпол нить и размещенной в модуле формы обработки. Напомним, что процедура - это вы полняющий некоторые действия программный компонент, который обменивается данными с другими компонентами через свои параметры. Также в процедурах 1С мо гут использоваться переменные модуля, диалога и глобальные объекты данных. (В нашем случае, правда, процедура параметров не имеет и работает только с одной локальной переменной а.) В процедуре Выполнить использована встроенная процедура Сообщить. Она вы водит значение своего аргумента в окно сообщений системы. Заметим, что более ин формативен следующий вывод значения переменной а: Сообщить("а =" + а); Процедура Сообщить сработает следующим образом. Прежде числовое представ ление переменной а будет преобразовано в символьное (число 5.1 будет преобразова но в строку "5.1"). Затем строка "а = " объединится со строкой "5.1" и в окне сообще ний отобразится текст а = 5.1 Отметим общие для всех программ моменты: 1. При написании программы регистр букв не имеет значения. Так, имя процедуры не изменится, если его написать прописными буквами; имена А и а задают одну и ту же переменную. 2.
После каждого оператора необходимо проставлять точку с запятой.
3.
В процедурах и функциях 1С можно не объявлять явно оператором Перем скаляр ные переменные (не массивы), а вводить переменные и определять их тип по мере необходимости. То есть оператор перем а; в приведенной процедуре Выполнить может быть опущен; числовая переменная а будет введена в результате выполнения оператора присваивания а = 5.1;
4. Поскольку программы пишутся в конфигураторе, а запускаются в 1С:Предприятии, то для ускорения процесса их отладки следует держать открытыми как конфигура тор, так и 1С:Предприятие; если в 1С:Предприятии открыта старая версия про граммы (в нашем случае обработки), то после внесения и сохранения исправлений (это выполняется в конфигураторе) старую версию нужно закрыть и загрузить за тем обновленный файл. В нашем случае для этого достаточно набрать Alt+1. (О мероприятиях по ускорению отладки см. также разд. 1.11.) Замечание. Созданная обработка Проба находится в файле Проба.ert, и если файл по какой-либо причине закрыт, то для его загрузки в конфигураторе следует выпол нить стандартные действия по открытию файла, например выбрать из списка недавно открытых файлов, имеющегося в колонке Файл меню конфигуратора.
1.4.4. МОДУЛЬ ОБРАБОТКИ ПРОБА Процедура Выполнить принадлежит модулю обработки. Модуль обработки в об щем случае может содержать следующие компоненты: • • •
объявления переменных модуля, которые доступны в любом его программном компо ненте; процедуры, в том числе и предопределенные, и функции, созданные пользователем; операторы основной программы модуля, следующие за его процедурами и функциями; основная программа выполняется один раз при загрузке модуля.
Запишем в качестве примера в модуле обработки код, содержащий объявление переменной модуля а, пользовательскую процедуру Выполнить и основную програм му из одного оператора. // Объявляем переменную модуля обработки // Она может быть использована в любой процедуре (функции) модуля // и его основной программе перем а; процедура Выполнить( ) // Выполнить - имя процедуры перем б; // Локальная переменная процедуры Выполнить Сообщить("а = " + а); // Выводим значение переменной а в окно сообщений б = 4.2; // Определяем значение локальной переменной б Сообщить("Сумма а и б равна " + (а + б)); конецПроцедуры // Выполнить // Основная программа модуля состоит из одного оператора а = 5.1; // Определяем значение переменной модуля После загрузки и запуска обработки в 1С:Предприятие в окне сообщений выведут ся две следующие строки: а=5.1 Сумма а и б равна 9.3 Замечания: 1. Нельзя изменить использованный порядок следования компонентов модуля То есть ошибочен, например, код перем а;
//
Объявляем переменную модуля
// Основная программа модуля; такое ее расположение в модуле ошибочно а = 5.1; // Определяем значение переменной модуля процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем б; // Локальная переменная процедуры Выполнить Сообщить("а = " + а); // Выводим значение переменной а в окно сообщений б = 4.2; // Определяем значение локальной переменной б Сообщить("Сумма а и б равна " + (а + б)); конецПроцедуры // Выполнить
2. Если употребить вызов Сообщить("Сумма а и б равна " + а + б);
//
а + б без круглых скобок
то в окне сообщений появится текст а=5.1 Сумма а и б равна 5.14.2 То есть без круглых скобок в символьное представление преобразовываются по от дельности переменные а и б, а не их сумма. 3. Если убрать объявление переменной модуля а, то переменная а основной програм мы станет локальной (область ее действия будет распространяться только на основ ную программу) и, следовательно, недоступной в процедуре Выполнить, написан ная программа окажется неработоспособной. Процедура Выполнить вызывается при нажатии кнопки Пуск диалога обработки Проба (см. рис. 1.14, б). В общем случае процедура модуля может быть вызвана как из диалога, так и из иного программного компонента модуля, в том числе и из его ос новной программы. Например, после загрузки обработки, в модуле которой содержит ся код процедура Выполнить( ) // б = 4.2; // Сообщить("б = " + б); конецПроцедуры // Выполнить
Связана с кнопкой Пуск обработки Проба Определяем значение локальной переменной б
// Основная программа модуля ОчиститьОкноСообщений( ); // Очищаем окно сообщений а = 5.1; // Определяем значение локальной переменной а Сообщить("а = " + а); // Процедура Выполнить будет вызвана из основной программы при загрузке обработки Проба Выполнить(); // Конец основной программы сразу будет исполнен код основной программы и, следовательно, процедуры Выпол нить, в результате чего в окне сообщений появится текст а = 5.1 б = 4.2 Последующие нажатия на кнопку Пуск также будут приводить к вызову процеду ры Выполнить и, следовательно, добавлять в окно сообщений строку б = 4.2 Код основной программы модуля для исполнения более недоступен. Заметим, что допускаются рекурсивные вызовы процедур, то есть вызовы, в кото рых процедура вызывает сама себя. Приведем теперь пример пользовательской функции модуля. Напомним, что функция - это программный компонент, возвращающий значение и вызываемый из выражения, которое это значение использует. Например, в операторе у = 2 * Лог(3.4);
//
Лог - встроенная функция
использована встроенная функция Лог, имеющая в качестве входного параметра по ложительное число и возвращающая в качестве результата числовое значение, равное натуральному логарифму параметра. Функция вызывается из выражения 2 * Лог(3.4). Разместим в модуле нашей обработки следующий код: функция ВычислитьУ(х) // х - формальный параметр функции ВычислитьУ у = 2 * Лог(х); // Лог - встроенная функция // Вернем в выражение, из которого вызывается функция ВычислитьУ, значение у возврат у; конецФункции // ВычислитьУ процедура Выполнить() // б = ВычислитьУ(3.0); // ОчиститьОкноСообщений(); // Сообщить("б = " + б); конецПроцедуры // Выполнить
Связана с кнопкой Пуск обработки Проба Функция ВычислитьУ вернет 21п(3) Очищаем окно сообщений
После исполнения набранного кода (для этого измененную обработку надо открыл в 1С:Предприятии и нажать на кнопку Пуск) в окне сообщений появится строка б = 2.1972245773362 Замечание. Нельзя в теле функции объявлять ее формальные параметры. Так, ошибочен код функция ВычислитьУ перем х;
(х) //
//
х-
формальный параметр функции Это объявление недопустимо
ВычислитьУ
Функция ВычислитьУ должна быть размещена в модуле до процедуры Выполнить, из которой функция вызывается. Если вы хотите записать код функции ВычислитьУ после процедуры Выполнить, то в Начале модуля надо привести предварителъ нов описание функции ВычислитьУ, завершив его словом Далее, например: функция ВычислитьУ(х) далее процедура Выполнить()
//
Предварительное описание функции ВычислитьУ
//
Код процедуры Выполнить
//
Код функции ВычислитьУ
конецПроцедуры // Выполнить функция ВычислитьУ(х) конецФункции // ВычислитьУ 1.
Замечания: В общем случае предварительное описание определенных в модуле процедур и функций можно размещать как в начале модуля после объявления его переменных, так и между его любыми программными компонентами.
1.5. ПЕРЕМЕННЫЕ ДИАЛОГА Переменные модуля и локальные переменные его программных компонентов объ являлись и определялись, то есть получали значения, в самом модуле. Переменные модуля доступны во всех его программных компонентах, локальные - только в том компоненте, где они были объявлены явно или неявно, появившись в левой части опе ратора присваивания. Кроме таких переменных, в модуле можно оперировать и пере менными диалога, которые объявляются в диалоге как идентификаторы его элементов. Область действия переменных диалога распространяется на все программные компо ненты модуля, то есть они имеют такой же статус, как и переменные модуля. Замечание. У переменных диалога есть другие названия - реквизиты формы и идентификаторы элементов диалога. Рассмотрим пример использования переменных диалога. Приведем диалог, отоб раженный на рис. 1.14, б, к виду, представленному на рис. 1.20, добавив в него два элемента - текст и числовое поле для ввода и редактирования данных.
Рис. 1.20. Диалог с числовым полем Текст добавляется после выбора мышью иконки панели инструментов Элемен ты диалога (рис. 1.21) и позиционирования курсора на диалоговом окне в точке начала размещения текста.
Рис. 1.21. Панель инструментов Элементы диалога После позиционирования курсора появится окно задания свойств текста, в кото ром его общие свойства определим в соответствии с рис. 1.22.
Рис. 1.22. Общие свойства текста Поле ввода и редактирования данных попадет в диалог после выбора иконки панели инструментов Элементы диалога. Общие свойства поля и его тип зададим в со ответствии с рис. 1.23.
6 а Рис. 1.23. Свойства поля ввода и редактирования данных: а - общие свойства; б - тип поля После вставки элементов отрегулируем их размеры и положение (для этого предварительно элемент выделяется одним ударом мыши, а затем вновь мышью модифицируются его геометрические характеристики). Для выравнивания новых элементе по левой границе предварительно выполним их выделение, а затем используем икот панели инструментов Редактор диалога (рис. 1.17). С полем ввода и редактирования данных мы связали его идентификатор, которы как мы уже говорили, интерпретируется в программных компонентах модуля как переменная. Сохраним обработку и продолжим ее редактирование. Напишем в модуле обработки код, меняющий значения переменной дЧ, добавим код процедуры ПриОткрытии и проследим поведение диалога после выполнения этой процедуры, а затем после неоднократного нажатия на кнопку Пуск. перем а;
//
Переменная модуля
процедура Выполнить() // Связана с кнопкой Пуск обработки Проба // Встроенное процедура Предупреждение выводит текст, // переданный процедуре в качестве параметра, в окно с кнопкой ОК Предупреждение("3начение числового поля будет увеличено в 2 раза."); дЧ = дЧ * а; // Результат после загрузки и двух нажатий конецПроцедуры // на кнопку Пуск см. на рис. 1.24 процедура ПриОткрытии() ОчиститьОкноСообщений(); а = 2.0; // дЧ = 5.23; // конецПроцедуры // ПриОткрытии
а
//
Очищаем окно сообщений Начальное значение переменной модуля а Начальное значение переменной диалога дЧ
б Рис. 1.24. Поле дЧ диалога обработки Проба: а - после открытия диалога; б - после двукратного нажатия на кнопку Пуск
Замечание. Имеющаяся в коде процедура ПриОткрытии является предопределен ной и выполняется, как это следует из ее названия, в момент открытия диалога. При необходимости процедуру можно вызвать и из иного программного компонента моду ля, например из процедуры Выполнить. Для этого, однако, потребуется либо добавить перед процедурой Выполнить оператор предварительного описания процедура ПриОткрытии() далее либо разместить процедуру ПриОткрытии перед процедурой Выполнить. С диалогом обработки связаны и другие предопределенные процедуры, например ПриЗакрытии или ПриВыбореЗакладки. Переменная диалога дЧ станет недоступной во всех программных компонентах модуля, если в модуле обработки объявить переменную модуля дЧ: перем дЧ;
//
Объявляем переменную модуля
Если же такое объявление сделать в каком-нибудь программном компоненте мо дуля, например в процедуре Выполнить, то переменная диалога дЧ станет недоступ ной в этом программном компоненте. В таких случаях говорят: "Локальная перемен ная закрывает глобальную переменную". Таким же образом можно закрыть в процеду ре или функции модуля его любую переменную. Каких-либо предупреждений о закры тии переменных компилятором не выдается. Избежать непредвиденных закрытий переменных будет легче, если придумывать имена переменным, опираясь на некоторую систему, например начинать переменные диалога с буквы д или использовать так называемую венгерскую нотацию. В соответ ствии с ней имя объекта снабжается префиксом из строчных букв, указывающих его тип. Последующая часть имени раскрывает его смысл. Причем каждая часть имени, отражающая отдельный смысловой компонент, начинается с прописной буквы. На пример, имя переменной сНазваниеПодразделения говорит нам не только о том, что в ней хранится, но и о том, что тип переменной является символьным.
1.6. ГЛОБАЛЬНЫЕ ИМЕНА Имена объектов (переменных, процедур и функций), определенных в модуле об работки, доступны только в самом модуле (в модуле, например, другой обработки эти имена недоступны). Причем области действия переменных модуля и локальных пере менных его программных компонентов различны, а область действия имени процеду ры (функции) модуля - это программные компоненты, расположенные в модуле вслед за рассматриваемой процедурой (функцией). Чтобы распространить область действия имени процедуры (функции) на весь модуль или его часть, нужно в соответствующем месте дать предварительное описание этой процедуры (функции). В то же время в системе существуют по-настоящему глобальные имена - это: • имена встроенных процедур, функций, атрибутов и методов. Так, функция ТекущееВремя вернет символьное представление времени, которое показывают часы вашего компьютера. Например: Сообщить(ТекущееВремя());
//
Вернет, например, 17:14:37
• •
имена системных констант, например имя РазделительСтрок; имена объектов, определенных в глобальном модуле системы и снабженных атрибу том Экспорт, например: // Глобальная переменная глобального модуля; // объявлена до начала кода процедур и функций глобального модуля Перем Вычеты Экспорт; Функция глНомерРелиза() Экспорт возврат "7.70.028"; КонецФункции // глНомерРелиза
•
//
Функция глобального модуля
имена метаданных, то есть имена объектов, входящих в. конфигурацию системы, на пример имена справочников или видов расчетов и их групп. Замечания:
1. Чтобы просмотреть глобальный модуль, прежде, находясь в конфигураторе, сле дует открыть конфигурацию (Конфигурация - Открыть конфигурацию), а затем в появившейся в меню колонке Действия выбрать пункт Глобальный модуль. 2.
Метаданные - это определенные в конфигурации системы данные со сложной структурой, позволяющие управлять другими, более низкого уровня данными. На пример, тип Справочник.Сотрудники имеет более 20 компонентов, такие, как Идентификатор, Синоним и др., причем некоторые из компонентов типа также об ладают сложной структурой. С объектами, тип которых описан в метаданных, свя заны методы - процедуры и функции, позволяющие выполнять некоторые дейст вия с объектами или его компонентами. Например: // Создаем переменную типа Справочник.Сотрудники сСотр = СоздатьОбъект("Справочник.Сотрудники"); // Перемещаемся на элемент справочника, в котором значение реквизита // Наименование начинается с буквы А. Для позиционирования // используем метод НайтиПоНаименованию сСотр.НайтиПоНаименованию("А", 0); // Выводим значение реквизита Наименование найденного элемента справочника // Возможный результат: Абасова Татьяна Анатольевна Сообщить(сСотр.Наименование);
Пример использования имени, определенного в глобальном модуле. Вывести в ок не сообщений регистрационный номер системы. Напишем в модуле обработки Проба простой код (взамен существующего): процедура Выполнить() // Сообщить(глНомерРелиза( )); конецПроцедуры // Выполнить
//
Связана с кнопкой Пуск обработки Проба Выведет сообщение, например 7.70.028
сохраним изменения, загрузим обработку в 1С:Предприятие (Alt+1) и нажмем на кноп ку Пуск. Работу с метаданными продолжим в следующем разделе, рассматривая объект Константы.
1.7. КОНСТАНТЫ 1.7.1. ДОСТУП К КОНСТАНТАМ Определяемые в конфигурации 1С константы хранят неизменяемые при расчетах и формировании документов данные, например размер минимальной заработной пла ты (в процессе эксплуатации системы значения подобных констант, разумеется, могут редактироваться). Значения констант располагаются в файле 1SCONST.DBF. Для внешнего представления данных с каждой записью файла связываются атрибуты и идентификатор (имя) некоторой константы, значения которых записаны в файле 1CV7.MD, хранящем конфигурацию системы. При отображении констант предусмот ренным в 1С способом (команда Константы.Открыть; может быть выполнена только в главном меню системы) используются ее атрибуты Синоним и Комментарий, а также значения поля Value файла 1SCONST.DBF. Замечание. Файл 1 SCONST.DBF содержит не только константы системы, но и дру гие данные, например значения периодических атрибутов справочников. Чтобы иметь возможность просматривать и редактировать константы, добавим, нахо дясь в конфигураторе, в меню интерфейса Ученик колонку Справочники, а в этой колонке создадим пункт Константы, имеющий приведенные на рис. 1.25 общие свойства.
Рис. 1.25. Общие свойства пункта меню Константы Загрузим далее 1С:Предприятие и откроем список констант (рис. 1.26).
Рис. 1.26. Фрагмент списка констант Для изменения значения кэнстанты необходимо дважды ударить мышью по соот ветствующему полю последнего столбца, ввести данные и нажать на Enter либо в левом верхнем углу окна со списком констант (рис. 1.26). на иконку Замечание. Имена первых двух столбцов списка констант не соответствуют име нам атрибутов, значения которых в этих столбцах отображаются. Так, в столбце Код представляется синоним константы, а в столбце Наименование - ее комментарий. Для согласования имен столбцов в приведенном на рис. 1.26 списке и имен атрибутов не обходимо изменить встроенную в 1С процедуру открытия списка констант, вызывае мую командой Константы.Открыть.
Отображаемые после открытия атрибуты Синоним и Комментарий для редактиро вания в этом списке закрыты, но могут быть изменены в конфигураторе. Там же мож но добавить новую или удалить существующую константу. Последовательность, на пример, добавления константы такова: находясь в конфигураторе, откройте конфигу рацию, оставаясь на закладке Метаданные (рис. 1.27), откройте список констант, ударив мышью по пункту Константы, или просто выделите пункт Константы, нажмите на правую кнопку мыши и выберите пункт Новая Константа.
Рис. 1.27. Закладка Метаданные В появившемся окне (рис. 1.28) определите значения всех полей, понимая, что значение, занесенное в поле Идентификатор, используется для доступа к константе в программах. Поэтому имя идентификатора образуется по тем же правилам, что и имя переменной.
Рис. 1.28. Общие свойства константы Название организации Замечание. В конфигурации системы объекта Справочники.Константы нет. Доступ к полю Значение (см. рис. 1.26) обеспечивается либо за счет использования иденти фикатора константы (для непериодических констант), либо в результате применения разработанных для констант методов.
1.7.2. НЕПЕРИОДИЧЕСКИЕ И ПЕРИОДИЧЕСКИЕ КОНСТАНТЫ Константы разделяются на непериодические и периодические. К последним отно сятся константы, старые значения которых нужно сохранять при вводе изменений. Примером может послужить константа ЕдиновременнаяВыплатаНаРебенка, содержа щая значение единовременного пособия при рождении ребенка. Это пособие может меняться, возможно даже увеличиваться, в течение, скажем, года несколько раз. Одна ко на эту константу могут быть ссылки в документах или в отчетах, например в отчете о выплатах упомянутого пособия за год. Поэтому, выполняя изменение размера посо бия, необходимо сохранять значения константы и период их действия. Это выполняет ся в 1С, если компонент константы Периодический имеет значение 1.
Константа ЕдиновременнаяВыплатаНаРебенка является периодической. Поэтому если изменяется ее значение, то в файле 1 SCONST.DBF в отношении этой константы появится новая запись и сохранятся существующие. Это иллюстрирует табл. 1.1. Таблица 1.1 Фрагмент файла 1SCONST.DBF с записями о константе ЕдиновременнаяВыплатаНаРебенка Date
Id
Value
15.04.00 01.01.01 25.08.01
EV EV EV
1000 1500 2500
В приведенном фрагменте отображается состояние константы Единовременная ВыплатаНаРебенка на 3 разные даты. (О том, что записи относятся к одной и той же константе, говорят совпадающие значения поля Id.) Значения, которые ранее имела периодическая константа, можно просмотреть, на с названием История, расположенную в окне со списком констант жав на иконку (см. рис. 1.26). С периодическими константами употребляются методы Получить и Установить, соответственно возвращающие и устанавливающие значение константы на заданную дату. Применив для взятой в качестве примера константы вызовы Сообщить(Константа.ЕдиновременнаяВыплатаНаРебенкаПолучить('21.05.00')); Сообщить(Константа.ЕдиновременнаяВыплатаНаРебенка.Получить('21.07.01')); Сообщить(Константа.ЕдиновременнаяВыплатаНаРебенка.Получить('21.09.01')); получим следующие сообщения:
:,
1000 1500 3500 Также с периодическими константами применяются методы объекта Периодиче ский, рассматриваемые в гл. 6. В частности, л разд. 6.1 приводится код вывода значе ний определенных в конфигурации непериодических констант. Замечания: 1. 2.
В списке констант, наблюдаемом в конфигурации, рядом с именем периодической , а непериодической - иконка константы стоит иконка Хотя в 1С и есть тип Константа (по крайней мере об этом говорится в документа ции), объектов с таким типом нет. Так, если написать процедуру процедура Выполнить() // Связана с кнопкой Пуск обработки Проба Сообщить(ТипЗначенияСтр(Константа.НазваниеОрганизации)); Сообщить(ТипЗначенияСтр(Константа)); конецПроцедуры // Выполнить то после ее запуска получим сообщения Строка НеизвестныйОбъект
1.7.3. ПРИМЕРЫ РАБОТЫ С НЕПЕРИОДИЧЕСКИМИ КОНСТАНТАМИ Значение непериодической константы можно получать и изменять, обращаясь к ней по имени Константа.ИдентификаторКонстанты или употребляя методы ПолучитьАтрибут и УстановитьАтрибут. Пример 1. Вывести название организации. процедура Выполнить() // Связана с кнопкой Пуск Сообщить(Константа.НазваниеОрганизации); // Напечатает, например, АО ТрансМаш // То же сообщение выведет вызов Сообщить(Константа.ПолучитьАтрибут("НазваниеОрганизации")); конецПроцедуры // Выполнить Пример 2. Создать код, изменяющий значение непериодической константы НазваниеОрганизации с прежнего на АО Простор, а затем восстанавливающий старое название. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем новоеНазвание, староеНазвание, ответ; новоеНазвание = "АО Простор"; староеНазвание = Константа.НазваниеОрганизации; ОчиститьОкноСообщений(); Сообщить("Прежнее название организации:" + староеНазвание); ответ = Вопрос("Изменить название организации на " + новоеНазвание +"?", "Да+Нет"); если ответ = "Да" тогда // Нажата кнопка Да Константа.НазваниеОрганизации = новоеНазвание; Сообщить("Название организации после изменения: " + Константа.НазваниеОрганизации); // Восстановим старое название константы Предупреждение("Название организации будет восстановлено."); Константа.НазваниеОрганизации = староеНазвание; иначе // Выбрана кнопка Нет Предупреждение("Название организации осталось без изменений."); конецЕсли; конецПроцедуры // Выполнить Замечания: 1 • Вместо присваивания Константа.НазваниеОрганизации = новоеНазвание; можно использовать метод Константа.УстановитьАтрибут("НазваниеОрганизации", новоеНазвание); 2-
Встроенная функция Вопрос выведет при исполнении программы окно с кнопкам! Да и Нет (рис. 1.29).
Рис. 1.29. Окно, порождаемое встроенной функцией Вопрос
3.
Имена кнопок определяются вторым параметром функции, заданным в виде стро ки "Да+Нет". Функция Вопрос вернет строку со значением, совпадающим с име нем нажатой кнопки. В случае громоздких операторов или выражений их следует размещать на несколь ких строчках. Символы переноса при этом не употребляются. Например: Сообщить("Название организации после изменения:" + Константа.НазваниеОрганизации);
4.
Пример показывает, что нужно крайне аккуратно работать со справочниками во обще и с константами в частности. Достаточно несколько строк небрежного кода, чтобы исказить данные и сделать в результате неработоспособной всю систему. В нашем примере мы избежали негативных последствий, добавив код, восстанав ливающий прежнее значение измененной константы. Пример 3. Выводится список непериодических констант, имеющих синоним, в ок
но сообщений. Просмотр списка констант можно организовать, написав программу, выводящую, например, в окно сообщений идентификатор, синоним константы и ее значение. Раз местим этот код, как всегда, в процедуре Выполнить модуля обработки Проба: // Процедура вывода списка непериодических, определенных в конфигурации констант процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем всегоКонстант; // Число констант в конфигурации перем идеи, син, значен; ОчиститьОкноСообщений(); // Очищаем окно сообщений всегоКонстант = Метаданные.Константа(); для ин = 1 по всегоКонстант цикл если Метаданные.Константа(ин).Периодический = 1 тогда продолжить; // Значения периодических констант не выводятся конецЕсли; син = Метаданные.Константа(ии).Синоним; // Выводим сообщения о константах, для которых задан синоним если ПустоеЗначение(син) = 0 тогда идеи = Метаданные.Константа(ин).Идентификатор; значен = Константа.ПолучитьАтрибут(иден); Сообщить(иден + " - " + син + " - " + значен); конецЕсли; конецЦикла; // для • конецПроцедуры // Выполнить Замечание. Встроенная функция ПустоеЗначение(параметр) вернет нуль, если па раметр определен (имеет значение), и единицу - в противном случае. Эта программа - пример работы с метаданными, в котором использован метод Ме таданные.Константа, возвращающий при работе с константами следующие величины: • •
если не задан параметр, число констант в конфигурации; константу, расположенную в конфигурации под номером ин, если параметром метода является целочисленная переменная ин, например Метаданные.Константа(ин);
•
константу, имеющую идентификатор идеи, если параметром метода является сим вольная переменная идеи, содержащая значение идентификатора, например: перем идеи, конст; идеи = "ГлБухгалтер"; конст = Метаданные.Константа(иден); Сообщить(конст); // Вернет значение синонима, например Главный бухгалтер
• • • • • • • • • • •
Вызывая этот метод, можно получить следующие сведения о константе: идентификатор, например АдресГНИ; синоним, например Адрес ГНИ; комментарий, например Адрес ГНИ; тип, например Строка; вид, например ДаНет, если тип константы - Перечисление; длина, например 240; точность, например 0; неотрицательный, например 0; разделятьТриады, например 0; периодический, например 0; областьРаспространения, например ВсеИнформационныеБазы. Пример. Процедура
Сообщить(Метаданные.Константа(1).Длина);
//
Вернет, например, 240
выведет в окне сообщений данные о длине первой константы.
1.7.4. МЕТОДЫ КОНСТАНТ Здесь и далее мы не будем, дублируя документацию и материал, имеющийся в справке, приводить подробные сведения о методах, применяемых с объектами 1С, по скольку наша цель - приобщение к методам программирования на языке 1С - достигается в результате решения разнообразных практических задач и анализа используемых при со ставлении программ приемов. В то же время полезно дать читателю как можно более пол ную информацию о возможностях системы. Понимая это, мы будем приводить необходи мые справочные данные. Первую порцию таких данных разместим в табл. 1.2-1.4. Таблица 1.2 Методы констант Вид Описание Метод непериодических и периодических констант Назначает тип значению константы, НазначитьТип Процедура для которого в конфигураторе задан неопределенный тип Методы непериодических констант УстановитьАтрибут Процедура Устанавливает значение константы по имени ее идентификатора Возвращает значение константы по имени ПолучитьАтрибут Функция ее идентификатора Метод
Метод Получить Установить
Вид Описание Методы периодических констант Функция Возвращает значение периодической константы на заданную дату Процедура Устанавливает значение периодической константы Таблица 1.3 Синтаксис вызова метода
Непериодические и периодические константы Константа.НазначитьТип(идентификатор, тип, [длина, точность]); Непериодические константы Константа.УстановитьАтрибут(идентификатор, значение); значение = Констата.ПолучтьАтрнбут(идентификатор); Периодические константы значение = Константа.ИдентификаторКонстанты.Получить(дата); Константа .ИдентификаторКонстанты.Установть(дата, значение); Замечания: 1. Здесь и далее при описании синтаксиса необязательные параметры процедур, функций и методов заключаются в квадратные скобки. 2.
Наличие в имени метода текста (ИдентифшаторКонстанты), записанного кур сивом, означает, что в программах этот текст должен быть заменен на соответст вующее значение. Например: // Меняем значение периодической константы с идентификатором Кассир Константа.Кассир.Установить(ТекущаяДата(), "Ремизова Нина Дмитриевна");
3.
Параметрами методов являются выражения, тип и смысл которых описан в табл. 1.4. Таблица 1.4 Описание параметров методов Параметр
дата длина значение идентификатор тип
точность
Смысл Дата, на которую устанавливается или возвращается значение периодической константы Размер поля, отводимого для представления константы, имеющей тип Число или Строка Значение константы Символьное представление идентификатора константы, например "НазваниеОрганизации" Символьное представление типа, используемого при определении значения константы, например "Число", "Строка", "Дата", "Справочник.Сотрудники" Число знаков после десятичной точки в представлении числовой константы
Тип Дата Число Строка, число или дата Строка "
1.8. ОТОБРАЖЕНИЕ СПИСКА КОНСТАНТ В ДИАЛОГОВОМ ОКНЕ Просмотр констант в списке, появляющемся в окне сообщений после запуска при веденной в разд. 1.7.3 процедуры, весьма затруднителен. Более высокое качество ото бражения перечня констант (и других представляемых в виде таблицы объектов) обес печивает элемент диалога Таблица значений. Продемонстрируем способ его употреб ления на примере вывода информации о константах системы. Как и ранее, будем выводить данные только о непериодических константах, отображая их идентификато ры, синонимы и значения. Изменения обработки Проба выполним в следующем порядке. Первоначально увели чим размер диалогового окна, переместим кнопки Пуски Закрыть, добавим в него текст находящуюся на пане Список непериодических констант, вставим, используя иконку ли инструментов Элементы диалога (см. рис. 1.21), элемент Таблица значений, дав ему в качестве идентификатора имя тЗнач (для подобных элементов с 1С предусмотрен тип ТаблицаЗначений), и изменим его размеры, ориентируясь на рис. 1.30.
Затем напишем в модуле нашей обработки следующий код, использующий мето ды, применяемые с переменными типа ТаблицаЗначений: процедура СоздатьТаблЗнач() далее // процедура ЗаполнитьТаблЗнач() далее
Предварительное описание процедур
процедура Выполнить() // Связана с кнопкой Пуск обработки Проба ОчиститьОкноСообщений(); Сообщить("Переменная тЗнач имеет тип ТаблицаЗначений."); конецПроцедуры // Выполнить процедура ПриОткрытии() // Процедура выполняется перед открытием формы // Создаем в таблице тЗнач три столбца СоздатьТаблЗнач(); // Вызов ранее описанной процедуры // Заполняем таблицу тЗнач данными о непериодических константах ЗаполнитьТаблЗнач(); // Эта процедура также описана ранее конецПроцедуры // Выполнить // Процедура создает незаполненную таблицу значений процедура СоздатьТаблЗнач() // Задаем при вызовах метода НоваяКолонка следующие параметры: // идентификатор столбца; // тип данных, заносимых в столбец; // далее пропускаем два параметра - длину и точность представления данных; // заголовок столбца
//Помним, что область действия переменной диалога тЗнач - все // программные компоненты модуля; тип переменной тЗнач - ТаблицаЗначений тЗнач.НоваяКолонка("иден", "Строка",,, "Идентификатор"); тЗнач.НоваяКолонка("син", "Строка",,, "Синоним"); тЗнач.НоваяКолонка("значен", "Строка",,, "Значение"); конецПроцедуры // СоздатьТаблЗнач // Процедура заполняет таблицу значений данными о непериодических константах процедура ЗаполнитьТаблЗнач() перем ин, син, идеи, значен; для ин = 1 по Метаданные.Константа() цикл если Метаданные.Константа(ин).Периодический = 1 тогда продолжить; // Значения периодических констант не выводятся конецЕсли; син = Метаданные.Константа(ин).Синоним; // Выводим сообщения о константах, для которых задан синоним если ПустоеЗначение(син) = 0 тогда идеи = Метаданные.Константа(ин).Идентификатор; значен = Константа.ПолучитьАтрибут(иден); // Используем для добавления новой строки в таблицу тЗнач метод НоваяСтрока тЗнач.НоваяСтрока(); // Добавляем новую строку в таблицу значений тЗнач.иден = идеи; // При занесении данных в ячейку столбца тЗнач.син = син; // пользуемся его идентификатором тЗнач.значен = сокрЛ(значен); конецЕсли; конецЦикла; // для конецПроцедуры // ЗаполнитьТаблЗнач Замечания: 1. Переменная тЗнач имеет тип ТаблицаЗначений, если (что справедливо для нашего случая) она определена в диалоге как идентификатор одноименного элемента диа лога или если она создана в программе в результате выполнения оператора тЗнач = СоздатьОбъект("ТаблицаЗначений)"; 2.
Встроенная функция СокрЛ(строка) возвращает значение параметра строка без ве дущих пробелов. Например: стр = СокрЛ("
Сводка
");
//
Вернет строку "Сводка
"
Сохраним данные, загрузим обработку в 1С:Предприятие и получим, даже не на жимая на кнопку Пуск, приведенный на рис. 1.31 результат.
Рис. 1.31. Список констант в элементе диалога Таблица значений
1.9. ФОРМИРОВАНИЕ ОТЧЕТА СО СПИСКОМ КОНСТАНТ При необходимости список констант можно распечатать. Для этого, однако, его предварительно нужно представить либо в виде табличного отчета, либо в виде тек стового документа, расположив информацию в отчете (документе) в удобном для про чтения виде. Расширим обработку Проба, которую мы до сих пор использовали для отладки и запуска учебных программ, до внешнего отчета 1С, использовав в ней вывод данных в таблицу, созданную на закладке Таблица (рис. 1.32).
Рис. 1.32. Начальный вид закладки Таблица
Первоначально, если не пользоваться помощником создания отчета, в таблице нет заполненных ячеек. Если поставить курсор на имя закладки Таблица и нажать на правую кнопку мы ши, то раскроется список действий, которые можно предпринять (рис. 1.33).
Рис. 1.33. Меню операций для таблицы
Из списка видно, что форма может в общем случае содержать более одной таблицы. Воспользуемся пунктом Задать имя таблицы и присвоим ей имя Константы, кото рое затем и будем использовать в программе. Поставим задачу получить отчет со списком констант в виде табл. 1.5. Таблица 1.5 Список непериодических констант, 02.10.01 Идентификатор
Значение
Синоним
АдресГНИ АдресОрганизации Архиватор
Адрес ГНИ Адрес Организации
ОграничиватьПосмотрСписка
Ограничивать просмотр списка
Некоторый адрес Некоторый адрес Нет Нет Всего непериодических констант: 94
Для ее решения потребуется в отчете Проба на закладке Константы (новое имя за кладки Таблица) сформировать макет отчета по образцу, приведенному на рис. 1.34, а затем написать программу вывода данных в таблицу Константы.
Рис. 1.34. Образец для отчета Список непериодических констант Проделаем эту работу в следующем порядке. Перейдем на закладку Константы, выделим ячейки 2, 3 и 4 первой строки и выполним их объединение, нажав на иконку панели инструментов Редактор таблиц (рис. 1.35) или выбрав пункт Объединить Колонки Таблицы меню конфигуратора.
Рис. 1.35. Панель инструментов Редактор таблиц Занесем в объединенные ячейки шаблон следующего вида: Список непериодических констант, [ТекущаяДата()], введя его на закладке Текст в окне Свойства ячейки, которое появляется после позиционирования курсора на ячейке, удара по правой клавише мыши и выбора пункта Свойства (рис. 1.36). (В квадратных скобках содержится выражение, вы числяемое при использовании шаблона. В нашем случае выражение - это встроенная функция, возвращающая установленную в вашем компьютере текущую дату.)
Рис. 1.36. Задание свойства Текст ячейки таблицы Константы Используя закладки Положение и Шрифт, расположим текст по центру ячеек 2-4, сделаем его полужирным и увеличим его размер до 12. Проведя аналогичные действия, сформируем в ячейках 2, 3 и 4 строки 3 заголовки будущей таблицы со списком констант, а затем увеличим, оперируя мышью, ширину каждого столбца с заголовками. При этом на закладке Текст окна Свойства ячейки в поле Тип установим значение не Шаблон, а Текст. Высоту шрифта и его толщину можно изменить сразу для всех заголовков, выделив их и вызвав затем окно Свойства ячейки. Кроме изменения параметров шрифта, зададим, перейдя на закладку Рамка, разделяющие ячейки вертикальные и горизонтальные линии.
Просмотрим промежуточный вариант макета отчета, выбрав на стандартной пане или пункт Просмотр колонки системного ли инструментов конфигуратора иконку меню Файл. Замечание. Чтобы изменить размер столбца таблицы, нужно захватить мышью, нажав на ее левую кнопку, разделяющую номера столбцов линию и переместить ее в нужном направлении. В строке 5 формируемого макета отчета в ячейку второго столбца (эта ячейка име ет номер R5C2) занесем текст иден, задав для него в поле Тип значение Выражение (рис. 1.37).
Рис. 1.37. Свойства ячейки R5C2 В нашем случае выражение иден - это имя переменной в программе, которой в процессе чтения информации об очередной константе присваивается значение ее идентификатора. Далее аналогичным образом зададим в ячейках 3 и 4 строки 5 выражения син и значен, являющиеся в нашем случае именами переменных, получающими соответст венно синоним и значение константы. Затем, выделив три ячейки с выражениями, на закладке Рамка зададим разделяющие их вертикальные линии. Замечание. Можно организовать перенос длинных имен, не умещающихся в за данных для них ячейках таблицы. Для этого в поле Контроль (рис. 1.37) следует уста новить значение Переносить. В строке 7 после объединения столбцов 3 и 4 зададим шаблон Всего непериодических констант: [числоКонст], в котором выражение числоКонст - это переменная, получающая в процессе вычислений значение, равное количеству непериодических констант. Перейдем к формированию секций. Объединим в секцию первые 3 строки табли^ цы. Для этого выполним их выделение, проведя мышью, у которой нажата левая кноп ка, по номерам строк 1-3, выберем иконку (см. рис. 1.35) и присвоим идентифика тору секции значение Заголовок. Аналогичным образом создадим секции оКонстанте и Всего. Макет отчета готов. Направим в него данные и отобразим результат, использовав в отчете Проба следующий снабженный обширным комментарием код: // Процедура формирования отчета, содержащего список непериодических констант процедура Выполнить() // Связана с кнопкой Пуск обработки (отчета) Проба перем ин, числоКонст, син, иден, значен; перем табл; ОчиститьОкноСообщений(); табл = СоздатьОбъект("Таблица"); // Свяжем переменную табл с таблицей Константы, содержащей макет отчета табл.ИсходнаяТаблица("Константы"); // При выводе применяем заданные по умолчанию параметры таблицы; // для их изменения следует обратиться к методу Опции
// Выводим, используя шаблон Список непериодических констант, [ТекущаяДата()], // секцию Заголовок табл.ВывестиСекцию("Заголовок"); числоКонст = 0; // Число непериодических констант для ин = 1 по Метаданные.Константа() цикл если Метаданные.Константа(ин).Периодический = 1 тогда продолжить; // Значения периодических констант не выводятся конецЕсли; числоКонст = числоКонст + 1; // Еще одна константа // Определяем значения переменных идеи, син и значен // Эти значения будут использованы при выводе секции оКонстанте идеи = Метаданные.Константа(ин).Идентификатор; син = Метаданные.Константа(ин).Синоним; значен = сокрЛ(Константа.ПолучитьАтрибут(иден)); // Вывод очередной строки в отчет табл.ВывестиСекцию("оКонстанте"); конецЦикла; // для табл.ВывестиСекцию("Всего"); // Вывод данных о числе непериодических констант // Запрещаем редактирование результирующей таблицы табл.ТолькоПросмотр(1); // Задаем в методе Показать заголовок окна с результирующей таблицей табл.Показать("Отчет о константах"); конецПроцедуры // Выполнить. Результат приведен в табл. 1.5.
1.10. ВЫВОД СПИСКА КОНСТАНТ В ТЕКСТОВЫЙ ФАЙЛ Текстовые файлы часто используют для обмена данными между программами, на пример для передачи из 1С платежных поручений в банк, где они воспринимаются ус тановленной там процедурой. Механизм вывода данных в текстовый документ с по следующим его сохранением в файле продемонстрируем на уже решенной нами задаче формирования списка констант, направляя его на этот раз не в таблицу значений (разд. 1.8) или в табличный отчет (разд. 1.9), а в текстовый файл. Наша задача довольно проста, и мы после создания объекта Текст обойдемся не большим числом методов, применяемых в 1С для объектов такого типа, - методами ДобавитьСтроку, ТолькоПросмотр, Показать и Записать. Для форматирования и пре образования данных используем встроенные функции Формат и Строка. Необходимые пояснения впервые использованных методов и функций дадим в нижеприводимом тексте программы. // Процедура формирования текстового файла с данными о непериодических константах процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ин, числоКонст, син, идеи, значен; перем текст, имяФайла; имяФайла = "Koнстакты.txt"; текст = СоздатьОбъект("Текст"); // Формируем текст с данными о константах; системная константа // РазделительСтрок обеспечит вставку пустой строки между // заголовком списка и заголовками столбцов текст.ДобавитьСтроку("Список непериодических констант" + РазделительСтрок);
// Вывод заголовков столбцов // Как и ранее, выведем в каждой строке 3 поля: идентификатор, синоним // и значение константы, задав длину каждого поля, равной 20 символам текст.ДобавитьСтроку(Формат("Идентификатор", "С20") + " " + Формат("Синоним", "С20") + "" + Формат("Значение", "С20") + РазделительСтрок); числоКонст = 0; // Число непериодических констант для ин = 1 по Метаданные.Константа() цикл если Метаданные.Константа(ин).Периодический = 1 тогда продолжить; // Значения периодических констант не выводятся конецЕсли; числоКонст = числоКонст + 1; // Определяем значения переменных идеи, син и значен идеи = Метаданные.Константа(ин).Идентификатор; син = Метаданные.Константа(ин).Синоним; значен = сокрЛ(Константа.ПолучитьАтрибут(иден)); // Вывод очередной строки в формируемый текст // Встроенная функция Формат(пар, "С20") вернет значение // параметра пар в виде строки длиной в 20 символов текст.ДобавитьСтроку(Формат(иден, "С20") + " " + Формат(син, "С20") + " " + Формат(значен, "С20")); конецЦикла; // для текст.ДобавитьСтроку(""); // Выводим пустую строку // Функция Строка преобразовывает значение переменной числоКонст // в символьное представление текст.ДобавитьСтроку("Всего непериодических констант " + Строка(числоКонст)); // Вывод данных о числе непериодических констант // Запрещаем редактирование результирующего текста текст.ТолькоПросмотр(1); // Покажем текст, в окне, имеющем заголовок Список непериодических констант текст.Показать("Список непериодических констант"); текст.Записать(имяФайла); // Запишем текст в файл Константы-txt нецПроцедуры // Выполнить. Результат приведен на рис. 1.38.
Рис. 1.38. Текст отображается с применением равномерного шрифта Замечания: 1.
Поля с данными будут расположены равномерно, если применить шрифт с фикси рованной шириной символа, например Fixedsys. Переход от пропорционального шрифта к равномерному в результирующем тексте осуществляется в 1С:Пред приятии в результате выбора пунктов меню Текст - Текст модуля. Колонка меню Текст появляется при просмотре текста.
2. Метод Записать, если не задан путь, разместит файл в директории с базами данных системы. Если файл записывается впервые, то метод Записать создаст этот файл. При записи в существующий файл и при его создании метод Записать, если он употребляется совместно с методом Показать, предварительно осведомится, нуж но ли записывать данные в указанный файл.
1 . 1 1 . ЗАГРУЗКА ТЕКСТА МОДУЛЯ ИЗ ФАЙЛА До сих пор мы редактировали диалог или таблицу формы или набирали код моду ля, находясь в конфигураторе, затем выполняли его синтаксический контроль, сохра няли, устранив ошибки, файл обработки (отчета), переходили в 1С:Предприятие, за гружали там обновленную обработку и выполняли ее запуск. Если же диалог и таблица в процессе работы над формой не изменяются, а моди фицируется только код программных компонентов модуля, то процесс запуска изме ненной программы можно несколько ускорить. Для этого следует воспользоваться ко мандой #ЗагрузитьИзФайла ИмяФайла Эта команда должна быть размещена в первой строке модуля и начинаться с ее первой позиции. ИмяФайла - это указанное без кавычек имя файла, в котором разме щается текст редактируемого модуля, например Проба.ert. При наличии в модуле такой команды весь расположенный вслед за ней код игно рируется. Редактирование данных должно выполняться в файле ИмяФайла. При этом файл загружается не в конфигураторе, а в 1С:Предприятии (Файл - Открыть). Чтобы появившийся текст представлялся как программа, то есть с использованием заданных для текста программ шрифта, цветов и размера табуляции, необходимо после откры тия файла выбрать в меню пункты Текст - Текст модуля. После внесения изменений необходимо сохранить файл (Ctrl+S), закрыть файл формы (обработки), если она открыта, и открыть его заново. В загруженной форме бу дет использован обновленный в файле ИмяФайла код модуля. Выполним только что приведенные рекомендации. Добавим, во-первых, в код мо дуля обработки Проба.ert, открытой в конфигурации, команду (в вашем случае путь, разумеется, может быть иным) // Команда размещена в первой строке модуля #ЗагрузитьИзФайла d:\1cv77\Test\TXT\Пpoбa.txt Затем создадим, например в папке ТХТ, текстовый файл Проба.txt, откроем его в 1С:Предприятии, скопируем в него код модуля обработки Проба.ert, выберем пункты меню Текст - Текст модуля, внесем в текст файла изменения, добавив, например, в процедуру выполнить оператор Сообщить("Код модуля загружен из файла Пpoбa.txt"); Сохраним файл Проба.Ш, откроем и запустим обновленную обработку (файл Пpoбa.txt закрывать не нужно).
Еще одна рекомендация. При работе с кодом модуля не забывайте о возможностях колонки меню Текст и панели инструментов Текстовый редактор (рис. 1.39).
Рис. 1.39. Панель инструментов Текстовый редактор Напомним, что действия с блоком (вторая группа кнопок на рис. 1.39) становятся возможными после выделения одной или нескольких строк текста. Отметим, что если программа модифицируется в 1С:Предприятии, а не в конфи гураторе, то на панели инструментов Текстовый редактор отсутствуют иконки и , то есть мы не имеем возможности просмотреть список процедур и функций мо дуля и осуществить синтаксический контроль кода без загрузки обработки в систему.
1.12. КАК ОТКРЫТЬ ОТЧЕТ ИЛИ ОБРАБОТКУ В ПРОГРАММЕ Запуск обработки Проба мы до сих пор выполняли из созданного нами меню, либо выбирая в нем Проба - Пуск, либо нажимая Alt+1. Кроме того, мы могли открыть об работку, использовав пункты Открыть колонки меню Файл. При необходимости, од нако, отчет или обработку можно открыть из любой другой формы или программы глобального модуля. Для этой цели употребляется функция ОткрытьФорму. Чтобы привести примеры обращения к функции и методу, открывающим отчет (обработку), создадим еще одну обработку, дав ей имя Открыть.ert, и разместим ее там же, где и обработка Проба. Формируя новую обработку, не забудем вставить команду ее вызова в меню Ученик, ответив утвердительно на соответствующее предложение помощника создания обработки. Диалог обработки может иметь при веденный на рис. 1.40 вид.
Рис. 1.40. Диалог обработки Открыть С кнопкой Открыть обработки свяжем процедуру Открыть, разместив в ней сле дующий код: Процедура Открыть() // Открывает обработку Проба перем контОбрПроба; // Контекст обработки Проба ОткрытьФорму("Отчет", контОбрПроба, "d:\lCv77\Test\ExtForms\npoбa.ert"); КонецПроцедуры // Открыть Теперь, если верно указано полное имя файла обработки Проба, после нажатия на кнопку Открыть диалога, приведенного на рис. 1.40, мы откроем обработку Проба.
Имя файла можно получить, применив метод ВыбратьФайл агрегатного типа ФС. Тогда код процедуры Открыть будет таким: процедура Открыть() // Открывает обработку Проба перем контОбрПроба; перем флаг, имяФайла; // Третий параметр метода ВыбратьФайл опущен флаг = ФС.ВыбратьФайл(0, имяФайла,, "Находим файл Проба.ert", "Отчет и обработки | *.ert"); если флаг = 1 тогда // Если файл обработки выбран если ОткрытьФорму("Отчет", контОбрПроба, имяФайла) = 0 тогда Предупреждение("Не удается открыть обработку " + имяФайла); возврат; конецЕсли; иначе Предупреждение("Файл обработки не выбран."); конецЕсли; конецПроцедуры // Открыть Метод ВыбратьФайл, если его первый параметр равен нулю, открывает диалог типа Открыть (рис. 1.41) и возвращает, если файл выбран, во второй параметр имя файла.
Рис. 1.41. Выбор файла, содержащего обработку Проба Четвертый параметр (третий опущен) задает заголовок диалога, а пятый - сообще ние, выводимое в поле Тип файлов, и после разделительной черты - маску, опреде ляющую вид отображаемых в диалоге файлов. Заданная нами маска оставит в диалоге только файлы с расширением ERT. Замечания: 1. Функция ОткрытьФорму имеет более широкое назначение. Она употребляется для открытия форм справочников, документов, различных журналов и др. 2.
Чтобы открыть отчет (обработку), встроенный в конфигурацию, можно из модуля не которой формы вместо функции ОткрытьФорму употребить метод ОткрытьПодбор.
1.13. КОНТЕКСТ ОБРАБОТКИ ПРОБА Вторым параметром использованной в предшествующем разделе функции От крытьФорму является контекст формы обработки Проба, под которым понимается совокупность ее реквизитов (переменных диалога) й методов формы обработки. Этот параметр является выходным.
Проиллюстрируем методы работы с контекстом на примере обработки, диалог ко торой содержит два поля (рис. 1.42): одно числового типа (дЧ), другое типа Перечис ление (пер).
Рис. 1.42. Обработка Проба с полями ДЧ и пер Определяя тип второго поля на закладке Тип в диалоге задания его свойств, выбе рем перечисление ТипПлатежа (рис. 1.43).
Рис. 1.43. Выбор вида перечисления для поля пер Это перечисление определено в конфигурации и содержит следующие идентифи каторы значений: • ВсеДолгиПоЗарплате; • Зарплата; • Аванс; • МежрасчВыплата; • ВыплатыНаДетей; • Дивиденды; • ЕдиновременныеПособия. В модуле модифицированной обработки Проба запишем предопределенную про цедуру ПриОткрытии с двумя операторами присваивания: процедура ПриОткрытии() // Начальные значения переменных диалога дЧ и пер дЧ = 5.55; пер = Перечисление.ТипПлатежа.ЗначениеПоНомеру(2); конецПроцедуры // ПриОткрытии
//
Зарплата
В обработке Открыть (см. рис. 1.40), из которой в результате применения функции ОткрытьФорму вызывается обработка Проба, создадим процедуру НовыйКонтекст, изменяющую значения переменных дЧ и пер диалога обработки Проба и обновляю щую ее диалог. В качестве результата выведем состояние диалога обработки Проба до и после изменения значения переменных дЧ и пер.
Чтобы вместе с обработкой Открыть закрывалась и обработка Проба, в предо пределенной процедуре ПриЗакрытии первой обработки выполним оператор контОбрПроба.Форма.Закрыть(); Таким образом, модуль обработки Открыть будет содержать следующий код: перем контОбрПроба; // Переменная модуля обработки Открыть // Изменяет значения переменных дЧ и пер и обновляет диалог обработки Проба процедура НовыйКонтекст(контОбрПроба) Сообщить(контОбрПроба.Дч); // 5.55 Сообщить(контОбрПроба.пер); // Зарплата Предупреждение("Сейчас будут изменены переменные дЧ и пер обработки Проба."); // Изменяем переменные диалога дЧ и пер обработки Проба // и смотрим на ее диалог; результаты наблюдений приведены на рис. 1.44 контОбрПроба.Дч = 7.98; контОбрПроба.пер = Перечисление.ТипПлатежа.ЗначениеПоНомеру(З); // Аванс // Обновляем диалог обработки Проба контОбрПроба.Форма.Обновить(); конецПроцедуры // НовыйКонтекст процедура Открыть() // Открывает обработку Проба перем флаг, имяФайла; флаг = ФС.ВыбратьФайл(0, имяФайла, "Находим файл Проба.еrt, "Отчет и обработки | *.ert"); если флаг = 1 тогда // Если файл обработки выбран если ОткрытьФорму("Отчет", контОбрПроба, имяФайла) = 0 тогда Предупреждение("Не удается открыть обработку " + имяФайла); возврат; иначе НовыйКонтекст(контОбрПроба); // Изменяем значения переменных дЧ и пер конецЕсли; // и обновляем окно формы обработки Проба иначе Предупреждение("Файл обработки не выбран."); конецЕсли; конецПроцедуры // Открыть // Закрывает открытую обработку Проба, используя для доступа к методу Закрыть // переменную модуля контОбрПроба процедура ПриЗакрытии() если ПустоеЗначение(контОбрПроба) = 0 тогда контОбрПроба.Форма.Закрыть(); конецЕсли; конецПроцедуры // ПриЗакрытии
Рис. 1.44. Диалог обработки Проба: а - до обновления; б - после него
Структура контекста говорит о том, что он является объектом агрегированного ти па, включающего компоненты (в нашем случае реквизиты формы) и методы, употреб ляемые с обработкой. Приведем еще один пример управления формой обработки Про ба через ее контекст. Так, если в процедуру перед вызовом контОбрПроба.Форма.Обновить(); выполнить оператор контОбрПроба.Форма.дЧ.Видимость(0);
//
Скрываем элемент диалога дЧ
то рис. 1.44, б примет приведенный на рис. 1.45 вид.
Рис. 1.45. Управление формой обработки Проба через ее контекст Замечание. Переменная контОбрПроба, возвращаемая функцией ОткрытьФорму, имеет тип ГрупповойКонтекст. В этом можно убедиться, выполнив вызов Сообщить(ТипЗначенияСтр(контОбрПроба));
//
Напечатает ГрупповойКонтекст
Это говорит о том, что через переменную контОбрПроба доступны все употребляе мые с формой методы: методы формы, элементов ее диалога, контекста модуля формы и контекста модуля формы отчета (обработки). Контекст формы можно передать в качестве входного/выходного параметра про цедуре или функции глобального модуля. Пусть, например, в глобальном модуле оп ределена процедура процедура РаботаСКонтекстом(конт) экспорт конт.Дч = 7.98; конт.пер = Перечисление.ТипПлатежа.ЗначениеПоНомеру(З); // Аванс конт.Форма.дЧ.Видимость(0); // Скрываем элемент диалога д Ч конт.Форма.Обновить(); // Обновляем диалог обработки Проба конецПроцедуры // РаботаСКонтекстом При вызове такой процедуры в качестве фактического параметра используется пе ременная Контекст. Например: процедура Выполнить() // РаботаСКонтекстом(Контекст); конецПроцедуры // РаботаСКонтекстом
Связана с кнопкой Пуск обработки Проба
Результат такого вызова см. на рис. 1.45. Замечание. Реально процедура РаботаСКонтекстом после запуска вышеприведен ного примера удалена из глобального модуля.
Разумеется, контекст обработки можно передать как входной/выходной параметр процедуре или функции, размещенных в модуле обработки. Например, запишем в мо дуле обработки Проба следующий код: процедура РаботаСКонтекстомВМодуле(конт) конт.Дч = 7.98; конт.пер = Перечисление.ТипПлатежа.ЗначениеПоНомеру(З); // Аванс конт.Форма.дЧ.Видимость(0); // Скрываем элемент диалога дЧ конт.Форма.Обновить(); // Обновляем диалог обработки Проба конецПроцедуры // РаботаСКонтекстомВМодуле процедура Выполнить() // РаботаСКонтекстомВМодуле(Контекст); конецПроцедуры // Выполнить
Связана с кнопкой Пуск обработки Проба
Исполнение кода даст тот же эффект, что и вызов процедуры РаботаСКонтекстом глобального модуля. В то же время очевидна избыточность такого подхода: область действия переменных диалога и процедур формы распространяется на все программ ные единицы ее модуля. Замечание. Все сказанное относительно контекста обработки применимо и к кон тексту произвольной формы: документа, формы списка справочника и др.
1.14. ФУНКЦИИ ДЛЯ АДМИНИСТРАТОРА Возможно, вам придется каким-либо образом управлять работой пользователей системы. Для этого могут пригодиться встроенные функции среды исполнения. Они приведены в табл. 1.6. Таблица 1.6 Функции среды исполнения Функция заголовокСтар = ЗаголовокСистемы ([заголовок]); имяЭВМ= ИмяКомпьютера();
Что возвращает
Возвращает и/или устанавливает заголовок окна системы Сетевое имя работающего с программой компьютера пользователь = ИмяПользователя(); Имя работающего с программой пользователя, взятое из списка пользователей (см. рис. 1.9) полноеИмя = ПолноеИмяПользователя( ); Полное имя работающего с программой пользователя (см. рис. 1.10) наборПрав = НазваниеНабораПрав( ); Название набора прав пользователя право = ПравоДоступа Единицу, если пользователь имеет право доступа, (названиеПрава, объект); заданное именем названиеПрава, к объекту, заданного именем объект, или нуль - в противном случае Название интерфейса, заданное пользователю интерфейс = НазваниеИнтерфейса(); в конфигураторе
Функция каталог = КаталогПользователя(); каталогБазы = КаталогИБ(); каталог1С = КаталогПрограммы( ); времКат = КаталогВременныхФайлов( ); режим = МонопольныйРежим(); язык = ОсновнойЯзык();
Что возвращает Заданное в конфигураторе имя рабочего каталога пользователя Имя каталога информационной базы данных Имя каталога с исполняемыми файлами 1С Имя каталога временных файлов, образуемых при работе с 1С Единицу, если программа запущена в монопольном режиме, или нуль - в противном случае Единицу, если основной язык русский, или нуль, если английский
Пример: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба ОчиститьОкноСообщений(); ЗаголовокСистемы("Система с учебными формами"); Сообщить(ИмяКомпьютера()); // DEFAULT Сообщить(ИмяПользователя()); // Ученик Сообщить(ПолноеИмяПользователя()); // Николаев Н. А. Сообщить(НазваниеНабораПрав()); // ПолныеПрава Сообщить(ПравоДоступа("ВводНового", "Справочник.КадровыеДанные")); //1 // Для справочников регулируются права доступа со следующими названиями: // чтение; // любыеИзменения; // вводНового; // удаление; // пометкаНаУдаление; // снятиеПометкиНаУдаление; // корректировка Сообщить(ПазваниеИнтерфейса()); // Ученик Сообщить(КаталогПользователя()); , //D:\lCv77\Test\ Сообщить(КаталогИБ()); // D:\lCv77\Test\ Сообщить(КаталогПрограммы()); // D:\l CV77\BIN\ Сообщить(КаталогВременныхФайлов()); // С:\ТЕМР\ Сообщить(МонопольныйРежим()); //1 Сообщить(ОсновнойЯзык()); //1 конецПроцедуры // Выполнить Замечания: 1.
Любая встроенная функция может быть вызвана как процедура, то есть в виде самостоятельного оператора, например ОсновнойЯзык(); В большинстве случаев такой вызов не окажет никакого воздействия на работу приложения. Так, из приведенных в табл. 1.6 функций только первая, будучи упот ребленная как самостоятельный оператор, позволяет вносить изменения.
2. Если при вызове встроенной функции и процедуры возникает ошибка исполнения, то выводится диагностическое сообщение и может произойти аварийное заверш ение программы. Выводимые в таких случаях сообщения, правда, не всегда адек ватны ситуации. Так, при запуске процедуры процедура Выполнить( ) // перем х; ОчиститьОкноСообщений(); х = 0; Лог(х); конецПроцедуры // Выполнить
Связана с кнопкой Пуск обработки Проба
получим сообщения х = 0; {D:\lCV77\TEST\EXTFORMS\ПРОБA.ERT(3)}: Деление на 0
1.15. ВЫВОДЫ Приведенные сведения позволяют сделать ряд выводов. 1. Для каждого пользователя можно (и нужно) создать свой интерфейс с определен ными правами доступа. 2.
Пользовательские программы (внешние обработки и отчеты) создаются в виде ' форм, содержащих диалог, модуль, одну или более таблиц.
3.
Формы разрабатываются в конфигураторе, а запускаются в 1С:Предприятии.
4.
Имена переменных, процедур и функций, заданных в форме (в ее диалоге, модуле или его программных компонентах), доступны только в этой форме. Причем имя переменной модуля или диалога может быть закрыто локальным именем процеду ры или функции модуля.
5.
Модуль формы в общем случае содержит объявления переменных модуля и про граммные компоненты - основную программу, пользовательские процедуры (в том числе и предопределенные) и функции. Предопределенные процедуры, то есть созда ваемые пользователем процедуры, имеющие определенные в документации имена и интерфейсы и исполняемые либо при наступлении стандартных событий, например при открытии формы, либо при рбращении к ним из других программных компонен тов модуля, включаются в модуль по мере необходимости. Порядок следования про граммных компонентов модуля подчиняется определенным правилам.
6.
Существуют по-настоящему глобальные имена (разд. 1.6), доступные в каждом программном модуле.
7.
Объекты, такие, как Таблица значений, Текст и др., становятся доступными после их создания в результате применения функции СоздатьОбъект; к иным объектам, например Константам, можно обратится по их полному имени. С каждым видом объектов связан набор методов, позволяющих выполнять необходимые для управ ления объектами действия.
8. 9.
Такие объекты, как Таблица и Список значений, становятся доступными в модуле формы после их размещения в диалоге формы (разд. 1.8). Вывод данных может быть выполнен в окно сообщений, в некоторые элементы диалога формы, например в таблицу значений, в таблицу (отчет), в текстовый до кумент и другие пока что нерассмотренные объекты системы. Кроме того, объек ты, это мы покажем ниже, позволяют выполнять и обратную операцию - ввод оп ределенных в них данных.
10. Методы, применяемые с метаданными, позволяют, в частности, получить перечень объектов определенного в системе типа, например Констант, а также прочитать их свойства; изменить свойства объектов, например задать новое имя идентификато ра какой-нибудь константы, можно, лишь находясь в конфигураторе системы. Лю бое изменение конфигурации выполняется в монопольном режиме. 11. Производительность работы повысится, если на стадии отладки кода модуля использовать возможности команды #ЗагрузитьИзФайла (разд. 1.11). 12. Отчет (обработку) можно открыть из любой другой формы, применив функцию ОткрытьФорму (ОткрытьФормуМодально) или метод ОткрытьПодбор. 13. Контекст обработки - это объект агрегированного типа ГрупповойКонтекст, обес печивающий доступ к переменным диалога обработки и к определенным для нее методам. 14. Контекст обработки может быть передан в качестве входного/выходного парамет ра процедуре (функции) модуля самой обработки и глобального модуля; 15. Контекст открываемой формы возвращает функция ОткрытьФорму (Открыть ФормуМодально) . 16. Для получения сведений о среде исполнения и управления работой пользователей полезны рассмотренные в разд. 1.12. функции.
2. БАЗОВЫЕ ПОНЯТИЯ Теперь, когда приобретены первоначальные по составлению и запуску простых программ знания, настало время более детально рассмотреть базовые элементы языка, такие, как виды объектов данных, типы данных, операции и их приоритет, выражения, программные компоненты и др.
2.1. ОБЪЕКТЫ И ТИПЫ ДАННЫХ В программах 1С можно оперировать следующими объектами данных: • буквальными и системными константами (не путать с рассмотренными выше опреде ленными в конфигурации системы константами); • переменными. • • • •
Объекты данных могут иметь следующие типы: числовой; символьный; дата; агрегатные, например Справочник. Замечания:
1. Диапазон задания числовых значений, а также максимально допустимая точность представления числовых данных в документации по языку не оговариваются. Также там отсутствует и указание относительно максимально возможной длины символьного объекта данных. Впрочем, о допустимой точности задания числовых данных можно судить по следующим примерам. Так, оператор Сообщить(Лог(2.5)); выведет в окно сообщений число 0.91629073187415 имеющее 14 знаков после десятичной точки. Правда, Сообщить(Лог(5.0));
//1.6094379124341
вернет число только с 13 знаками после десятичной точки, а Сообщить(1.0/Лог(1.01));
//100.499170807131365574
напечатает число с 18 знаками после десятичной точки: Оператор Сообщить(2 / 3);
//
0.66666666666666666667
напечатает результат, имеющий рекордное число знаков после десятичной точки - 20. 2. Агрегатный тип данных имеет разновидности. Например, разновидностями типа Справочник являются Справочник.Должности, Справочник.Сотрудники и др. Агре гатный тип данных мы рассмотрим в гл. 3.
3. Некоторые методы, например приведенный в табл. 1.2 метод НазначитьТип, при нимают в качестве параметра символьное представление типа данных или разно видности типа. Это символьное представление для числового типа задается как "Число", для символьного - как "Строка". Для остальных типов оно дублирует имя типа или его разновидности. Например, символьное представление разновидности типа Справочник.Города - это строка "Справочник.Города".
2.2. БУКВАЛЬНЫЕ КОНСТАНТЫ Буквальные константы (далее - просто константы) числового типа - это вещест венные числа с точкой или без точки, со знаком или без него, например: -2.34,2 ,+2.0, .25
//
Нуль перед десятичной точкой можно опустить
Константа символьного типа - последовательность символов, обрамленная двой ными кавычками, например "Это строка" или // Это константа нулевой длины Если необходимо двойную кавычку включить в состав символьной константы, то кавычку нужно повторить дважды, например процедура Сообщить("""Константа, которая начинается и завершается двойными кавычками"""); напечатает в окне сообщений следующий текст: "Константа, которая начинается и завершается двойными кавычками" Длинная символьная константа - текст, расположенный на двух или более строках, записывается либо с использованием знака продолжения - вертикальной черты, простав ляемой в начале строки продолжения константы, например стрЗ = "Это длинная символьная константа, | поскольку она размещается не на одной, | а на трех строчках"; либо как последовательность однострочных констант, например стрЗ = "Это длинная символьная константа," " поскольку она размещается не на одной," " а на трех строчках"; В таком тексте сохраняются символы конца строки. Длинную константу без сим волов конца строки на двух или более строчках записать нельзя. Заметим, что в виде подобных текстов нередко составляются запросы к базам данных. Константы типа Дата, если год представляется в виде двух чисел, - это последова тельность из трех пар чисел, обрамленных одинарными кавычками и разделенных точками. Формат даты -'ДД.ММ.ГГ', где ДД - число месяца, ММ - номер месяца, ГГ две последние цифры в номере года. Например: '03.11.01'
//
3 ноября 2001 г.
Год в константе типа Дата можно задать полностью: '03.11.1942'
//
3 ноября 1942 г.
Если в представлении константы типа Дата год задается двумя цифрами, то при интерпретации даты используется значение года начала рабочего столетия, которое задается на закладке Общие в окне Настройка параметров системы, появляющемся в 1С:Предприятии после выбора пунктов меню Сервис - Параметры (рис. 2.1).
Рис. 2.1. Задание параметров, относящихся к датам Так, если в представлении константы типа Дата используются две цифры, то если они меньше или равны последних двух цифр года начала рабочего столетия (в нашем случае он равен 1941), то дата относится к нынешнему веку, в противном случае к прошедшему. Например: '03.11.1942' '03.11.42' '03.11.41'
//3 // //
ноября 1942 г. Так же 3 ноября 1942 г. Это 3 ноября 2041 г.
Убедимся в этом визуально, активизировав кнопку 4 (рис. 2.1), сохранив новую настройку и запустив обработку, содержащую два следующих сообщения: Сообщить('03.11.42'); Сообщить('03.11.41');
// //
Напечатает 03.11.1942 Напечатает 03.11.2041
Используя 4 цифры в представлении даты, можно задать любую дату от Рождества Христова, например '01.01.0001'
//1
января 1г.
Константу с датой до Рождества Христова задать нельзя. Если константа задает несуществующую дату, то она воспринимается как пустое значение, например Сообщить(ПустоеЗначение('33.12.01')); //Напечатает 1 (нет такой даты) Сообщить('33.12.01'); //Напечатает . . Сообщить(ПустоеЗначение('23.12.01')); // Напечатает 0 (верная дата)
2.3. СИСТЕМНЫЕ КОНСТАНТЫ В языке есть 3 системные именованные константы, имеющие символьный тип и следующие имена: РазделительСтраниц; РазделительСтрок; Символ Табуляции.
Они, как правило, употребляются при работе с текстом. Например: // Константа РазделительСтрок обеспечит вставку пустой строки // вслед за текстом "Список непериодических констант" текст.ДобавитьСтроку("Список непериодических констант" + разделительСтрок);
2.4. ПЕРЕМЕННЫЕ ЧИСЛОВЫЕ, СИМВОЛЬНЫЕ И ДАТЫ Переменные разделяются на скалярные и массивы. В текущей реализации 1С можно использовать только одномерные массивы, называемые также векторами. Переменные появляются в программе: • после их объявления, например перем а, стр; перем мас[20]; •
•
Объявляем скалярные переменные а и стр Объявляем вектор из 20 элементов
после первого размещения имени переменной (числовой, строковой и даты) в правой части оператора присваивания, например у = Лог(а);
•
// //
//
Вводим переменную у, определяем ее тип и значение
при определении имен идентификаторов редактируемых элементов диалога (см. разд. 1.5); при задании формальных параметров процедур, например в пользовательской функ ции функция ВычислитьУ(х) // х = 2.0 * х; // у = 2 * Лог(х); // возврат у; конецФункции // ВычислитьУ
х - формальный параметр функции ВычислитьУ употребляется в функции как переменная Лог - встроенная функция
формальный параметр x используется как скалярная переменная. При объявлении переменной ее тип и значение не определяются. Такая перемен ная имеет пустое значение. Однако ее можно употреблять в выражениях. Например: Пример 1. Тип и значение переменной а не определены. Встроенная функция ПустоеЗначение(а) вернет 1, а результатом последующего выражения будет 0. перем а; // Сообщить(ПустоеЗначение(а)); Сообщить(а); // б = 2 * а; Сообщить(ПустоеЗначение(б)); Сообщить(б); // Сообщить(б + 3); Сообщить(а + 3);
// //
// //
Объявляем скалярную переменную а Напечатает 1 Напечатает пустую строку Напечатает 1. То есть б имеет пустое значение Напечатает 0, а не пустую строку, // несмотря на то что б имеет пустое значение Напечатает 3 Напечатает пустую строку
Переменная обретает тип при ее определении, то есть когда она получает значение в результате выполнения оператора присваивания или если переменная является фор мальным параметром процедуры, при вызове этой процедуры, в котором определены соответствующие фактические параметры.
Пример 2. Переменные а и стр после выполнения присваивания будут иметь соот ветственно числовой и символьный тип. перем а, стр; а = 1.22; стр = "Это строка";
// // //
Объявляем скалярные переменные а и стр Теперь переменная а имеет числовой тип Теперь переменная стр имеет символьный тип
Пример 3. При вызове функции ВычислитьУ ее формальный параметр х будет оп ределен как числовая переменная (текст функции ВычислитьУ см. выше). процедура Выполнить() б = ВычислитьУ(3.0); // При вызове функции ВычислитьУ определяется Сообщить("б = " + б); // тип и значение ее формального параметра х конецПроцедуры // Выполнить Замечание. Переменные, имена которых неизвестны в программном компоненте, нельзя использовать в качестве фактических параметров других программных компо нентов. Например, ошибочен следующий код: процедура ОпределитьХ(х) х = 2.0; конецПроцедуры // ОпределитьХ процедура Выполнить() ОпределитьХ(х); // Сообщить(х); конецПроцедуры // Выполнить
Этот вызов в текущей версии недопустим
В то же время вполне корректен второй вариант процедуры Выполнить: процедура Выполнить() перем х; // ОпределитьХ(х); // Сообщить(х); // конецПроцедуры // Выполнить
Объявляем переменную х Теперь этот вызов возможен Напечатает 2
По ходу выполнения программы одна и та же переменная может менять свой тип, например: перем а; а = 1.22; Сообщить(а); а = "Это строка"; Сообщить(а); а = '24.12.01' Сообщить(а);
// //
// // // // //
Объявляем скалярные переменные а Теперь переменная а имеет числовой тип Напечатает 1.22 Теперь переменная а имеет символьный тип Напечатает Это строка Теперь переменная а имеет тип Дата Напечатает 24.12.01
2.5. ВВОД ЗНАЧЕНИЙ РАЗНЫХ ТИПОВ Значения имеющихся в программе переменных числового, символьного типа, дат и агрегатных типов можно определить, используя диалоги, вызываемые приведенны ми в табл. 2.1 функциями; в табл. 2.2 приведен их синтаксис, а в табл. 2.3 - описание их параметров.
Таблица 2.1 Функции ввода значений
Таблица 2.2 Синтаксис функций ввода значений флаг = ВвестиЧисло(пер, заг, длина, точность, [задержка]); флаг = ВвестиСтроку(пер, заг, длина, [признак], [задержка]); флаг = ВвестиДату(пер, заг, [задержка]); флаг = ВвестиЗначение(пер, заг, тип, [длина], [точность]);
Таблица 2.3 Описание параметров функций ввода значений Параметр
Описание
длина
Длина числового или символьного поля ввода. Задается при вводе чисел и строк
задержка
Числовое выражение, задающее время ожидания ввода данных в секундах. При его превышении диалог исчезает, функция возвращает -1, значение переменной пер не изменяется. Время ожидания неограниченно, если задержка равна нулю или если этот параметр опущен
заг
Текст заголовка окна диалога, например "Ввод сотрудника"
пер
Переменная, в которую при нажатии на ОК устанавливается введенное значение; при выборе Отмена значение переменной пер не изменяется. Переменная пер должна появиться в программном компоненте до вызова диалога ввода данных
Описание
Параметр признак
тип
точность
Числовое выражение, задающее вид вводимой строки. Если признак равен единице, то появляется диалог ввода многострочного текста с разделителями (см. разд. 2.2), в противном случае вводится строка без разделителей строк (задано по умолчанию) Тип переменной, значение которой определяется при нажатии на ОК, например "Число", "Строка", "Дата", а также разновидности агрегатных типов Справочник, Документ и Перечисление, например "Справочник.Сотрудники", "Документ.Договор", "Перечисление.Пол". В случае задания несуществующего типа, например "Символ", или типа, с которым функция не работает, например "Календарь", диалог ввода Ьанных не появляется и значение переменной пер сохраняется, а функция возвращает нуль Число знаков после десятичной точки в поле ввода числового значения. Задается при вводе чисел
Замечания: 1. Все функции возвращают число 1, если нажата кнопка ОК, или 0 - в противном случае. 2.
Функции ВвестиДату и ВвестиЗначение позволяют задавать в поле ввода несуще ствующие даты. В этом случае в определяемую переменную пер при нажатии ОК устанавливается пустое значение. Примеры:
перем а, сотр; флаг = ВвестиЧисло(а, "Введите номер документа", 10, 0); флаг = ВвестиСтроку(а, "Введите подстроку для поиска", 30); стр = "Это длинная символьная константа, | поскольку она размещается не на одной, | а на трех строчках"; // Задаем надлежащее (достаточно большое) число символов в тексте // Диалог, появляющийся после вызова функции, приведен на рис. 2.2, а флаг = ВвестиСтроку(стр, "Отредактируйте текст и нажмите ОК", 500, 1); // Диалог, появляющийся после вызова функции, приведен на рис. 2.2, б // После выбора сотрудника переменная сотр будет иметь тип Справочник флаг = ВвестиЗначение(сотр, "Выберите сотрудника", "Справочник.Сотрудники");
а б Рис. 2.2. Диалоги ввода данных: а - многострочного текста; б - сотрудника
2.6. ВЫРАЖЕНИЯ И ОПЕРАЦИИ 2.6.1. ВЫРАЖЕНИЯ Выражение - это формула, по которой вычисляется значение. В выражении 1С могут присутствовать операнды разных типов (при наличии в выражении с несколькими операндами операнда агрегатного типа данных этот опе ранд воспринимается как пустое значение). Тип выражения определяется типом его результата. Тип результата выражения (или его подвыражения) определяется типом его первого операнда или первого заключенного в круглые скобки подвыражения. На пример (выражения располагаются в правых частях операторов присваивания): а= 1.4; //1.4 - числовое выражение из одного операнда б = 2 + 5 * а; // Числовое выражение, возвращающее число 9 в = 2 + " 1 -я строка"; // Числовое выражение, возвращающее число 3 г = "Строка и символ " + 2; // Символьное выражение, возвращающее строку // "Строка и символ 2" д = 2 + '21.12.01'; // Числовое выражение, возвращающее число 2452267 е = '21.12.01' + 2; // Выражение типа Дата, возвращающее дату 23.12.01 ж = (2 * "2-я строка") + 1; // Числовое выражение, возвращающее число 5 Из примеров видно, что порядок вычисления выражения таков: первоначально по первому операнду определяется тип выражения; затем выполняется вычисление выражения с учетом круглых скобок и приоритета выполнения операций, при этом значения операндов, имеющих тип, отличный от типа выражения (или вычисляемого в данный момент подвыражения), преобразовываются в значения, тип которых совпа дает с типом выражения. Например, переменная з в операторе з = "Результат = " + 4 / "2-я строка"; получит символьный тип и значение, равное "Результат = 2". Действительно, опера ция / имеет более высокий приоритет, чем операция +, поэтому прежде будет вычисле но подвыражение 4/"2-я строка", которое, судя по его первому операнду, является чис ловым и поэтому вернет число 2. Далее это число будет преобразовано в строку "2", которая объединится со строкой "Результат = ". Результатом выражения будет пустое значение, если первый операнд выражения имеет пустое значение. Например: перем а; б = 2 + а; в = а + 2;
// // //
Объявляем скалярную переменную а Вернет 2 Вернет пустое значение
Переменная агрегатного типа данных воспринимается в выражениях, имеющих более одного операнда, как пустое значение. Например: табл = СоздатьОбъект("Таблица"); а = табл + 2; // б = табл; //
Вернет пустое значение Типы переменных б и табл совпадают
Выражение называется логическим, если в нем есть хотя бы одна операция отно шения или логическая операция. Например: 2.5 /1.33 > 0 (а = 5) и (б = 2)
//Вернет //
1 Вернет 0, если, например, а = 1
Логические выражения употребляются в управляющих конструкциях языка, на пример если 2.5 /1.33 > 0 тогда Сообщить("Да"); иначе Сообщить("Нет"); конецЕсли; Замечания: 1. Логические выражения отличаются от иных, например числовых, выражений 1С тем, что их нельзя использовать в правой части оператора присваивания или в ка честве фактических параметров процедур, функций и методов. Так, ошибочен оператор флаг = (а = 5) и (б = 2);
//
Ошибка! В правой части оператора присваивания // размещено логическое выражение
Этот недостаток создает определенные неудобства при записи программ. 2.
Тип данных выражения возвращают функции ТипЗначения и ТипЗначенияСтр. Первая возвращает число, по которому определяется тип данных ее параметра, вторая - имя типа данных параметра. Например: а =1.2; Сообщить(ТипЗначения(а)); // Напечатает 1 Сообщить(ТипЗначенияСтр(а)); // Напечатает Число сСотр = СоздатьОбъект("Справочник.Сотрудники"); Сообщить(ТипЗначенияСтр(сСотр)); // Напечатает Справочник
2.6.2. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ ДЛЯ ЧИСЛОВОГО ТИПА ДАННЫХ С каждым типом данных связан набор операций. С числовыми данными употреб ляются известные арифметические операции *, /, +, - и операция %, возвращающая ос таток от деления операндов, например: 5%2 5.2 % 2 5.8 % 2 5.8 % "2-я строка"
// // // //
Вернет 1 Вернет 1 Вернет 0 Также вернет 0
Из примеров видно, что при вычислении результата выражения с операцией % числовые операнды округляются до целых значений (если второй операнд является нечисловым, то он прежде приводится к числовому типу), а затем вычисляется ос таток от деления двух целых чисел.
2.6.3. ОПЕРАЦИИ ДЛЯ СТРОК И ДАТ В символьных выражениях допустима только операция +, называемая операцией конкатенации. Например: "1-я строка и " + "строка 2"
//
Вернет строку "1-я строка и строка 2"
В выражениях типа Дата можно употреблять операции + и -, например // Выражение типа Дата, возвращающее дату 01.01.02 // Выражение типа Дата, возвращающее дату 10.12.01
'21.12.01'+11; '21.12.01' -11;
Следующее выражение ошибочно: '21.12.01' * 11
//
Операция * недопустима в выражениях типа Дата
2.6.4. " И С Т И Н А " И " Л О Ж Ь " В 1С Констант со значениями истина и ложь во встроенном языке 1С нет. В то же вре мя понятия истина и ложь используются в 1С при оценке логических выражений, появляющихся в управляющих конструкциях языка.
2.6.5. ОПЕРАЦИИ ОТНОШЕНИЯ В логических выражениях используются приведенные в табл. 2.4 операции отно шения. Таблица 2.4 Операции отношения Операция < <= > >= = <>
Описание Меньше Меньше или равно Больше Больше или равно Равно Не равно
Операндами операций отношения могут быть выражения числового или символь ного типа, а также даты. Причем типы операндов должны совпадать. Например: 2=5 "Строка 2">"Строка 1" 2<'23.12.01'>
// Это ложь // Это истина // Это неверное выражение отношения, так как его // операнды имеют разный тип
Также операндами операций = и <> могут быть переменные агрегатного типа. На пример: тЗнач1 = СоздатьОбъект("ТаблицаЗначений"); сСотр = СоздатьОбъект("Справочник.Сотрудники"); тЗнач2 =тЗнач1; // Круглые скобки в нижезаписанном логическом выражении обязательны
если (тЗнач1 = тЗнач2) и (тЗнач1 = сСотр) тогда Сообщить("Да"); иначе Сообщить("Нет"); конецЕсли; Если операция отношения выполняется над строками, то они сравниваются по символьно до тех пор, пока не будут обнаружены несовпадающие символы. Они то и решат судьбу сравнения. Следует помнить, что символ сим_1 больше сим_2, если код символа сим_1 больше кода символа сим_1. Если строки-операнды операции от ношения имеют разную длину и если есть необходимость продолжить сравнение сим волов строк-операндов (все предыдущие сравниваемые символы оказались равными), то в качестве недостающего символа более короткой строки используется символ с нулевым кодом. Например: "Строка" > "Строка 1"
//
Это ложь
2.6.6. ЛОГИЧЕСКИЕ ОПЕРАЦИИ Применяются следующие логические операции: НЕ - логическое НЕ (отрицание); И - логическое И; ИЛИ - логическое ИЛИ. Операндами логических операций должны быть логические выражения. В табл. 2.5 приведены результаты логических операций над логическими выраже ниями ЛВ1 и ЛВ2, принимающими значения истина (И) или ложь (Л). Таблица 2.5 Таблица истинности ЛВ1
ЛВ2
ЛВ1 И ЛВ2
ЛВ1 ИЛИ ЛВ2
НЕ ЛВ1
И И Л Л
И Л И Л
И Л Л Л
И И И Л
Л Л И И
Операция отрицания является унарной операцией, располагаемой слева от операн да. Все остальные рассмотренные операции являются бинарными.
2.6.7. ПРИОРИТЕТ ВЫПОЛНЕНИЯ ОПЕРАЦИЙ Все операции 1С выполняются в выражении слева направо в соответствии с их приоритетом (старшинством), то есть, если две последовательные операции имеют равный приоритет, первоначально выполняется левая операция. Подвыражения, за ключенные в круглые скобки, вычисляются в первую очередь. В табл. 2.6 операции 1С расположены в порядке убывания их приоритета. Таблица 2. б Приоритет выполнения операций
%
*,/
+, -
НЕ
И
ИЛИ
<, <=, >, >=, =, <>
Замечание. Каждая ячейка таблицы содержит операции с равным приоритетом. Пример: 8%2*3 8 % (2 * 3)
// //
Вернет 0 Вернет 2
Поскольку логические операции старше операций отношения, то ошибочно сле дующее логическое выражение: 3>2и4<5
//
Так неверно
так как первоначально оценивается логическое подвыражение 2и4 операнды которого не есть истина или ложь. А это неверно. Зато верно логическое выражение (3 > 2) и (4 < 5)
//
Это истина
Пример. Вычислить результат логического выражения (х / а = 1) или (б / (а + б) < 1) и не (б = а) или (х <> 6) при х = 6.0, а = 2.0 и б=3.0. Вычислив результат операций подвыражений, заключенных в круглые скобки, по лучим: ложь или истина и не ложь или ложь. Далее выполняем пошагово логические операции с учетом их приоритета. После выполнения не ложь: ложь или истина и истина или ложь. После выполнения истина и истина: ложь или истина или ложь. Окончательный результат: истина.
2.7. МАССИВЫ Массив - это объект данных, содержащий несколько значений, доступ к которым осуществляется по их номеру (индексу). Число элементов массива называется его размером. Размером массива может быть только целочисленная буквальная константа. Оператор перем а[5]; объявляет одномерный массив (вектор) а из пяти элементов. Элементы массива имеют следующие имена: а[1], а[2], а[3], а[4] и а[5]. В этих именах величины 1-5 - индексы элементов массива. Массив считается определенным, если заданы значения всех его элементов. Для задания начальных значений элементов массива (инициализации массива) возможен следующий цикл: для ин = 1 по 5 цикл а[ин] = 1; конецЦикла;
//
Теперь все элементы массива равны единице
Присваивания а[2] = 3; а[5] = -4.7; изменят соответственно значения 2-го и 5-го элементов массива а. В общем случае в качестве индексов массива могут использоваться числовые вы ражения, называемые индексными выражениями. Например: а[6 / 2] = 9.1;
//
Меняем значение 3-го элемента массива а
Если индексное выражение массива вычисляется с нецелым значением, то в каче стве индекса берется целая часть этого значения. Например: а[6
/
5]
=
9.1;
//
Меняем
значение
1
-го
элемента
массива
а
Значение индекса не должно выходить за границы массива. Так, при работе с ра нее объявленным массивом а из пяти элементов ошибочны операторы а[0] = 5;
//
Индекс не может быть меньше числа 1
а[6] = 9;
//
Индекс не может быть больше числа 5
Элементы одного и того же массива могут быть разного типа. Например: а[1] = 5; а[3] = "Строка"; а[5] =
'25.11.01';
//Элемент // //
числового типа Элемент символьного типа Элемент типа Дата
Элементы массива могут быть агрегатного типа. Например: сСотр= СоздатьОбъект("Справочник.Сотрудники"); табл = СоздатьОбъект("Таблица"); а[1] = 5; // Элемент числового типа а[3] = сСотр; // Элемент агрегатного типа а[5] = табл; // Элемент другого агрегатного типа Массив не может в качестве элементов содержать другие массивы. Массив может быть формальным параметром программного компонента (про цедуры или функции). При этом размер массива не указывается, а квадратные скобки сохраняются. Для определения размера переданного процедуре (функции) массива используется встроенная функция Разм. Пример: процедура ИнициализацияМассива(а[ ]) перем ин, размА; размА = Разм(а); // Встроенная функция Разм вернет размер массива а для ин = 1 по размА цикл а[ин] = 1; // Теперь все элементы массива равны единице конецЦикла; а[3] = -5; конецПроцедуры // ИнициализацияМассива
процедура Выполнить() перем а[5]; // Вызов процедуры, устанавливающей начальные значения элементов массива // Ее фактическим параметром является имя массива ИнициализацияМассива(а); Сообщить(а[2]); // Напечатает 1 Сообщить(а[3]); // Напечатает -5 конецПроцедуры // Выполнить
2.8. ВСТРОЕННЫЕ ФУНКЦИИ ДЛЯ РАЗНЫХ ТИПОВ ДАННЫХ 2.8.1. ВСТРОЕННЫЕ МАТЕМАТИЧЕСКИЕ ФУНКЦИИ Приведенные в табл. 2.7 математические функции применяются с числовыми ти пами данных. Они имеют в качестве аргументов числовые выражения и возвращают (если при их исполнении не возникло ошибки) некоторое число. Таблица 2.7 Математические функции Функция
Что возвращает
результат = Окр(число, [числоЗнаков], [способ]);
Округленное значение параметра число. Параметр числоЗнаков, если он присутствует, задает число знаков дробной части результата. Значение по умолчанию - 0. Параметр способ, если он вычисляется со значением 1, устанавливает, что число вида 1.5 округляется до числа 2 или округляется до числа 1 в противном случае. Значение по умолчанию - 0
результат = Цел(число);
Целую часть параметра число
минЗначение = Мин(число1, [число2],..., [числоN);
Минимальное значение своих параметров. Может иметь только один параметр - число!
максЗначение = Макс(чнсло1, [число2],..., [числоN);
Максимальное значение своих параметров. Может иметь только один параметр - число1
результат = Лог10(число);
Десятичный логарифм аргумента число или нуль, если аргумент меньше нуля. Возникнет завершающая ошибка, если аргумент равен нулю
результат - Лог(число);
Натуральный логарифм аргумента число или нуль, если аргумент меньше нуля. Возникнет завершающая ошибка, если аргумент равен нулю
Примеры: Сообщить(Окр(2.45, Сообщить(Окр(2.45, 1, 0)); Сообщить(Окр(2.597)); Сообщить(Цел(2.9)); Сообщить(Мин(-2.9,-5,
1,
1)); // // // 12));
//2.5 2.4 3 2 //-5
2.8.2. ВСТРОЕННЫЕ ФУНКЦИИ ДЛЯ СИМВОЛЬНЫХ ДАННЫХ Приводятся в табл. 2.8. Параметры, имя которых содержит слово строка или текст, являются символьными выражениями, а имеющие слово число - числовыми. Исполь зуются целые части числовых параметров, если они являются нецелыми числами. Таблица 2.8 Функции для строк функция
Что возвращает
длина - СтрДлина(строка);
Длину параметра строка
флаг = ПустаяСтрока(строка);
Число 1, если строка имеет нулевую длину или состоит из одних пробелов, или 0 -в противном случае
новаяСтрока - СокрЛ(строка);
Строку, получаемую в результате удаления ведущих пробелов в аргументе строка
новаяСтрока = СокрП(строка);
Строку, получаемую в результате удаления завершающих пробелов в аргументе строка
новаяСтрока = СокрЛП(строка);
Строку, получаемую в результате удаления ведущих и завершающих пробелов в аргументе строка
подстрока = Лев(строка, число);
Строку, содержащую первые число символов параметра строка или всю строку строка, если число => СтрДлина(строка). Если число <=0, то возвращается строка нулевой длины
подстрока = Прав(строка, число);
Строку, содержащую последние число символов параметра строка или всю строку строка, если число > СтрДлина(строка). Если число < 0, то возвращается строка нулевой длины
подстрока = Сред(строка, число1, [число 2]);
Строку, содержащую подстроку параметра строка, начинающуюся с символа под номером число 1 длиной число2. Если параметр число2 опущен, возвращается подстрока, начинающаяся с символа под номером число1 длиной до конца строки. Если число1 > СтрДлина(строка) или число2 < 0, то возвращается строка нулевой длины. Если параметр число1 < 0, то он принимается равным единице
позиция = Найти(строка, подстрока);
Позицию, с которой подстрока начинается в параметре строка, или нуль, если подстрока не найдена в первом параметре
новаяСтрока = СтрЗаменить (строка, подстрока!, подстрока2);
Строку, в которой все вхождения параметра подстрока 1 в параметр строка заменены на параметр подстрока2
кол = СтрЧислоВхождений(строка, Число вхождений параметра подстрока в параметр строка подстрока);
Функция Что возвращает Число строк в тексте текст. Строки в тексте разделены кол = СтрКоличествоСтрок символом РазделительСтрок (см. разд. 2.3) {текст); стр = СтрПолучитьСтроку(текст, Строку параметра текст, имеющую номер номерСтроки. номерСтроки); Если номерСтроки < 0 или номерСтроки больше числа строк в тексте, то возвращается строка нулевой длины новаяСтрока = Врег(строка); Строку, в которой все символы параметра строка преобразованы в верхний регистр новаяСтрока - Нрег(строка); Строку, в которой все символы параметра строка преобразованы в нижний регистр новаяСтрока = OemToAnsi Строку, в которой OEM-коды символов параметра (строка); строка преобразованы в ANSI-коды новаяСтрока = AnsiToOem Строку, в которой ANSI-коды символов параметра (строка); строка преобразованы в ОЕМ-коды символ = Симв(кодСимвола); Символ, код которого равен значению параметра кодСимвола код = КодСимв(строка); Код первого символа параметра строка Примеры: длина = СтрДлина("ААББСС"); // Вернет 6 флаг = ПустаяСтрока(" ААББСС"); // Вернет О новаяСтрока = СокрЛП(" ААББСС "); // "ААББСС" подстрока = Лев(" ААББСС", 2); // Вернет "АА" подстрока = Прав("ААББСС", 2); // Вернет "СС" подстрока = Сред("ААББСС", 3); // Вернет "ББСС" подстрока = Сред("ААББСС", 3,2); // Вернет "ББ" позиция = Найти("ААББСС", "БС"); // Вернет 4 позиция = Найти("ААББСС", "БС1"); // Вернет О новаяСтрока = СтрЗаменить("ААББАА", "АА", "ДДЦ") // Вернет "ДДЦББДДЦ" кол = СтрЧислоВхождений("ААББАА", "АА"); // Вернет 2 текст = "Это длинная символьная константа, | поскольку она размещается не на одной, | а на трех строчках"; кол = СтрКоличествоСтрок(текст); // Вернет 3 стр = СтрПолучитьСтроку(текст, 3); // Вернет "а на трех строчках" новаяСтрока = Врег("ааббсс"); // Вернет "ААББСС" новаяСтрока = Нрег("ААББСС"); // Вернет "ааббсс" стр = "вГСбв DOS"; // Содержит значение "текст DOS" в DOS-кодировке новаяСтрока = OemToAnsi(cтp); // Вернет строку "текст DOS" в кодировке ANSI новаяСтрока = AnsiToOem(новаяСтрока); // Вернет вГСбв DOS уже в кодировке OEM символ = Симв(200); // Вернет И код = КодСимв("И"); // Вернет 200 код = КодСимв("ИЛИ"); // Вернет 200
2.8.3. ВСТРОЕННЫЕ ФУНКЦИИ ДЛЯ ДАТ И ВРЕМЕНИ 1С, как и полагается программе бухгалтерского назначения, хранит много различных Дат, для управления которыми и доступа к которым используются приводимые в табл. 2.9
функции. Параметры, имя которых содержит слово дата, - это выражения типа Дата. Па раметр вариант может быть либо датой, либо выражением символьного типа. Прочие па раметры - это числовые выражения. Большинство функций, если они получают в качестве параметра несуществующую дату, например '33.10.01', возвращают пустое значение типа Дата. Например: Сообщить(НачМесяца('33.10.01'));
//Напечатает
. . Таблица 2.9
Функции для дат Функция рДатаТек = РабочаяДата ([рДата], [режимСмены]);
ЧТО возвращает Устанавливает/возвращает рабочую дату, то есть дату, используемую при формировании документов и проводок в текущем сеансе. Параметр режимСмены применяется при смене рабочей даты на значение параметра рДата и установки режима смены рабочей даты в полночь. Причем если режимСмены задается равным числу •
0, то рабочая дата в полночь не меняется;
•
1, то система предлагает изменить рабочую дату в полночь;
• 2, то рабочая дата меняется в полночь автоматически. Если параметр режимСмены не задан, то при вызове функции РабочаяДата сохраняются заданные в системе установки режима смены. Если новое значение даты это несуществующая дата, то рабочая дата не меняется. Рабочую дату можно также изменить, воспользовавшись в 1С:Предприятии пунктами меню Сервис- Параметры и выбрав в появившемся окне закладку Общие (см. рис. 2.1). По умолчанию рабочая и текущие даты совпадают тДата = ТекущаяДата();
Текущую (системную) дату
новаяДата = ДобавитьМесяц (дата, числоМесяцев);
Дату, получаемую в результате прибавления к значению параметра дата заданного вторым параметром числа месяцев. Значение параметра числоМесяцев может быть меньше нуля
нМесяца = НачМесяца(дата);
Дату начала месяца, которому принадлежит дата
кМесяца = КонМесяца(дата);
Дату конца месяца, которому принадлежит дата
нКвартала = НачКвартала(дата);
Дату начала квартала, которому принадлежит дата
кКвартала = КонКвартала(дата);
Дату конца квартала, которому принадлежит дата
нГода = НачГода(дата);
Дату начала года, которому принадлежит дата
кГода = КонГода(дата);
Дату конца года, которому принадлежит дата
Функция
Что возвращает
нНедели = НачНедели(дата);
Дату начала недели, которой принадлежит дата
кНедели - КонНедели(дата);
Дату конца недели, которой принадлежит дата
год = ДатаГод(дата);
Числовое значение года, которому принадлежит дата
месяц = ДатаМесяц(дата);
Номер месяца, которому принадлежит дата
число = ДатаЧисло(дата);
Номер дня месяца, заданного параметром дата
Номер недели года, которой принадлежит дата неделя = НомерНеделиГода(дата); деньГода = НомерДняГода(дата);
Номер дня года, заданного параметром дата
деньНедели = НомерДняНедели(дата);
Номер дня недели, заданного параметром дата (нумерация дней начинается с понедельника)
период = ПериодС тр (датаНачПериода, датаКонПериода);
Символьное представление периода, границы которого задаются значениями параметров функции. Если период это месяц, квартал, полугодие или год, то выводится соответствующее название периода, например 1 Полугодие 2002 г.
нИнтервала = Начало
Устанавливает/возвращает вариант задания начала стандартного интервала отображения журнала документов. Параметр вариант, если он имеет тип Дата, задает дату начала интервала журнала документов. Если параметр это символьное выражение, то оно должно вычисляться со следующими значениями: "День", "Месяц", "Квартал", "Год". Если параметр опущен или содержит неверное значение, имеющаяся в системе установка сохраняется. Начало стандартного интервала можно задать в 1С:Предприятии, обратившись к пунктам меню Сервис Параметры и выбрав в появившемся окне закладку Журналы (рис. 2.3)
СтандартногоИнтервала ([вариант]);
кИнтервала = Конец СтандартногоИнтервала ([вариант]);
Устанавливает/возвращает вариант задания конца стандартного интервала отображения журнала документов. Параметр вариант имеет тот же, что и для предшествующей функции, смысл с той разницей, что задает конец стандартного интервала
время = ТекущееВремя ([час], [мин], [сек]);
Строку, отображающую текущее (системное) время. Если заданы параметры час, мин и сек, то функция запишет в них числовые значения соответственно часа, минут и секунд текущего времени
флаг = ВвестиПериод (нПер, кПер, заг)
Единицу, если в выведенном диалоге (рис. 2.4) задания даты начала (нПер) и даты конца (кПер) периода нажата кнопка ОК, или нуль, если нажата кнопка Отмена, или клавиша Esc, или кнопка, закрывающая окно диалога. Символьный параметр заг задает заголовок диалогового окна
Рис. 2.3. Задание начала и конца стандартного интервала на закладке Журналы
Рис. 2.4. Диалог задания дат начала и конца периода Примеры: рДата = РабочаяДата(); тДата = ТекущаяДата(); новая Дата = ДобавитьМесяц(рДата, -2); нМесяца = НачМесяца(рДата); кМесяца = КонМесяца(рДата); нКвартала = НачКвартала(рДата); кКвартала = КонКвартала(рДата); нГода = НачГода(рДата); кГода = КонГода(рДата); нНедели = НачНедели(рДата); кНедели = КонНедели(рДата); год = ДатаГод(рДата); месяц = ДатаМесяц(рДата); дч = ДатаЧисло(рДата); неделя = НомерНеделиГода(рДата); деньГода = НомерДняГода(рДата); деньНедели = НомерДняНедели(рДата); период = ПериодСтр(нКвартала, кКвартала); // Новая установка начала стандартного интервала НачалоСтандартногоИнтервала("Месяц"); нИнтервала = НачалоСтандартногоИнтервала( );
// 10.10.01 // 10.10.01 // 10.08.01 //01.10.01 //31.10.01 //01.10.01 //31.12.01 //01.01.01 //31.12.01 //08.10.01 //14.10.01 //2001 //10 //10 //41 //283 // 3 //4 Квартал 2001 г.
// Месяц
// Новая установка конца стандартного интервала КонецСтандартногоИнтервала("Квартал"); // Квартал кИнтервала = КонецСтандартногоИнтервала(); // 4 Квартал 2001 г. время = ТекущееВремя(); // 19:53:12 // Подготовка к вызову диалога (рис. 2.4) задания дат начала и конца периода // Устанавливаем значения дат, отображаемых в диалоге нПер = '01.04.01'; кПер = '30.06.01'; флаг = ВвестиПериод(нПер, кПер, "Введите период"); Замечание. При работе с датами на всем временном интервале, начиная от рожде ства Христова, используется григорианский календарь, который берет отсчет от 4 ок тября 1582 г. по юлианскому календарю. Причем этот день юлианского календаря стах 15 октября 1582 г. в григорианском. Функцию ТекущееВремя часто употребляют для замера времени вычисление (правда, достаточно продолжительных, поскольку функция не возвращает миллисе кунды), например так: процедура Выполнить( ) // перем нЧас, нМин, нСек; // перем кЧас, кМин, кСек; // перем времяВычислений; перем ин, с; ТекущееВремя(нЧас, нМин, нСек); // Некоторые вычисления
Замеряет время вычислений нСек - время начало вычислений кСек - время конца вычислений //
Фиксируем время начала вычислений
с = 0;
для ин = 1 по 500000 цикл с = с + (ин * 2) / ин; // Некоторые вычисления конецЦикла; ТекущееВремя(кЧас, кМин, кСек); // А это время конца вычислений // Время вычислений в секундах времяВычислений = (кЧас - нЧас) * 3600 + (кМин - нМин) * 60 + (кСек - нСек); Сообщить("Длительность процесса равна " + времяВычислений + " сек."); конецПроцедуры // Выполнить
2.8.4. ВСТРОЕННЫЕ ФУНКЦИИ ПРЕОБРАЗОВАНИЯ ТИПОВ ДАННЫХ Как мы видели, в выражениях с операндами разных типов данных преобразования ти пов выполняются автоматически в соответствии с описанными в разд. 2.5 правилами. При этом тип выражения определяется типом его первого операнда. Используя такое свойстве выражений 1С, можно решать вопросы преобразования типов данных, такие, как преобра зования "число - строка", "дата - число" и обратные. Покажем это на примерах. Пример 1. Преобразования "число - строка" и "строка - число". перем а, б, стр; а =123.45;
// Преобразование "число - строка". Переменная стр имеет символьный тип, поскольку // первый операнд выражения "" + а - это строка стр = "" + а; // В результате имеем стр = "123.45" стр = стр + "67"; // Имеем: стр = "123.4567"
// Имеем: стр = "25.10.02" // Напечатает Строка Некоторые преобразования типов данных можно выполнять, применяя приведен ные в табл. 2.10 встроенные преобразовывающие функции. Таблица 2.10 Функции преобразования типов данных Функция
Что делает
Вариант 1: дат = Дата(параметр); Вариант 2: дат = Дата(год, месяц, число);
В первом варианте функция преобразовывает значение выра жения параметр в значение типа Дата. Выражение может быть числовым, символьным или типа Дата. В первом случае значение выражения трактуется как число дней от Рождества Христова и преобразовывается в соответствующую дату, во втором случае строка преобразовывается в дату, в третьем преобразований не выполняется. Во втором варианте функция преобразовывает дату, заданную числовыми выражениями год, месяц, число, в значение типа Дата. Причем для задания значения параметра год использу ются все цифры, а не две последние
Функция стр = Строка(параметр);
чис = Чиспо(параметр);
Что делает Преобразовывает значение выражения параметр в строку. Выражение может быть датой или иметь числовой или символьный тип. В последнем случае преобразований не выполняется Преобразовывает значение выражения параметр в число. Выражение может быть датой или иметь символьный или числовой тип. В последнем случае преобразований не выполняется
Примеры (используем при выводе две цифры для представления года даты): дат = Дата(2452573); // Имеем: дат = '25.10.02' дат = Дата("25.10.2002"); // Имеем: дат = '25.10.02' дат = Дата("25.10.02"); // Имеем: дат = '25.10.02' дат = Дата('25.10.2002'); //Имеем: дат = '25.10.02' дат = Дата(2002, 10, 25); // Имеем: дат = '25.10.02' стр = Строка('25.10.02'); // Имеем: стр = "25.10.02" стр = Строка(+123.45); //Имеем: стр = "123.45" стр = Строка("25.10.02"); // Имеем: стр = "25.10.02" чис = Число('25.10.2002'); // Имеем: чис = 2452573 чис = Число('25.10.02'); // Имеем: чис = 2452573 чис = Число("123.45"); //Имеем: чис = 123.45 чис = Число("+123.45"); //Имеем: чис = 123.45 чис = Число("-123.45"); // Имеем: чис = -123.45 чис = Число(123.45); //Имеем: чис= 123.45
("число - дата") ("строка - дата") ("строка - дата") ("дата - дата") ("дата - строка") ("число - строка") ("строка - строка") ("дата - число") ("дата - число") ("строка - число") ("строка - число") ("строка - число") ("число - число")
2.8.5. ФОРМАТИРОВАНИЕ ДАННЫХ Представление данных при их выводе на различные носители можно изменять, применяя встроенную функцию Формат, имеющую следующий синтаксис: представлениеЗначения = Формат(значение, форматнаяСтрока); Функция Формат, получив значение, преобразовывает его в соответствии с прави лами, заданными символьным параметром форматнаяСтрока, и возвращает резуль тат в виде строки с отформатированными данными. Пример. Выводится число -123.45 на поле длиной в 9 символов. В четырех по следних символах поля отображаются знаки числа, стоящие после десятичной точки. Сообщить(Формат(-123.45, "49.4")); // Напечатает -123.4500 Форматная строка может начинаться с символов Ч, С или Д, если форматируются соответственно числовые, символьные данные или даты. Если тип данных не соответ ствует, употребленному символу, то до форматирования будет выполнено соответст вующее преобразование типов, например Сообщить(Формат("21-я строка", "45.2"));
//
Напечатает 21.00
2.8.5.1.
ФОРМАТИРОВАНИЕ
ЧИСЛОВЫХ ДАННЫХ
Параметр форматнаяСтрока при форматировании числовых данных в общем случае имеет следующий вид: "Ч[3][0][Д][.Т][Р1][Р2][>С]" •
• • • • •
•
Появляющиеся в форматной строке обозначения имеют следующий смысл: 3 - заполнитель ведущих пробелов, присутствующих в строке-результате функции Формат. Если на месте заполнителя указать (0), то ведущие пробелы заменяются на нули; 0 - флаг вывода пустого поля, когда форматируемое значение равно нулю; Д - длина результирующей строки в символах; Т - число знаков после десятичной точки (точность); Р1 - символ, отделяющий целую часть числа от нецелой. По умолчанию в качестве разделителя Р1 используется точка; Р2 - символ, разделяющий триады. (Триада - это тройка последовательных чисел в целой части числа. Например, число 1234567 при форматировании с разделением на триады, когда на месте Р2 стоит символ ', примет вид 1'234'567.) По умолчанию триа ды не разделяются; С - размер правого сдвига форматируемого значения. В результате сдвига удаляются С правых символов целой части форматируемого числа. Примеры:
Сообщить(Формат( 1234567.89, "Ч(0)20.4")); Сообщить(Формат( 1234567.89, "Ч14.4,_")); Сообщить(Формат( 123456000, "Ч(0)14>3"));
// Напечатает 000000001234567.8900 // Напечатает 1_234_567,8900 // Напечатает 00000000123456
Если длина результирующей строки недостаточна для представления значения, то на месте результата напечатаются девятки и заданные разделители, например //
Сообщить(Формат(1234567.89, "Ч8.2,_"));
Напечатает 9_999,99 .
Функция Формат выполнит преобразование "число - строка" без форматирова ния, если форматная строка не содержит компонентов Д и/или .Т или не имеет вид "ЧП[Д][С]", например пред = Формат(21, "ЧМ");
//
Вернет 21
2.8.5.2. ВЫВОД ДЕНЕЖНЫХ ВЕЛИЧИН И ЦЕЛЫХ ЧИСЕЛ ПРОПИСЬЮ • • •
Форматируемое значение выводится прописью как целое число, если форматная строка имеет вид "ЧП"; прописью в виде денежной суммы без копеек, если форматная строка имеет вид "ЧПД"; прописью в виде денежной суммы с копейками, если форматная строка имеет вид "ЧПДС"; Примеры:
Сообщить(Формат(59421.67, "ЧП")); Сообщить(Формат(5 9421.67, "ЧПД")); Сообщить(Формат(59421.67, "ЧПДС"));
Результат:
Пятьдесят девять тысяч четыреста двадцать два Пятьдесят девять тысяч четыреста двадцать два рубля Пятьдесят девять тысяч четыреста двадцать один рубль 67 копеек
По умолчанию образцы для представления чисел прописью берутся из файла 1CV7.SPL, который размещен в папке BIN и содержит 5 секций со следующими данными: {"Speller", {"Money", {"Рубль", "Рубля", "Рублей", "Копейка", "Копейки", "Копеек", "М"}}, {"Numbers", {"Один", "Два", "Три", "Четыре", "Пять", "Шесть", "Семь", "Восемь", "Девять", "Одна", "Две", "Десять", "Одиннадцать", "Двенадцать", "Тринадцать", "Четырнадцать", "Пятнадцать", "Шест надцать", "Семнадцать", "Восемнадцать", "Девятнадцать", "Двадцать", "Тридцать", "Сорок", "Пятьдесят", "Шестьдесят", "Семьдесят", "Восемьдесят", "Девяносто", "Сто", "Двести", "Триста", "Четыреста", "Пятьсот", "Шестьсот", "Семьсот", "Восемьсот", "Девятьсот", "Тысяча", "Тысячи", "Тысяч", "Миллион", "Миллиона", "Миллионов", "Миллиард", "Миллиарда", "Миллиардов", "Триллион", "Триллиона", "Триллионов", "Нуль"}}, {"Date", {"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь", "Января", "Февраля", "Марта", "Апреля", "Мая", "Июня", "Июля", "Августа", "Сентября", "Октября", "Ноября", "Декабря", "г.", "Квартал"}}, {"DateRange", {"Полугодие", "Месяцев"}}, {"WeekDay", {"Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"}} } Замечание. Последний элемент секции Money - это латинская буква М или F (сокра щения от male и female), указывающая, какой род (мужской или женский) используется для представления денежных единиц. Изменить способ вывода числа прописью можно, обратившись к процедуре Про пись, рассмотренной в разд. 2.8.5.5. 2.8.5.3.
ФОРМАТИРОВАНИЕ
СИМВОЛЬНЫХ ДАННЫХ
Форматная строка при форматировании символьных данных имеет вид "С[Д]", где Д - длина результирующей строки. Если Д больше длины форматируемой строки, то последняя дополняется пробелами справа, если меньше, то в результате сохраняют ся первые Д символов. Например: Сообщить(СтрДлина(Формат("ААББВВ", Сообшить(Формат("ААББВВ", "С2"));
"С10"))); //
//
Напечатает 10 Напечатает АА
Форматируемая строка не изменяется, если компонент Д форматной строки отсут ствует или на его месте стоит не число. Например: Сообщить(Формат("ААББВВ", "С"))); Сообщить(Формат("ААББВВ", "СК")); 2.8.5.4.
// //
Напечатает ААББВВ Напечатает ААББВВ
ФОРМАТИРОВАНИЕ ДАТ
При форматировании дат форматная строка начинается с буквы Д. Варианты зада ния форматной строки приведены в табл. 2.11. В ее последнем столбце приводятся ре зультаты форматирования дат 21.12.01 и 05.12.01. Таблица 2.11 Форматные строки для дат Форматная строка "ДДЦММГГ" "ДДЦММГГГГ" "ДДДММММГГГГ" "Д(0)ДДММММГГГГ"
"ДММММГГГГ" "ДММММГГ" "ДММММ"
Примеры
Вид результата
дд.мм.гг дд.мм.гггг
21.12.01
ДД месяц прописью ГГГГ г.
5 Декабря 2001 г.
ДД месяц прописью ГГГГ г., если в дне две цифры ОД месяц прописью ГГГГ г., если в дне одна цифра
21 Декабря 2001 г.
Месяц прописью ГГГГ г. Месяц прописью ГГ г.
Декабрь 2001 г.
21.12.2001
Декабрь 01г. Декабрь
Месяц прописью
"ДККККГГГГ" "ДККККГГ" "ДКККК"
Номер квартала ГГГГ г. Номер квартала ГГ г.
4 Квартал 2001 г. 4 Квартал 01 г. 4 Квартал
Номер квартала. ГГГММДЦ
"ДГГГММДД" "ДНННН"
05 Декабря 2001 г.
20011205
Название дня недели даты; по умолча нию берется из секции WeekDay файла 1CV7.SPL
Среда
Представление даты не изменяется, если форматная строка не отвечает приведен ным в табл. 2.11 требованиям. 2.8.5.5.
ИЗМЕНЕНИЕ СПОСОБА
ВЫВОДА
ЧИСЕЛ ПРОПИСЬЮ
Осуществляется процедурой Пропись([образец]); в которой параметр образец - это • либо строка, задающая имя файла образцов прописей; • либо переменная типа СписокЗначений, содержащая образцы прописей (тип данных СписокЗначений рассмотрен в разд. 3.2); • либо пустая строка.
В последнем случае образцы прописей берутся из заданного по умолчанию файла 1CV7.SPL. Также этот файл используется, если параметр образец опущен или если об ращений к процедуре Пропись не было. При задании файла образцов прописей нужно сохранять имеющиеся в файле по умолчанию (1CV7.SPL) секции и последовательность расположения образцов. При задании параметра образец в виде списка значений в нем можно определять либо все секции, имеющиеся в файле 1CV7.SPL, либо часть из них. В первом случае элементами параметра образец являются 5 списков значений, элементы которых - это образцы представлений числовых данных следующих секций: • • • • •
Money; Numbers; Date; DateRange; WeekDay.
Порядок задания образцов определен в файле 1CV7.SPL. Если образец включает не все секции, то в список значений, содержащий образец, в качестве представления заносится название соответствующей секции. Пример. Формируется список значений сЗнач, содержащий секцию WeekDay, с образцами, отличающимися от приведенных в файле 1CV7.SPL. Далее сЗнач исполь зуется в качестве параметра образец процедуры Пропись. С использованием нового образца функцией Формат выводится день недели. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сЗнач, сЗнач2; ОчиститьОкноСообщений(); сЗнач = СоздатьОбъект("СписокЗначений"); сЗнач2 = СоздатьОбъект("СписокЗначений"); // Раздел WeekDay сЗнач2.ДобавитьЗначение("Понед."); сЗнач2.ДобавитьЗначение("Вт."); сЗнач2.ДобавитьЗначение("Ср."); сЗнач2.ДобавитьЗначение("Четв."); сЗнач2.ДобавитьЗначение("Пятн."); сЗнач2.ДобавитьЗначение("Субб."); сЗнач2.ДобавитьЗначение("Воскр."); // Параметр образец - список значений, элементы которого также списки значений // Не забываем определить представление "WeekDay" при добавлении в список сЗнач // элемента сЗнач! сЗнач.ДобавитьЗначение(сЗнач2, "WeekDay"); // Теперь при выводе дней недели будут использоваться названия списка сЗнач2, // добавленного в образец сЗнач Пропись(сЗнач); Сообщить(Формат('21.12.01', "ДНННН")); // Напечатает Пяти. Сообщить(Формат('05.12.01', "ДНННН")); // Напечатает Ср. // Возвращаемся к установленному по умолчанию файлу с образцами прописей Пропись(""); Сообщить(Формат('21.12.01', "ДНННН")); // Напечатает Пятница Сообщить(Формат('05.12.01', "ДНННН")); // Напечатает Среда конецПроцедуры // Выполнить
2.9. УПРАВЛЯЮЩИЕ ОПЕРАТОРЫ И КОНСТРУКЦИИ 2.9.1. АЛГОРИТМ КАК СОВОКУПНОСТЬ БАЗОВЫХ СТРУКТУР Программа и ее отдельные части создаются на основе алгоритмов. Фактически программа - это кодировка некоторого алгоритма или группы алгоритмов. Из этого следует, что до написания кода необходимо разработать и записать (последнее не всег да делается) алгоритм. Алгоритм - это последовательность действий, либо приводящая к решению зада чи, либо поясняющая, почему это решение получить нельзя. Например, отрицатель ный результат получается при ошибочном задании входных данных. Алгоритм составляется из отдельных фрагментов, которые могут иметь одну из следующих структур: • блок операторов и конструкций; • ветвление; • цикл. Блок операторов и конструкций (БОК) - это выполнение одного или нескольких простых или сложных действий. БОК может содержать и ветвления и циклы, которые являются примерами сложных действий. Простым действием является, например, в ыполнение присваивания или вызов процедуры. Конструкции состоят из нескольких операторов и используются для выполнения управляющих действий, например цик лов. Так, конструкция Если ... КонецЕсли состоит из двух операторов: Если и КонецЕсли. Последний оператор конструкции должен завершаться точкой с запятой. Ветвление - это выбор одного из возможных направлений выполнения алгоритма, осуществляемый в зависимости от значения некоторых условий. Ветвления подразде ляются следующие виды: • если - то; • если - то - иначе; • если - то - иначе - если; • выбор по ключу (в 1С это ветвление не используется, вместо него употребляется "Ес ли - то - иначе - если"); • попытка. Цикл - это повторное выполнение некоторого БОК с разными, как правило, значе ниями входящих в БОК переменных. Однократное выполнение БОК цикла называют итерацией. БОК цикла также называют телом цикла. Различают следующие циклы: • с параметром; • пока; • до. Последний вид цикла применяется крайне редко; в 1С он отсутствует. При записи алгоритма составляющие его базовые структуры либо отображаются графически, либо записываются в виде линейных схем. Проиллюстрируем обе эти воз можности.
2.9.2. ВЕТВЛЕНИЯ " Е С Л И " В ветвлениях "если - то", "если - то - иначе" и "если - то - иначе - если" для записи условий используется логическое выражение (ЛВ), результатом которого может быть истина (И) или ложь (Л). Графически ветвления проиллюстрирует рис. 2.5.
Рис. 2.5. Ветвления: а - ветвление "если - то"; б - ветвление "если - то - иначе" • • •
Ветвление "если - то" работает так: вычисляется значение ЛВ; если оно истинно, то выполняется БОК1; если оно ложно, то управление передается БОК2. Записи ветвления "если - то" в линейной схеме алгоритма:
Х°. Если истинно ЛВ, то [выполнить:] БОК1 конец если [Х°]. В 1С ветвление "если - то" записывается очень похоже: если ЛВ тогда БОК1 конецЕсли; • • • •
//
Некоторые операторы
Ветвление "если - то - иначе" работает так: вычисляется значение ЛВ; если оно истинно, то выполняется БОК1 если оно ложно, то выполняется БОК2; далее управление передается БОК3. Запись ветвления "если - то - иначе" в линейной схеме алгоритма:
Х°. Если истинно ЛВ, то [выполнить:] БОК1 иначе [выполнить:] БОК2 конец если [Х°]. Запись ветвления "если - то - иначе" в 1С: если ЛВ тогда БОК1 иначе БОК2 конецЕсли;
//
Некоторые операторы
//
Некоторые операторы
Пример использования ветвлений "если - то" и "если - то - иначе" см. в разд. 1.7.3. Замечание. Если БОК1 и БОК2 в ветвлении "если - то - иначе" являются выраже ниями, то для выбора вычисляемого выражения лучше употреблять функцию, вычис ляющую выражение по условию. Ее синтаксис: результат = ?(ЛВ, выражение1 , выражение2 ); Пример: у = ?(х > 0, Лог(х), х);
//
Вернет Лог(х), если х >0, или х - в противном случае
Приведенный оператор заменяет следующее ветвление: если х > 0 тогда у = Лог(х); иначе у = х; конецЕсли; Для ветвления "если - то - иначе - если" укажем только способ его записи в 1 С: если ЛВ1 тогда БОК1 иначеЕсли ЛВ2 тогда БОК2
//
Некоторые операторы
//
Некоторые операторы
иначеЕсли ЛВк тогда БОКк // [иначе // БОК к+1 ] // конецЕсли;
Некоторые операторы Последние два элемента конструкции являются необязательными
2.9.3. "ПОПЫТКА" Ветвление Попытка служит для обработки исключений и реализуется в 1С виде следующей конструкции: попытка БОК1, исключение БОК2 конецПопытки; В конструкции БОК, - это последовательность операторов, в которых может воз никнуть исключение, а в БОК 2 - это последовательность операторов, обрабатывающих исключение. Напомним, что исключение - это ошибка, возникающая при исполнении програм мы, например деление на нуль или выход за границы массива. В первом случае систе ма выдаст сообщение "Деление на 0", во втором - "Значение индексного выражения
Работа программы, если не используется обработка исключений, при возникнове нии ошибки прекращается. Некоторые ошибки, приводящие к останову программы, могут быть, однако, устранены. Например, ошибка блокировки данных, возникающая при обращении к файлу, захваченному в настоящий момент другим приложением, устраняется после завершения работы этого приложения. Другие ошибки, если их иг норировать, не исказят конечного результата. Например, выход за границы массива. В таких случаях целесообразно применить конструкцию Попытка с тем, чтобы при возникновении ошибки выполнить ее обработку, например просто сообщить об ошиб ке, и продолжить расчеты. Пример. Вычисляется сумма элементов вектора. В процедуре при обращении к вектору умышленно введена ошибка - выход за границы вектора. Для ее преодоле ния применяется конструкция Попытка. процедура Выполнить() // Запускаем процедуру из обработки Проба перем а[5], ин, сум; // Инициализация вектора а а[1] = 0; а[2] = 1.5; а[3] = 2.5; а[4] = -1; а[5] = 7; // Сумма элементов массива а. Ожидаемый результат: сум = 10 сум = 0; // Умышленно для иллюстрации работы конструкции Попытка // задаем верхний параметр цикла большим числа элементов вектора а для ин = 1 по 7 цикл попытка // При выходе за границы вектора сум = сум + а[ин]; // произойдет обработка исключения; исключение // исполнение программы не прекратится Сообщить(ОписаниеОшибки() + ". Вычисления будут продолжены."); конецПопытки; конецЦикла; // для Сообщить("сум = " + сум); // Напечатает 10 конецПроцедуры // Выполнить Замечание. Встроенная функция ОписаниеОшибки возвращает описание ошибки в том виде, в каком оно выдается системой в окно сообщений при отсутствии обработ ки исключений. Конструкции Попытка могут быть вложенными. В таких случаях при возникнове нии ошибки ее обработка передается оператору Исключение того уровня вложенно сти, на котором ошибка возникла. Чтобы передать обработку ошибки оператору более высокого уровня, применяется оператор ВызватьИсключение. Если оператор ВызватьИсключение применен, а внешнего обработчика не существует, то произойдет останов программы с выдачей сообщения об ошибке. Последний эффект будет, в ча стности, получен, если оператор ВызватьИсключение вставить в код вышеприведен ного примера перед оператором КонецПопытки. Пример. Вычисляется сумма элементов вектора. В процедуре при обращении к вектору умышленно введена ошибка - выход за его границы. Для ее преодоления применяется конструкция Попытка. Однако в этот раз в результате применения опера тора ВызватьИсключение осуществляется выход из цикла и передача управления внешнему обработчику.
процедура Выполнить() // Запускаем процедуру из обработки Проба перем а[5], ин, сум; // Инициализация вектора а а[1] = 0; а[2] = 1.5; а[3] = 2.5; а[4] = -1; а[5] = 7; // Сумма элементов массива а. Ожидаемый результат: сум = 10 сум = 0; // Умышленно для иллюстрации работы конструкций Попытка // задаем верхний параметр цикла большим числа элементов вектора а попытка для ин = 1 по 7 цикл попытка // При выходе за границы вектора передадим сум = сум + а[ин]; // управление внешнему обработчику исключений исключение вызватьИсключение; // Передача управления внешнему обработчику конецПопытки; // внутренней конецЦикла; // для исключение Сообщить(ОписаниеОшибки() + ". Осуществлен выход из цикла. Работа продолжается"); конецПопытки; // внешней Сообщить("сум = " + сум); // Напечатает 10 конецПроцедуры // Выполнить Замечание. Внешний обработчик в данной ситуации, конечно же, избыточен и введен лишь для иллюстрации оператора ВызватьИсключение. При отсутствии внешней конструкции Попытка вместо оператора ВызватьИсключение следует упот ребить оператор Прервать, предваренный, например, тем же, что и в приведенном при мере, вызовом процедуры Сообщить.
2.9.4. ЦИКЛЫ В цикле "с параметром n" задаются начальное значение параметра к, его конечное значение к и шаг ш - отличная от нуля величина, на которую изменяется значение па раметра и после выполнения очередной итерации. В 1С шаг ш всегда принимается равным единице. Параметр п также называют переменной цикла, которая должна быть целочисленной. Параметры ник являются в 1С числовыми выражениями и представ ляют соответственно нижнюю и верхнюю границы переменной цикла. Если параметр н(к) вычисляется с нецелым значением, то в качестве параметра используется целая часть результата. Замечание. Параметр цикла часто называют его индексом, для обозначения кото рого в программах нередко употребляют имя ин или инд. Графически цикл "с параметром" иллюстрирует рис. 2.6.
Рис. 2.6. Цикл с параметром
Цикл "с параметром" работает так (случай ш > 0): 1°. Присвоить: п = н. 2°. Если п <= к, то перейти к п. 3°, иначе завершить цикл. 3°. Выполнить БОК. 4°. Присвоить: п = п + ш и перейти к п. 2° (повтор). Когда ш < 0, п. 2° выглядит так: 2°. Если п > к , то переход к п. 3°, иначе завершить цикл. Замечания: 1. В цикле "с параметром" приведенные в пп. 1° и 4° операторы в тексте программы не присутствуют, но будут автоматически встроены компилятором в объектный код при компиляции программы. 2.
Во многих языках программирования, например в Фортране, в цикле "с парамет ром" запрещается в теле цикла менять значения переменной цикла и. Изменение параметров н, к и шага ш в теле цикла не отразится на выполнении цикла: цикл будет выполняться с теми значениями параметров, какие они имели до начала первой итерации цикла. В 1С запрет на изменение значения переменной цикла п не действует. Однако следует помнить, что изменение п в теле цикла - это плохой стиль программирования. Запись цикла "с параметром" в линейной схеме алгоритма:
Х°. С параметром п = н,к,ш [выполнить:] БОК конец цикла [с параметром п] | [Х°]. и в 1С: для п = н по к цикл БОК конецЦикла; Цикл Пока выполняется до тех пор, пока истинно некоторое ЛВ. Причем проверка ис тинности ЛВ выполняется перед началом очередной итерации. Цикл До отличается от цикла Пока тем, что проверка истинности ЛВ осуществляется после выполнения оче редной итерации. Графическая интерпретация циклов Пока и До приведена на рис. 2.7.
6 Рис. 2.7. Циклы: а - цикл Пока; б - цикл До
Замечание. При работе с циклами Пока и До надо следить, чтобы ЛВ обязательно рано или поздно приняло значение ложь. Иначе произойдет зацикливание - "бесконеч ное" выполнение операторов цикла. Запись циклов Пока в линейной схеме алгоритма: Х°. Пока истинно ЛВ [, выполнить:] БОК конец цикла Х°. и в 1С: пока ЛВ цикл БОК конецЦикла;
//
Некоторые операторы
Замечания: 1. Далее при ссылке на циклы будем использовать принятые в 1С имена операторов циклов - Для и Пока. 2.
Циклы Для и Пока могут быть вложенными. То есть в теле цикла Для (Пока) могут быть другие циклы Для и/или Пока.
Проиллюстрируем второе замечание примером, записав код вывода групп подряд следующих непериодических констант, сообщающий перед выводом группы ее номер. (Уточним: в примере группа - это подмножество подряд следующих непериодических констант, не имеющее периодических констант. То есть группы разделяются одной и более периодическими константами.) // Процедура, выводящая группы непериодических констант // Запускается из обработки Проба процедура Выполнить() перем всегоКонстант, ин, номерГруппы, идеи; ОчиститьОкноСообщений(); номерГруппы = 0; всегоКонстант = Метаданные.Константа(); ин = 1; // Номер константы пока ин <= всегоКонстант цикл если Метаданные.Константа(ин).Периодический = 0 тогда номерГруппы = номерГруппы + 1; Сообщить("Выводится группа с номером " + номерГруппы); // Вложенный цикл Пока пока Метаданные.Константа(ин).Периодический = 0 цикл идеи = Метаданные.Константа(ин).Идентификатор; Сообщить(иден + " " + Константа.ПолучитьАтрибут(иден)); ин = ин + 1; // Не забываем перейти к следующей константе если ин > всегоКонстант тогда прервать; // Досрочный выход из вложенного цикла конецЕсли; конецЦикла; // пока Метаданные.Константа(ин).Периодический = 0 иначе ин = ин + 1;
//
//
Имеем периодическую константу Не забываем перейти к следующей константе
конецЦикла; // пока ин <= всегоКонстант если номерГруппы = 0 тогда Сообщить("В конфигураторе нет непериодических констант"); конецЕсли; конецПроцедуры // Выполнить
2.9.5. О ВЫЧИСЛЕНИИ ЛОГИЧЕСКИХ ВЫРАЖЕНИЙ В 1С ЛВ при их оценке всегда вычисляются полностью (по крайней мере в теку щей версии). Это создает некоторые неудобства. Так, в последнем примере вложенный цикл Пока было бы лучше записать следующим образом, обойдя применение операто ра Прервать: пока (ин <= всегоКонстант) и (Метаданные.Константа(ин).Периодический = 0) цикл идеи = Метаданные.Константа(ин).Идентификатор; Сообщить(иден + " " + Константа.ПолучитьАтрибут(иден)); ин = ин + 1; // Не забываем перейти к следующей константе конецЦикла; // пока (вложенного) Однако тогда возникнет ошибка исполнения пока (ин <= всегоКонстант) и (Метаданные.Константа(ин).Периодический = 0) цикл {D:\ПPOБA.ERT(14)}: Поле агрегатного объекта не обнаружено (Периодический) Ее природа в том, что на некотором шаге значение переменной ин стало больше значения переменной всегоКонстант. При этом значение ЛВ (ин <= всегоКонстант) и (Метаданные.Константа(ин).Периодический = 0) независимо от значения второго выражения отношения будет ложь. Поэтому в принципе вычисление второго выражения отношения избыточно и его можно не выполнять. Но в 1С оно вычисляется, и, поскольку константы с номером ин = всегоКонстант + 1 нет, возника ет описанная выше ошибка, приводящая к останову программы. Рассмотрим теперь конструкцию "если" с ЛВ, состоящим из двух подвыражений отношения, объединенных логической операцией ИЛИ: если (3 > 2) или (Лог(0) = 1) тогда сообщить("Что-то"); конецЕсли; Результат этого ЛВ, если первое подвыражение истинно, не зависит от значения второго подвыражения, и, следовательно, оно могло бы не вычисляться. Однако в 1С ЛВ вычисляются полностью, поэтому данный код, поскольку в нем вычисляется 1п0, приведет к ошибке исполнения, о которой 1С сообщит следующим образом: если (3 > 2) или (Лог(0) = 1) тогда {D:\lCV77\CONFIGMELZ\EXTFORMS\ПРОБA.ERT(3)}: Деление на 0
2.9.6. ПРИМЕР ЗАПИСИ АЛГОРИТМА Обратимся для иллюстрации правил записи алгоритма к ранее рассмотренной задаче вывода списка непериодических констант, для которых в метаданных задан синоним (разд. 1.7.3). В этой задаче сведения одного вида (идентификатор, синоним и значение) выводятся для различных констант. Повторное выполнение одних и тех же действий ре ализуется в виде цикла. Здесь наиболее уместен цикл "с параметром", в котором в качестве
параметра выступает номер константы. Для учета условий, ограничивающих вывод дан ных, используем условие "если - то". Алгоритм решения задачи можно отобразить в виде следующей линейной схемы: 1.
Начало. // Число констант в конфигурации 2. всегоКонстант = Метаданные.Константа() // Переменная флагВывода принимает значение 1, если напечатаны данные // хотя бы об одной константе 3. флагВывода = 0; 4. С параметром ин= 1, всегоКонстант выполнить: 4.1. Если константа с номером ин непериодическая, то // Синоним константы с номером ин син = Метаданные.Константа(ин). Синоним; 4.1.1. Если син не является строкой нулевой длины, то флагВывода = 1; Вывести идентификатор, синоним и значение константы с номером ин. конец если 4.1.1. конец если 4.1. конец цикла 4. 5. Если флагВывода = 0, то Сообщить: "Нет непериодических констант с непустым синонимом." конец если 5. 6. Конец. Код приведенного фрагмента на языке 1С будет незначительно отличаться от кода процедуры Выполнить примера 3 разд. 1.7.3. Условия 4.1 и 4.1.1 можно объединить таким образом: 4.1. Если константа с номером ин непериодическая и константа имеет синоним, то флагВывода = 1; Вывести идентификатор, синоним и значение константы с номером ин. конец если 4.1. Такому объединению отвечает следующий код на 1С: если (Метаданные.Константа(ин).Периодический = 0) и (ПустаяСтрока(Метаданные.Константа(ин). Синоним) = 0) тогда флагВывода = 1; син = Метаданные.Константа(ин).Синоним; идеи = Метаданные.Константа(ин).Идентификатор; значен = Константа.ПолучитьАтрибут(иден); Сообщить(иден + " - " + син + " - " + значен); конецЕсли; Его недостаток в том, что дважды извлекается информация о синониме, что связано с дополнительными временными издержками; с другой стороны, он компактнее первого варианта. Замечание. Встроенная функция ПустаяСтрока(строка) вернет 1, если ее аргумент строка имеет нулевую длину или состоит из одних пробелов, в противном случае функция вернет 0.
Любой цикл с параметром можно, конечно же, заменить циклом Пока, например ин = 1; // Подготовка к циклу пока ин <= всегоКонстант цикл // Тело цикла см. в вышеприведенном цикле "с параметром"
Пока
// Добавляем в тело цикла оператор увеличения номера константы // Без этого оператора произойдет зацикливание ин = ин+ 1; конецЦикла;
2.9.7. ПРЕРЫВАНИЯ ЦИКЛА. ОБЪЕДИНЕНИЕ УСЛОВИЙ Выйти из цикла и передать управление на первый следующий за циклом выполняе мый оператор можно, применив оператор Прервать. Чтобы пропустить часть операторов цикла и перейти к следующей итерации, следует использовать оператор Продолжить. При этом управление передается оператору начала цикла - оператору Для или Пока. Операторы Прервать и Продолжить отдельно не применяются, а встраиваются в конструкции "если". Пример. Сообщить значение первой непериодической константы числового типа. // Процедура, выводящая значение первой непериодической константы числового типа // Запускается из обработки Проба процедура Выполнить() перем всегоКонстант, флагВывода, ин, идеи; ОчиститьОкноСообщений(); // флагВывода примет значение 1, если будет обнаружена // непериодическая константа числового типа флагВывода = 0; всегоКонстант = Метаданные.Константа(); для ин = 1 по всегоКонстант цикл если Метаданные.Константа(ин).Периодический = 1 тогда продолжить; // Передаем управление оператору Для конецЕсли; если Метаданные.Константа(ин).Тип = "Число" тогда идеи = Метаданные.Константа(ин).Идентификатор; Сообщить(иден + " " + Константа.ПолучитьАтрибут(иден)); // БалансДней 1 флагВывода = 1; прервать; // Досрочный выход из цикла Для конецЕсли; конецЦикла; // для если флагВывода = 0 тогда Сообщить("В конфигураторе нет непериодических констант числового типа."); конецЕсли; конецПроцедуры // Выполнить Замечание. Иногда программисты в цикле Для вместо оператора Прервать прибе гают к изменению значения переменной цикла ин. Так, в нашем случае оператор Пре рвать мог быть заменен оператором ин = всегоКонстант;
Такие действия, однако, классифицируются как плохой стиль программирования. Некоторые программисты считают, что операторы прерывания цикла (в 1С это Продолжить и Прервать) ухудшают структуру программы, и поэтому по возможности отказываются от их употребления. Взамен используется объединение условий. Последуем и мы принципам структурного программирования, написав решающий вышеприведенную задачу код, использующий объединение условий. В этом коде нам придется отказаться от цикла Для, заменив его циклом Пока. // Процедура, использующая объединение условий и выводящая значение первой // непериодической константы числового типа. Запускается из обработки Проба процедура Выполнить() перем всегоКонстант, флагВывода, ин, идеи; ОчиститьОкноСообщений(); // флагВывода примет значение 1, если будет обнаружена // непериодическая константа числового типа флагВывода = 0; всегоКонстант = Метаданные.Константа(); ин = 1; // Номер константы пока (ин<= всегоКонстант) и (флагВывода = 0) цикл если (Метаданные.Константа(ин).Периодический = 0) и (Метаданные.Константа(ин).Тип = "Число") тогда идеи = Метаданные.Константа(ин).Идентификатор; Сообщить(иден + " " + Константа.ПолучитьАтрибут(иден)); флагВывода = 1; конецЕсли; ин = ин + 1; // Не забываем перейти к следующей константе конецЦикла; // для если флагВывода = 0 тогда Сообщить("В конфигураторе нет непериодических констант числового типа."); конецЕсли; конецПроцедуры // Выполнить В приведенном коде объединение условий использовано при записи ЛВ дважды: (ин <= всегоКонстант) и (флагВывода = 0)
(Метаданные.Константа(ин).Периодический = 0) и (Метаданные.Константа(ин).Тип = "Число") Это позволило нам исключить из процедуры операторы Продолжить и Прервать.
2.9.8. ПЕРЕХОД ПО МЕТКЕ Выполняется оператором Перейти метка; где метка - это имя, начинающееся с тильды (знака ~), например ~М1. Этот оператор неприемлем для сторонников структурного программирования. Однако есть ситуации, кода он полезен, например для досрочного выхода из вложен ного цикла.
Пример. Записать, код вывода групп подряд следующих непериодических конс тант, сообщающий перед выводом группы ее номер, завершая вывод, обнаружив непе риодическую константу типа Календарь. // Процедура, выводящая группы непериодических констант // Завершается либо при обнаружении непериодической константы типа Календарь, // либо после вывода всех групп. Запускается из обработки Проба процедура Выполнить() перем всегоКонстант, ин, номерГруппы, иден; ОчиститьОкноСообщений(); номерГруппы = 0; всегоКонстант = Метаданные.Константа(); ин = 1; // Номер константы v пока ин <= всегоКонстант цикл если Метаданные.Константа(ин).Периодический = 0 тогда номерГруппы = номерГруппы + 1; Сообщить("Выводится группа с номером " + номерГруппы); // Вложенный цикл Пока пока Метаданные.Константа(ин).Периодический = 0 цикл если Метаданные.Константа(ин).Тип = "Календарь" тогда перейти ~М1; // Выход из всех циклов конецЕсли; иден = Метаданные.Константа(ин).Идентификатор; Сообщить(иден + " " + Константа.ПолучитьАтрибут(иден)); ин = ин + 1; // Не забываем перейти к следующей константе если ин > всегоКонстант тогда прервать; // Досрочный выход из вложенного цикла конецЕсли; конецЦикла; // пока Метаданные.Константа(ин).Периодический = 0 иначе // Имеем периодическую константу ин = ин + 1; // Не забываем перейти к следующей константе конецЕсли; конецЦикла; // пока ин <= всегоКонстант // Располагаем после метки ~М1 двоеточие ~М1: // Сюда передается управление оператором Перейти ~М1; если номерГруппы = 0 тогда Сообщить("В конфигураторе нет непериодических констант."); конецЕсли; конецПроцедуры // Выполнить Замечание. В данной задаче метку ~М1 нельзя расположить перед оператором ко нецЦикла: ~М1: конец цикла; // пока ин <= всегоКонстант поскольку такое использование метки не приводит к выходу из цикла, а передает управление на оператор Пока внешнего цикла. В конкретном случае результатом тако го размещения метки будет зацикливание программы. Разумеется, задачу можно решить и без использования оператора, введя флаг об наружения константы типа Календарь и применив, например, объединение условий. При желании соответствующий код вы можете составить самостоятельно.
2.10. ПОЛЬЗОВАТЕЛЬСКИЕ ПРОЦЕДУРЫ И ФУНКЦИИ 2.10.1. ПРОГРАММИРОВАНИЕ "СВЕРХУ ВНИЗ" Разработка алгоритмов и программ осуществляется, как правило, по принципу "сверху вниз". Суть такого подхода состоит в разбиении исходной задачи на ряд более простых задач - фрагментов - и последующей работе с ними. При разбиении задачи на фрагменты надо придерживаться следующей схемы: 1.
Проанализировать задачу и выделить в ней фрагменты.
2.
Отобразить процесс разбиения в виде блок-схемы или линейной схемы и пронумеро вать в ней фрагменты.
3.
Установить между выделенными фрагментами связи: для каждого фрагмента опреде лить, какие данные он получает (входные данные) и какие данные возвращает (выход ные данные). Связи между фрагментами называются интерфейсом.
4.
Рассмотреть далее каждый фрагмент самостоятельно; разработать для него алгоритм и записать его либо в виде линейной схемы, либо в виде блок-схемы. При необходи мости подвергнуть фрагмент разбиению на более мелкие фрагменты. Такое разбиение продолжать до тех пор, пока не будут получены фрагменты, программирование кото рых не составляет особых затруднений.
5.
Оформить выделенные фрагменты в виде программных компонентов или БОК.
При таком подходе программу можно рассматривать как совокупность фрагмен тов, которые, принимая некоторые данные, вырабатывают результат и передают его другому фрагменту и так вплоть до завершения вычислений. Составляемые для фрагментов линейные схемы алгоритмов (разд. 2.8) сопровож даются заголовком и описанием интерфейса, отражающим состав входных и выход ных данных. В 1С для реализации фрагментов можно использовать программные компоненты: основную программу модуля, процедуры модуля и его функции. Фрагмент алгоритма, как правило, оформляется в виде функции, если в результате выполненных в нем вычислений возвращается единственный скаляр. Если же фраг мент возвращает в вызывающий фрагмент в качестве результата несколько значений, в том числе и в виде массива, или не возвращает результатов, н апример осуществляет вывод данных в файл, то он оформляется в виде процедуры.
2.10.2. СТРУКТУРА ПРОЦЕДУР И ФУНКЦИЙ Процедуры и функции имеют схожую структуру: Процедура | Функция Имя([[3нач] парам1 = [значение^]], ..., [[Знач] парамп = [значениеп]]) [Экспорт] [неисполняемые операторы объявления переменных] [исполняемые операторы] КонецПроцедуры | КонецФункции
Ключевое слово, или атрибут, Экспорт указывается для процедур и функций, рас полагаемых в глобальном модуле (разд. 1.6), в том случае, если их надо сделать дос тупными для вызова в модулях форм. Процедуры вызываются в виде отдельного оператора с соблюдением следующего синтаксиса: Имя_процедуры([парам1], ..., [парамn]); Функции, как правило, вызываются из выражений, но могут, так же как и процеду ры, вызываться в виде отдельно оператора: Имя_функции([парам1],..., [парамп]); Функция, если выход из нее осуществляется не в результате выполнения оператора Возврат выражение; а по оператору КонецФункции возвращает число 0. Функция, в которой нет оператора Возврат выражение; всегда возвращает нуль. Замечание. При программировании функции следует выходить из нее по оператору Возврат выражение; даже если выражение - это константа нуль. Например, функции ф1 и ф2 функция ф1(а) функция ф2(а) если а > 0 тогда если а > 0 тогда возврат 2 * а; возврат 2 * а; иначе конецЕсли; возврат 0; конецФункции // ф2 конецЕсли; конецФункции // ф1 возвращают одинаковый результат, однако вариант с ф1 отвечает принятому для функций стилю программирования, чего нельзя сказать о функции ф2. Впрочем, в данной ситуации функцию ф1 лучше записать так: функция ф1(a) возврат ?(а > 0, 2 * а, 0); конецФункции // ф1 Процедуры и функции могут вызываться только из тех процедур и функций моду ля, ранее которых расположены либо они сами, либо их предварительное описание (разд. 1.4.4). Например, в модуле с кодом функция ф1(а) возврат ?(а > 0, 2 * а, 0); .конецФункции // ф1
процедура п 1 (д) далее
// Предварительное описание процедуры п\
процедура Выполнить() перем б; б = 2.5 * ф1(3); п1(б); Сообщить(б); конецПроцедуры // Выполнить
// б= 15; функция ф1 вызывается из выражения // б = 35; б - фактический параметр процедуры п1
функция ф2(с) возврат с / 2; конецФункции // ф2 процедура п1(д) д = 4*ф2(д) + 5; конецПроцедуры // п1
// д - фактический параметр процедуры n1 // д = 4 * 7.5 + 5 = 35
из процедуры Выполнить можно вызвать функцию ф1 и процедуру nl, но нельзя вы звать функцию ф2. Замечания: 1.
Предварительное описание процедуры (функции) должно совершенно точно дуб лировать заголовок процедуры (функции). Так, не допускаются расхождения имен параметров, и если заголовок процедуры (функции) снабжен ключевым словом Экспорт, то оно должно присутствовать и в тексте предварительного описания пе ред ключевым словом Далее. Например: процедура п3(д1, д2) экспорт далее // Предварительное описание процедуры пЗ
2. Перечень имен процедур и функций модуля можно просмотреть, нажав, находясь в конфигураторе, на иконку на панели инструментов Текстовый редактор. Так, для вышеприведенного набора процедур и функций, если их разместить, например, в модуле обработки Проба, мы получим приведенный на рис. 2.8 список (сортиров ка имен не задана).
Рис. 2.8. Окно вывода списка процедур и функций модуля На начало любой процедуры (функции) или на ее предварительное описание можно переместиться, выбрав в вышеприведенном окне необходимую строку и нажав на кнопку Перейти.
2.10.3. ПАРАМЕТРЫ ПРОЦЕДУР И ФУНКЦИЙ Обмен данными между программными компонентами 1С осуществляется через глобальные объекты, переменные модуля (разд. 1.4.4), диалога (разд. 1.5) и параметры процедур и функций. Дополнительно от функции можно получить и использовать в выражении возвращаемое ею значение. Параметры, используемые при вызове процедуры или функции, называются фак тическими. Так, в выражении, стоящем в правой части оператора присваивания д = 4 * ф2(д) + 5;
//
д = 4 * 7.5 + 5 = 35
из процедуры п\ предшествующего раздела параметр д функции ф2 является факти ческим. Параметры, стоящие в заголовке процедуры (функции), в том числе и в заголовке, присутствующем в тексте предварительного описания, называются формальными. Та ким является, например, параметр с в следующем заголовке: функция ф2(с)
//
с - формальный параметр функции ф2
При вызове процедуры (функции) между фактическими и формальными парамет рами устанавливается соответствие: формальный параметр с номером к адресует фак тический параметр (или его копию) с номером к. Рассмотрим процедуру п3 с тремя формальными параметрами а, б и в. Пусть по назначению первый параметр будет входным, то есть передающим данные в процеду ру, второй - выходным, то есть возвращающими данные в вызывающую программу, а третий - входным-выходным. Формальные параметры используются в процедуре как обычные переменные. Объявлять формальные параметры в теле процедуры посредст вом оператора Перем нельзя. процедура пЗ(а, б, в) перем с; с = 3 * а; б = с - в - 2; в = в + б + с/3; конецПроцедуры // пЗ
//
с - промежуточная переменная процедуры п3 //
Определяем значения параметров б и в
Обратимся к иЗ из процедуры Выполнить обработки Проба, использовав в качест ве фактических параметров переменные а1,а2 и а3. процедура Выполнить() перем а, а2, аЗ; ОчиститьОкноСообщений(); а=1; аЗ = 3; пЗ(а, а2, аЗ); // Сообщить("а2 = " + а2 + "; аЗ = " + аЗ); конецПроцедуры // Выполнить
Вызов процедуры п3
При таком вызове формальному параметру а процедуры п3 соответствует факти ческий параметр а, формальному параметру б фактический параметр а2 и формально му параметру в - фактический параметр а3. Понятно, что при вызове параметр а полу чит значение 1, а параметр в - значение 3. Параметр б - будет иметь пустое значение. Как видно из примера, имена формальных и фактических параметров могут и совпа дать и различаться. Замечание. Из всех присутствующих в процедуре Выполнить переменных обяза тельно объявление только переменной а2, в противном случае ее нельзя использовать в качестве фактического параметра процедуры п3, или - более широко - операндом лю бого выражения. После исполнения п3 в окне сообщений обнаружим следующий результат: а2 = -2; а3 = 2 Приведенное нами деление параметров на входные, выходные и входные-выход ные является условным, вытекающим из способа их употребления в программе. На месте любого фактического параметра может стоять константа. Например, вызов п3(1,
а2,
аЗ);
//
Первый
фактический
параметр
-
константа
полностью эквивалентен вышеприведенному. Более того, константа может быть и на месте третьего параметра: п3(1,а2,3); хотя соответствующий ему формальный параметр меняется в теле процедуры иЗ. В общем случае на месте фактического параметра может быть любое правильно построенное выражение. Например: п3(3 * а * а, а2, 3);
//
На месте первого параметра выражение 3 * а * а
Примеры с вызовом п3 показывают, что при выходе из процедуры фактический параметр, если он является переменной, принимает значение формального параметра. В этом случае говорят, что фактические параметры передаются по ссылке (формаль ный параметр адресует фактический параметр непосредственно). Можно, однако, ус тановить и иной способ передачи параметров - по значению. Для этого в списке фор мальных параметров перед параметром, передаваемым по значению, следует поста вить ключевое слово (атрибут) Знач, например процедура п3(а, б, знач в) конецПроцедуры // п3 В этом случае формальный параметр адресует копию фактического параметра и изменения формального параметра не отражаются на значении фактического. Дейст вительно, обратившись к модифицированной процедуре п3 перем а2; аЗ = 3; // Параметр а2 передается по ссылке, а параметр а3 - по значению п3(1, а2, аЗ); // Вызов процедуры Сообщить("а2 = " + а2 + "; аЗ = " + аЗ);
п3
найдем в окне сообщений текст а2 = -2; аЗ = 3 Как и ожидалось, значение а3 осталось без изменений. По значению параметры передаются, если есть необходимость защитить фактичес кий параметр от изменений в процедуре или функции. Рассмотрим следующий код: функция ф1(а) а = 2 * а; возврат а; конецФункции // ф1 функция ф2(знач а) а = 2 * а; возврат а; конецФункции // ф2 процедура Выполнить() ОчиститьОкноСообщений(); х=1; Сообщить(ф1(х) * х * ф1(х)); Сообщить(ф1(х) * ф1(х) * х); х = 1; Сообщить(ф2(х) * х * ф2(х)); Сообщить(ф2(х) * ф2(х) * х); конецПроцедуры // Выполнить
// //
Напечатает 16 Напечатает 2048
// //
Напечатает 4 Напечатает 4
Из примера видна разница между передачей параметров по ссылке (функция ф1) и передачей параметров по значению (функция ф2). Замечание. Системные константы (РазделительСтраниц, РазделительСтрок и СимволТабуляции) при использовании их в фактических формальных параметрах воспри нимаются как передаваемые по ссылке переменные. Эту неточность можно поправить, пе редавая их, если в этом есть необходимость, в программные компоненты по значению. Формальные параметры процедур и функций являются необязательными. То есть соответствующие им фактические параметры могут быть опущены. Следующие за ни ми запятые должны быть при этом сохранены. Например, к вышеприведенной проце дуре п3 можно обратиться, опустив все, кроме последнего, параметры: п3(, ,аЗ); При таком вызове и такой организации процедуры п3 в выражениях, где присутст вуют формальные параметры а и б, используются пустые значения. Можно, однако, в формальные параметры установить значения по умолчанию, которые будут исполь зоваться в выражениях при отсутствии соответствующих им фактических параметров. Для этого используется следующий синтаксис заголовка процедуры: процедура п3(а = 1, б = -2, в = 3) перем с; // с = 3*а; б = с - в - 2; // в = в + б + с/3; конецПроцедуры // п3
с - промежуточная переменная процедуры п3 Определяем значения параметров б и в
Теперь, когда для последнего формального параметра в задано значение по умолЛ чанию, можно, вызывая пЗ, опустить и соответствующий ему фактический параметре не сохраняя предшествующую ему запятую: п3(1,а2);
//
= -2 (верно!)
а2
Если же запятая сохранена, то процедура п3 вернет иное, ошибочное значение а2: п3(1,а2,);
//
а2
= 1 (ошибка!)
Поскольку компилятор ошибку не отслеживает, что в общем-то странно, нужно придерживаться общего правила обращения к процедуре (функции) без последних параметров: "Если процедура (функция) вызывается без последних параметров, то разделяющие их запятые и запятая, отделяющая эти параметры от присутствующего фактического параметра, опускаются". Например: // Процедура п3 вызвана без двух последних параметров п3(а); При вызовах процедур переменные, используемые как фактические параметры, могут менять свой тип. Например: процедура п4(а) а = "строка"; конецПроцедуры // п4 процедура Выполнить() ОчиститьОкноСообщений(); а=1; Сообщить(ТипЗначенияСтр(а)); п4(а); Сообщить(ТипЗначенияСтр(а)); конецПроцедуры // Выполнить
//
Число
//
Строка
Напомним, что формальными параметрами могут быть не только скалярные переменные произвольного типа, но и массивы (разд. 2.6). В этом случае соответствующие фактические параметры также должны быть массивами. Замечание. Формальные параметры могут иметь агрегатный тип данных, в чем мы смогли убедиться, например, в разд. 1.13, рассматривая контекст обработки. Соответ ствующие фактические параметры в этом случае должны иметь тот же тип.
2.10.4. ВЛОЖЕННЫЕ ВЫЗОВЫ ПРОЦЕДУР И ФУНКЦИЙ Любая процедура или функция может содержать вызовы иных процедур и функ ций, но не может вызвать сама себя явно или через другую процедуру (функцию). На личие такого вызова приведет к "зависанию" программы. Например: процедура п6(а) далее процедура п5(а) а = 2 * а; п6(а); конецПроцедуры // п5
// // //
Процедура п5 вызывает сама себя из процедуры п6. В результате имеем зависание программы
процедура п6(а) а = -а; п5(а); конецПроцедуры // п6 процедура Выполнить() Предупреждение("Сейчас программа зависнет. | Для продолжения вычислений нажмите Esc."); а=1; п5(а); конецПроцедуры // Выполнить
2.10.5. ОПЕРАТОР ВОЗВРАТ Как мы видели, функция возвращает результат выражения оператора Возврат выражение; Такой оператор может быть использован в функции неоднократно. Однако его можно разместить и в процедуре (обычно внутри конструкций "если" или Попытка). При этом выражение должно быть опущено. Например: процедура п7(а, б) если а <= 0 тогда Сообщить("Неверное значение аргумента функции Лог."); возврат; // выражение опущено конецЕсли; б = Лог(а); конецПроцедуры // п7 Впрочем, лучше обойтись без оператора Возврат, применив ветвление "если-тоиначе": процедура п8(а, б) если а <= 0 тогда Сообщить("Неверное значение аргумента функции Лог."); иначе б = Лог(а); конецЕсли; конецПроцедуры // п8
2.11. ВЫВОДЫ 1.
В 1С переменные могут быть простыми или структурированными. Простыми являют ся числовые, символьные переменные и переменные типа Дата (скалярные или масси вы). Сложной структурой обладают переменные агрегатных типов данных.
2.
Неопределенные переменные всех типов имеют пустое значение.
3.
Константы могут быть числового, символьного типа или типа Дата. Нельзя задать именованные константы, массив - константу и константу агрегатного типа данных. Среди символьных констант 3 являются системными. Нет констант со значениями истина и ложь.
4.
В выражении 1С можно употреблять данные различных типов. При вычислении выражения автоматически выполняются преобразования типов данных. Также для преобразования типов данных можно применить встроенные функции Дата, Стро ка и Число. Тип выражения определяется типом первого операнда.
5.
Все операции в выражениях 1С выполняются слева направо.
6.
Логические операции имеют более высокий приоритет, чем операции отношения, что нужно учитывать при записи логических выражений.
7.
Логические выражения нельзя размещать в правой части оператора присваивания, использовать в качестве фактических параметров процедур и функций и в выражении; оператора Возврат. Их место - это конструкции "если", Для, Пока и функция ?.
8.
Логические выражения, состоящие из нескольких подвыражений, всегда вычисляются полностью, несмотря на то, что их результат может быть известен до оценки последнего подвыражения.
9.
Массивы могут быть только одномерными. Индекс первого элемента массива всегда равен единице. Элементы массивов могут быть разных типов, в том числе; и агрегатных.
10. Для управления вычислениями в языке предусмотрены ветвления "если" и Попытка, циклы "с параметром" и Пока, операторы прерывания цикла, оператор перехо да и оператор выхода из процедуры (функции). 11. Структура программы улучшается, если в ней вместо операторов прерывания цик ла и перехода использовать объединение условий. 12. Фактические параметры процедур и функций, если они не являются константами и им не предшествует ключевое слово Знач, передаются по ссылке, в противном случае - по значению. 13. Фактические параметры процедур и функций являются необязательными. При o i l сутствии фактического параметра используется установленное для него по умол чанию значение, если оно задано, либо пустое значение - в противном случае. 14. Формальные параметры процедур и функций могут быть скалярами и массивамилюбого типа, в том числе и агрегатного. 15. Функция может вызываться, как и процедура, в виде отдельного оператора. При этом возвращаемое ею значение игнорируется. 16. Предварительное описание процедуры (функции), приведенное в начале модуля после объявления его переменных, снимает ограничение на место расположения, процедуры (функции) в теле модуля.
3. АГРЕГАТНЫЕ Т И П Ы Д А Н Н Ы Х О Б Щ Е Г О НАЗНАЧЕНИЯ 3.1. ОБЩИЕ СВОЙСТВА АГРЕГАТНЫХ ТИПОВ ДАННЫХ 1С В 1С помимо числовых, символьных данных и дат можно работать с объектами, относящимися к данным агрегатного типа. Всего в 1С определено около 30 агрегатных типов данных. Большинство из них приведено в табл. 3.1. Таблица 3.1 Агрегатные типы данных 1С Тип БухгалтерскиеИтоги ВидРасчета, ГруппаРасчетов ВидСубконто ГрупповойКонтекст Документ Журнал документов ЖурналРасчетов, РасчетныйПериод, ЗаписьЖурналаРасчетов Запрос Календарь, Праздники Картинка Константа КорректныеПроводки Метаданные Операция Периодический Перечисление ПланСчетов Регистр СписокЗначений, ТаблицаЗначений Справочник Счет Текст Таблица Форма ФС Xbase, Ключ
Раздел, где рассматривается или используется 7.3, 7.8,7.10, 7.11,7.12 7.15 1.13,5.2,5.4 5.8,7.6,7.9 5.8.3,7.6.5,7.9.5 7.1,7.2,7.4,7.13 5.11.3,7.4.7,7.17.4 7.5 3.4.4 1.7 1.6, 1.7,4.1,7.5.2.3,9.3.1.2 Гл. 6,9.3.1.2 1.13, Гл. 4, 7.3.3 1.8,3.3,3.4,7.17.2 Гл. 5,7.3.7, 8.5.2 1.10,7.17.3 1.9,5.11,7.17.2 1.8, 1.9, 3.3.4, 3.4.2, 5.3.3.1, 5.3.3.2, 5.7, 5.8.2, 5.8.3, 5.8.4, 5.10.1-5.10.3, 7.4.3, 7.6, 7.9 1.12,7.4.6 7.4.6,9.3.2
Замечание. Язык 1С не предоставляет возможности для создания иных, кроме оп ределенных в языке, агрегатных типов данных. Также нельзя изменить атрибуты (кроме реквизитов) и методы, используемые с объектами агрегатных типов. Агрегатные типы, введенные 1С, позволяют строить модели, отражающие процес сы в различных подразделениях предприятия, например на складе, в отделе снабжения или сбыта, бухгалтерии и др. Агрегатные типы можно разделить на две группы. В первую отнесем типы специального назначения, например Документ, ЖурналРасчетов, ПланСчетов и др. Объекты таких типов определены в конфигурации систе мы, поэтому, открыв ее, вы сможете просмотреть полный перечень типов первой группы и их разновидностей. Создавая в программе тот или иной объект этой группы, прежде необходимо удо стовериться, что он присутствует в конфигурации. Если это не так, то следующий код вызовет, если убрать конструкцию Попытка, ошибку исполнения, приводящую к оста- I нову вычислений: // Невозможно создать объект Справочник.Сотрудники_2, // поскольку его в конфигурации системы нет // Для преодоления ошибки, чтобы избежать останова программы, // используем конструкцию Попытка попытка сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); исключение // Неудачная попытка создания объекта Справочник.Сотрудники_2 Сообщить(ОписаниеОшибки()); конецПопытки; После добавления в конфигурацию справочника Сотрудники_2 приведенный выше код имеет право на существование. Однако всякое изменение конфигурации связано с нарастанием проблем по ее обновлению, поэтому мы, имея скромный опыт по обустройству 1С, отложим до лучших времен рассмотрение подобных объектов. Во второй группе агрегатных типов данных разместим типы общего назначения, такие, как Текст, СписокЗначений, ФС и др. Объекты этих типов называются внешними объектами (поскольку они не присутствуют в конфигурации). Они создаются на время исполнения программ для решения частных, предусмотренных алгоритмом задач. Функция ТипЗначения, заметим, возвращает для внешних объектов число 100. Например: тЗнач = СоздатьОбъект("ТаблицаЗначений"); Сообщить(ТипЗначения(тЗнач)); //Напечатает
100
К одному и тому же агрегатному типу данных в 1С могут относиться разные объекты, отличающиеся в некоторых случаях структурой. Например, переменные сКД и сСотр, появившиеся в программе в результате исполнения кода сКД = СоздатьОбъект("Справочник.КадровыеДанные"); сСотр = СоздатьОбъект("Справочник.Сотрудники"); имеют один тип - Справочник, но разную структуру, определяемую набором их рек визитов - компонентов агрегатного типа, значения которых хранятся в полях DBF- -
файлов базы данных системы. Объекты одного типа будем в дальнейшем называть разновидностями типа. Разновидности имеющихся в конфигурации типов можно вывести, использовав применяемые с метаданными методы (разд. 1.7.3). процедура Выполнить( ) // Вывод списка справочников ОчиститьОкноСообщений(); // Выводятся идентификаторы справочников для ин = 1 по Метаданные.Справочник( ) цикл Сообщить(Метаданные.Справочник(ин).Идентификатор); конецЦикла; // для // Имеем в окне сообщений: // Аттестация // БазаДополнительныхНачислений // БазаНалогов //... конецПроцедуры // Выполнить Замечание. Объект, например Справочник, можно создать без определения его ви да, то есть так: спр = СоздатьОбъект("Справочник"); // спр - объект типа Справочник неопределенного вида В дальнейшем разновидность устанавливается и изменяется методом Вид, например // Теперь спр - это объект с разновидностью типа Справочник.Сотрудники спр.Вид("Сотрудники"); Агрегатные типы данных первой группы описываются в конфигурации своими характеристиками, в качестве которых, например, для справочника Сотрудники ис пользуются следующие величины: Идентификатор Синоним Комментарий Владелец КоличествоУровней ДлинаКода ДлинаНаименования СерииКодов ТипКода "Числовой" ОсновноеПредставление КонтрольУ никальности АвтоНумерация ГруппыВпереди СпособРедактирования ЕдинаяФормаЭлемента ОсновнаяФорма ОсновнаяФормаДляВыбора ОбластьРаспространения АвтоРегистрация
"Сотрудники" "Список сотрудников предприятия" "3" "8" "40" "ВесьСправочник" "ВВидеНаименования" " 1" "2" " 1" "ВДиалоге" "0" "Справочник.Сотрудники.ФормаСписка.Административная" "Справочник.Сотрудники.ФормаСписка.ДляВвода" "ВсеИнформационныеБазы" " 1"
Идентификатор объекта задает его имя, которое, в частности, употребляется при вызове функции СоздатьОбъект. Доступ к характеристикам типа можно получить, как мы видели, пользуясь методами для метаданных, ну и, конечно же, через предусмотренный в конфигурации интерфейс. С каждым агрегатным типом связаны методы - процедуры и функции, позволяю щие обрабатывать объекты агрегатного типа. Изменять и/или добавлять имеющиеся в 1С для агрегатных типов данных методы нельзя; также не разрешается удалять, редактировать названия или добавлять характе ристики агрегатных типов. Значения характеристик константой не являются. Агрегатный тип имеет атрибуты - изменяемые величины, являющиеся компонен тами разновидностей типа. Так, атрибутами объектов агрегатного типа Справочник являются в частности реквизиты справочника, которые ссылаются на поля DBFфайлов, хранящих используемые справочником данные. Например, справочник Со трудники отображает данные, записанные в файл SC2.DBF и связанные с ним файлы. Имена (идентификаторы) реквизитов и соответствующих им полей DBF-файлов раз личаются. Реквизиты, в отличие от характеристик типа, могут удаляться, добавляться и редактироваться. Перечень характеристик и атрибутов каждой разновидности типа первой группы можно выгрузить в текстовый файл, выбрав в конфигурации пункты меню Конфигура ция - Описание структуры метаданных. Понятно, что разновидности одного и того же аг регатного типа имеют один и тот же набор характеристик (с разными значениями). Список идентификаторов и представлений (описаний) реквизитов, например спра вочника Сотрудники, выведет в окно сообщений следующий, анализирующий мета данные код: процедура Выполнить() ОчиститьОкноСообщений(); видСпр = "Сотрудники"; // Вид справочника Сообщить("Реквизиты справочника " + видСпр + " (их идентификаторы и представления)."); для ин = 1 по Метаданные.Справочник(видСпр).Реквизит( ) цикл иден = Метаданные.Справочник(видСпр).Реквизит(ин).Идентификатор; предст = Метаданные.Справочник(видСпр).Реквизит(ин).Представление(); Сообщить(иден + " -" + предст); конецЦикла; // Результат приведен после кода процедуры конецПроцедуры // Выполнить Результат: Реквизиты справочника Сотрудники (их идентификаторы и представления). Оклад - Оклад Тариф - Тариф Разряд - Разряд РазрядРабочих - Разряд рабочих ПроцентЕжемесПремии - % ежемес. премии Ставка - Ставка Категория - Категория ГрафикРаботы - График работы МестоРаботы - Место работы
Должность - Должность Подразделение - Подразделение формаТруда - Форма оплаты труда ТипСотрудника - Тип сотрудника ПрофСоюз - Членство в профсоюзе Аванс - Аванс ОплачиватьПитание - ОплачиватьПитание ОплачиватьПроезд - ОплачиватьПроезд КоличествоАкций - Количество акций ДатаСправки - ДатаСправки СальдоПН - Сальдо по под. налогу ВозвратПН - ВозвратПН ДатьЛьготы - ДатьЛьготы ДатаРождения - ДатаРождения СевернНадбавка - % северной надбавки ДатаСевернСтажа - ДатаСевернСтажа ДатаСтажаДляВыслугиЛет - ДатаСтажаДляВыслугиЛет ПриказОприеме - ПриказОприеме ПриказОбУвольнении - ПриказОбУвольнении • СправкаНаНачРасч - СправкаНаНачРасч СправкаСПрМестаР - СправкаСПрМестаР РК - Районный коэффициент Военнослужащий - Военнослужащий ХО-ХО СальдоПН_Фед - Сальдо по под. налогу в фед. бюдж. БезВзнСРаботод - Взимать с работодателя взносы в фонды БезВзнССотр - Взимать с сотр. взносы в фонды КоэфОплаты - Коэф-т оплаты БезВзносовВПФР - БезВзносовВПФР БезВзносовВФСС - БезВзносовВФСС БезВзносовВФОМС - БезВзносовВФОМС Срочник - Срочник ПодоходныйПосчитан - ПодоходныйПосчитан Замечания: 1. Наименование и Код являются заданными по умолчанию, не подлежащими пере именованию атрибутами (реквизитами) любого справочника 1С. Атрибут Наиме нование удаляется, если его длина задается равной нулю. Атрибут Код удалению не подлежит. 2.
Представление реквизита несет ту же нагрузку, что и синоним константы (разд. 1.7.1).
3.
Значения объектов типа Справочник, Документ и Перечисление можно получить, вызвав встроенную функцию ВвестиЗначение, рассмотренную в разд. 2.5.
4.
Идентификатор реквизита, например идентификатор Должность реквизита спра вочника Сотрудники, используется для доступа к значению реквизита. Для этого может быть пригоден следующий код (на примере разновидности типа Справоч ник.Сотрудники):
сСотр = СоздатьОбъект("Справочник. Сотрудники"); // Устанавливаем дату, на которую выбираются периодические реквизиты справочника сСотр.ИспользоватьДату(РабочаяДата()); // Ищем сотрудника, фамилия которого начинается с подстроки стр стр = "Га"; // Значение второго параметра - нуль, следовательно, // поиск осуществляем во всем справочнике флаг = сСотр.НайтиПоНаименованию(стр, 0); если флаг = 1 тогда // Сотрудник найден // Наименование - атрибут справочника Сотрудники, Должность - его реквизит. Сообщить(сСотр.Наименование); // Вывод фамилии найденного сотрудника Сообщить(сСотр.Должность); // Должность сотрудника иначе Предупреждение("Сотрудник, фамилия которого начинается с букв " + стр + " не найден."); конецЕсли; Изучение агрегатных типов мы начнем со списка и таблицы значений, объекты ко торых не фиксируются в конфигурации системы, но зато часто применяются в пользо вательских программах. Однако прежде вновь вернемся к понятию "пустое значение", распространив его на все типы данных 1С.
3.2. ОБОБЩЕНИЕ ПУСТОГО ЗНАЧЕНИЯ Переменная после ее объявления оператором Перем не определена, то есть имеет пустое значение, причем неопределенного типа. Например: перем а; // Напечатает 0, что соответствует неопределенному типу данных Сообщить(ТипЗначения(а)); Однако неопределенной переменной, употребив встроенную функцию ПолучитьПустоеЗначение, можно присвоить пустое значение некоторых имеющихся в 1С типов данных, таких, как Число; Строка; Дата; Справочник; Документ; Счет; ВидСубконто; ВидРасчета; Календарь. Например: // После объявления а - неопределенная переменная неопределенного типа перем а; а = ПолучитьПустоеЗначение("Дата"); Сообщить(ТипЗначенияСтр(а)); // Напечатает Дата
а = ПолучитьПустоеЗначение("ВидСубконто"); Сообщить(ТипЗначенияСтр(а)); //
Напечатает ВидСубконто
Другие типы данных пустых значений не имеют. Например: а = ПолучитьПустоеЗначение("КорректныеПроводки"); // Напечатает 0, что соответствует неопределенному типу данных Сообщить(ТипЗначения(а)); Также пустое значение заданного типа можно получить, передав функции ПолучитьПустоеЗначение не имя типа, а имя разновидности типа, например а = ПолучитьПустоеЗначение("ВидРасчета.ВыплатаЧерезБанк"); Сообщить(ТипЗначенияСтр(а)); // Напечатает ВидРасчета При этом функция ПолучитьПустоеЗначение вернет верное значение агрегатного типа, даже если ее аргумент задает несуществующую разновидность типа. Например: а= ПолучитьПустоеЗначение("Справочник.НетТакогоСправочника"); Сообщить(ТипЗначенияСтр(а)); // Напечатает Справочник Добавим, что константа или определенная переменная символьного типа, если они содержат только пробелы, считаются пустыми, например Сообщить(ПустоеЗначение(" "));
//
Напечатает 1
Равное нулю число пустым не является, например Сообщить(ПустоеЗначение(0));
//
Напечатает 0
3.3. СПИСОК ЗНАЧЕНИЙ 3.3.1. СТРУКТУРА СПИСКА ЗНАЧЕНИЙ Объекты типа СписокЗначений хранят таблицы с тремя столбцами (колонками), имеющими следующий смысл: •
значение; данные заносимые в этот столбец, могут быть любого, в том числе и агрегатного типа;
•
представление значения - описание значения, или его смысл, или комментарий, или второе значение; данные этого столбца должны быть символьного типа; если это не так, то представление значения автоматически преобразовывается в символьный тип;
•
пометка.
Второй столбец списка значений может не заполняться. При этом, правда, мы ли шаемся методов, управляющих списком по представлению значения, таких, как Полу чить и Установить. В последний столбец для каждого значения заносится число 1 или 0. В первом слу чае значение считается помеченным, во втором - нет. По умолчанию все заносимые в список значения не помечены.
Замечания: 1. В дальнейшем каждую строку хранимой в списке значений таблицы будем назы вать элементом списка. 2.
В качестве значения элемента списка значений может выступать другой список значений (пример см. в разд. 2.8.5.5).
3.3.2. ПРИМЕР СПИСКА ЗНАЧЕНИЙ Создадим для примера список значений, содержащий представленные в табл. 3.2. данные, отражающие некоторые сведения о подразделениях предприятия. Таблица 3.2 Состав списка значений Код подразделения (значение элемента списка) 099 100 111
Подразделение (представление элемента списка) Отдел кадров Бухгалтерия
001
Снабжение и сбыт Цех 1
002
Цех 2
011
Цех 11
Выполним для начала следующие действия: 1. Занесем исходные данные в массивы кодПодр и имяПодр, а затем переместим их, используя метод ДобавитьЗначение, в список значений. Код подразделения ис пользуем для определения столбца значение, а название подразделения - для опре деления столбца представление значения формируемого списка значений. Эти действия произведем в процедуре Выполнить обработки Проба. 2.
Для контроля выведем элементы списка в окно сообщений. Вывод осуществим в процедуре Контроль, в которой для определения числа элементов списка приме няется метод РазмерСписка, а для доступа к элементу списка - метод ПолучитьЗначение.
3.
Отсортируем (метод Сортировать) список значений по кодам подразделений и вновь выполним вывод результата.
4.
Вставим после 3-й позиции списка новый элемент (088, Цех 88), удалим предпослед ний элемент списка и изменим имя подразделения, код которого равен 111, на новое имя, предварительно проверив, есть ли в списке элемент с таким кодом, сдвинем изме ненный элемент на одну позицию вверх и выведем модифицированный список. Для выполнения намеченных действий используем методы ВставитьЗначение, УдалитьЗначение, НайтиЗначение, УстановитьЗначение и СдвинутьЗначение.
5.
Вновь отсортируем список, но теперь уже по именам подразделений (метод СортироватьПоПредставлению) и выполним вывод результата.
6.
Выведем, вызывая метод ВыбратьЗначение, диалог со списком имен подразделе ний и выберем в нем Цех 88. Возвращенные методом значения (код цеха и его но мер в списке значений) выведем в окно сообщений.
7.
Преобразуем, используя метод ВСтрокуСРазделителями, итоговый список в текст.
// Предварительное описание процедуры Контроль процедура Контроль(сЗнач, этап) далее процедура Выполнить() перем сЗнач, ин, поз; перем кодПодр[20], подр[20]; // Векторы для имен подразделений и их кодов перем код, нПодр; // Код подразделения и его номер перем числоПодр; // Число подразделений перем стр; // Строка, в которую выгружается список значений // Перед каждым запуском процедуры Выполнить будем очищать окно сообщений ОчиститьОкноСообщений(); [ числоПодр = 6; кодПодр[1] = "099"; подр[1] = "Отдел кадров"; кодПодр[2] = "100"; подр[2] = "Бухгалтерия"; кодПодр[3] = "111"; подр[3] = "Снабжение и сбыт"; кодПодр[4] = "001"; подр[4] = "Цех 1"; кодПодр[5] = "002"; подр[5] = "Цех 2"; кодПодр[6] = "011"; подр[6] = "Цех 11"; // сЗнач - переменная (объект) типа СписокЗначений сЗнач = СоздатьОбъект("СписокЗначений"); для ин = 1 по числоПодр цикл // Добавляем значение и его представление // (код подразделения и его название) в список значений сЗнач сЗнач.ДобавитьЗначение(кодПодр[ин], подр[ин]); конецЦикла; // для Контроль(сЗнач, "Контрольный вывод начального списка."); // Сортировка списка по возрастанию значений имен подразделений сЗнач.Сортировать(); Контроль(сЗнач, "Контрольный вывод отсортированного начального списка."); // Вставка элемента (кода подразделения и его названия) после 3-го элемента списка сЗнач.ВставитьЗначение(4, "088", "Цех 88"); // Удаляем предпоследний элемент списка. Его номер равен сЗнач.РазмерСписка() - 1 сЗнач.УдалитьЗначение(сЗнач.РазмерСписка() - 1); // Изменяем имя подразделения с кодом 111 на новое, // если элемент с таком кодом присутствует в списке поз = сЗнач.НайтиЗначение("111"); если поз > 0 тогда // Если элемент найден сЗнач.УстановитьЗначение(поз, "111", "Отдел снабжения и сбыта"); // Переместим измененный элемент на одну позицию вверх сЗнач.СдвинутьЗначение(-1, поз); иначе Предупреждение("Подразделение с кодом 111 не найдено."); конецЕсли;
Контроль(сЗнач, "Контрольный вывод измененного списка."); // Сортировка списка по возрастанию имен подразделений сЗнач.СортироватьПоПредставлению(); Контроль(сЗнач, "Контрольный вывод итогового отсортированного списка."); // Окно выбора подразделения по его имени см. на рис. 3.1 // Установим курсор на строку, соответствующую подразделению Цех 1 // Для этого в переменную код установим код первого цеха код = "001"; флаг = сЗнач.ВыбратьЗначение(код, "Выбор подразделения", нПодр); Сообщить(РазделительСтрок + "Выбрано подразделение с кодом " + код + ". нПодр = " + нПодр); стр = сЗнач.ВстрокуСРазделителями(); Сообщить(РазделительСтрок + "Значения элементов списка в виде строк с разделителем:" + стр); конецПроцедуры // Выполнить процедура Контроль(сЗнач, этап) // Выводит список в окно сообщений перем числоПодр; // Число подразделений перем имяПодр, кодПодр; // Имя подразделения и его код // Контрольный вывод списка сЗнач Сообщить(РазделительСтрок + этап); числоПодр = сЗнач.РазмерСписка(); // Метод, возвращающий размер списка для ин = 1 по числоПодр цикл // Метод ПолучитьЗначение вернет код подразделения с номером ин // и через параметр имяПодр - имя подразделения кодПодр = сЗнач.ПолучитьЗначение(ин, имяПодр); // Тип параметра процедуры Сообщить всегда будет символьным, поскольку // выражение, задающее значение параметра, начинается с пустой строки Сообщить("" + кодПодр + " - " + имяПодр); конецЦикла; // для конецПроцедуры // Контроль
Рис. 3.1. Диалог выбора значения списка по его представлению После последнего контрольного вывода в окно сообщений добавится следующий текст: Контрольный вывод итогового отсортированного списка. 099 - Отдел кадров 111 - Отдел снабжения и сбыта 001 - Цех 1 011 - Цех 11 002 - Цех 2 088 - Цех 88
Выбрано подразделение с кодом 088; нПодр = 6 Значения элементов списка в виде строк с разделителем: "099","111","001","011","002","088" Замечание. В списки значений часто переносятся данные об объектах агрегатного типа 1С, например при работе с разновидностью типа Справочник.Сотрудники в столбце зна чение можно разместить идентификаторы реквизитов справочника, а в столбце пред ставление значения - представления реквизитов. Код получения идентификаторов рекви зитов и их представлений см. в предшествующем разделе. Число создаваемых в программе списков значений произвольно. Конечно, они должны иметь разные имена (по крайней мере в одной и той же области видимости). Для указания списка, с которым используется тот или иной метод, имя метода предва ряется именем списка и разделяющей точкой, например сЗнач.РазмерСписка()
3.3.3. МЕТОДЫ СПИСКА ЗНАЧЕНИЙ Из примера видно, что методы списка значений, как это и полагается при работе с объектами подобного рода, позволяют добавлять, изменять, удалять, сортировать, находить, отображать и выбирать данные. Следует ожидать, что аналогичные методы мы обнаружим, работая, например, с таблицей значений или текстом. Полный список методов, применяемых со списком значений, приведен в табл. 3.3. Таблица 3.3 Методы списка значений Метод
Описание
сЗнач.ДобавитьЗначение(знач, [пред]);
Добавляет элемент со значением знач и представлением пред в конец списка
сЗнач.ВставитьЗначение (поз, знач, [пред], [кол]);
Вставляет в список кол элементов со значением знач и представлением пред начиная с позиции поз списка. Должно выполняться условие 1 <= поз <= сЗнач.РазмерСписка() + 1
разы = сЗнач.РазмерСписка();
Возвращает число элементов в списке
поз = сЗнач.НайтиЗначение(знач);
Возвращает номер позиции элемента списка, имеющего значение знач, или нуль, если такого элемента в списке нет. При наличии в списке нескольких элементов со значением знач возвращается номер первого элемента, содержащего искомое значение
знач = сЗнач.ПолучитьЗначение (поз, [пред]);
Возвращает значение элемента с номером поз и заносит в параметр пред, если он задан, представление этого элемента
сЗнач.УстановитьЗначение (поз, знач, [пред], [кол]);
Заменяет кол раз существующие значения и представления элементов списка соответственно на знач и пред начиная с позиции поз. Значение параметра кол, если он опущен, равно единице
Метод
Описание
знач = сЗнач.Получить(пред);
Возвращает значение элемента списка по его представлению пред или пустое значение, если представление пред в списке не найдено
сЗнач.Установить(пред), знач);
Находит первый элемент списка с заданным представлением пред и заменяет существующее значение элемента списка на знач. Если элемент не найден, метод добавляет в конец списка новый элемент со значением знач и представлением пред
сЗнач.УдалитьЗначение(поз, [кол]);
Удаляет кол элементов списка начиная с указанной позиции. Значение параметра кол, если он опущен, равно единице. Если кол > 0 и указывает за пределы списка, то удаляются все элементы списка начиная с позиции поз. Если кол < 0, метод игнорируется
сЗнач.УдалитьВсе( );
Удаляет все элементы списка. Эквивалентен вызову сЗнач.УдалитьЗначение(1, сЗнач.РазмерСписка());
сЗнач.Сортировать([напр], [поДате]);
Сортирует элементы списка по их значениям. Если напр = 0 или опущен, то сортировка осуществляется по возрастанию значений, в противном случае по убыванию. Если значения списка - это документы и по Дате = 1, то документы сортируются по дате, в противном случае - по значению
сЗнач.СортироватьПоПредставлению ([напр]);
Сортирует элементы списка по их представлениям. Если напр = 0 или опущен, то сортировка осуществляется по возрастанию значений, в противном случае - по убыванию
сЗнач.СдвинутьЗначение(кол, поз);
Перемещает элемент списка с позиции поз на кол позиций вниз, если кол > 0, или на кол позиций вверх, если кол < 0. Если параметр кол указывает на несуществующую позицию, то сдвиг выполняется либо в вершину списка, если кол < 0, либо в его конец, если кол > 0
флаг = сЗнач.Принадлежит(знач);
Возвращает единицу, если в списке есть элемент, значение которого равно знач, или нуль в противном случае
флаг = сЗнач.ВыбратьЗначение (знач, заг, [нПоз], [задержка], [способ]);
Вызывает, если способ опущен или не равен 1 или 2, диалог, имеющий заголовок заг и содержащий список представлений элементов списка значений. В диалоге (или окне со списком) курсор позиционирован на элементе со значением знач или на первом элементе, если значения знач в списке нет. После выбора представления и нажатия ОК возвращает 1, в параметр знач заносит значение выбранного элемента, а в параметр нПоз, если он задан, - его номер в списке. Если способ = 1, то окно для выбора отображается в виде меню, иначе, если способ = 2, окно отображается в виде небольшого списка. Смысл параметра задержка см. в табл. 2.3
Метод
Описание
Вызывает диалог (рис. 3.2), имеющий заголовок заг флаг = сЗнач.ОтметитьЗначения (знач, заг, [нПоз], [задержка]); и содержащий список представлений элементов списка значений, и предоставляет возможность пометить или снять пометку с элемента списка. В диалоге курсор позиционирован на элементе со значением знач или на первом элементе, если значения знач в списке нет. После выбора представления и нажатия ОК возвращает 1, в параметр знач заносит значение выбранного элемента, а в параметр нПоз, если он задан, его номер в списке. Смысл параметра задержка см. в табл. 2.3 отметка = сЗнач.Пометка(поз, [флаг]); Помечает, если флаг = 1, элемент списка значений, имеющий позицию поз, или, если флаг = 0, снимает отметку с данного элемента. Если флаг опущен, то отметка элемента не изменяется. Возвращает значение отметки элемента (0 или 1), которое он имел до вызова метода номерДо = сЗнач.ТекущаяСтрока ([номер]);
Возвращает номер элемента списка, на котором находится курсор в элементе диалога Список или Поле со списком, и устанавливает курсор, если задан параметр номер, на элемент списка, номер которого совпадает со значением параметра
стр = сЗнач.ВстрокуСРазделителями(); Преобразовывает значения списка в строку, содержащую заключенные в двойные кавычки значения списка, если они имеют нечисловой тип, или числа, если значения списка числового типа сЗнач.ИзСтрокиСРазделителями(стр);
Преобразовывает строку стр, содержащую разделенные запятыми последовательности символов или цифры в значения списка
сЗнач.Выгрузить(знач, [поз], [кол]);
Выгружает в список или таблицу значений знач кол элементов списка значений начиная с позиции поз этого списка. В результирующем списке (таблице) выгруженные элементы располагаются с первой позиции. При выгрузке в таблицу в ней добавляется новый столбец. Список значений, в который производится выгрузка, может быть создан функцией СоздатьОбъект, но должен быть пустым значением. Если знач не является списком или таблицей значений, то в результате выполнения процедуры Выгрузить, знач приобретет тип СписокЗначений
Рис. 3.2. Диалог после вызова сЗнач.ОтметитьЗначения (код, "Пометка элементов списка"); сЗнач - список из приведенной в разд. 3.3.2 процедуры Выполнить; перед вызовом диалога выполнено присваивание: код = "100"
Замечания: 1. Имя сЗнач списка значений, употребленное в табл. 3.3 перед названием методов, может быть произвольным. 2. Если метод может вызываться как функция, то он размещается в правой части оператора присваивания. Напомним, что любой метод может вызываться как про цедура. 3. В методах для списка значений ПолучитьЗначение, УстановитьЗначение, УдалитьЗначение и Пометка должно выполняться условие 1 < =поз < =сЗнач.РазмерСписка(). 4. Если в методах ВставитьЗначение, ПолучитьЗначение, УстановитьЗначение, УдалитьЗначение и Пометка задана неверно позиция поз, то выводится сообщение "Индекс не входит в границы списка значений", метод игнорируется, вычисления продолжаются. 5. Если в методах СдвинутьЗначение и Выгрузить задана неверно позиция поз, то возникает завершающая работу программы ошибка исполнения, сопровождавмая сообщением "Номер за пределами значения". 6. В документации по встроенному языку 1С указано, что метод СдвинутьЗначение перемещает значение списка на новую позицию. На самом деле на новую позицию перемещаются и значение, и представление, и пометка, то есть элемент списка. Аналогичные неточности присутствуют и при описании иных методов, например указывается, что метод УдалитьЗначение удаляет значения, в то время как он удаляет, если правильно заданы параметры, элементы списка. Пример. Строка с разделителями преобразовывается в список значений сЗнач, в котором затем определяются представления и осуществляется, начиная со второго элемента, выгрузка списка сЗнач в список сЗнач2. Полученный список выводится при ведений в разд. 3.3.2 процедурой Контроль. перем сЗнач2; // Не забываем о том, что присутствующие в символьной константе кавычки // должны повторяться дважды стр = """Цех 1"", ""НПК 20"", ""Отдел 48"", 24"; сЗнач.ИзСтрокиСРазделителями(стр); // Текст процедуры Контроль см. в разд. 3.3.2 Контроль(сЗнач, "Контрольный вывод значений полученного из строки списка.");
// Определяем представление списка сЗнач, равным номеру элемента, // которому это представление принадлежит. Значения элементов сохраняем для ин = 1 по сЗнач.РазмерСписка() цикл сЗнач.УстановитьЗначение(ин, сЗнач.ПолучитьЗначение(ин), ин); конецЦикла; // Переменная сЗнач2 объявлена оператором Перем. Без этого объявления ее // нельзя использовать в качестве фактического параметра процедуры Выгрузить сЗнач.Выгрузить(сЗнач2, 2); // Копирование осуществляем со второй позиции Контроль(сЗнач2, "Контрольный вывод значений списка сЗнач2."); Результат после второго вызова процедуры Контроль: Контрольный вывод значений списка сЗнач2. НПК 20 - 2 Отдел 4 8 - 3 24-4
3.3.4. СПИСОК ЗНАЧЕНИЙ КАК ЭЛЕМЕНТ ДИАЛОГА Представления элементов списка значений могут отображаться в двух элементах диалога: Список и Поле со списком, вставляемых в диалог в результате выбора на п анели Элементы диалога иконок Вернемся вновь к обработке Проба, загрузим ее в конфигураторе, откроем заклад ку Диалог и приведем его к виду, представленному на рис. 3.3, дав идентификаторам элементов Список и Поле со списком соответственно имена сЗнач2 и сЗнач3.
Рис. 3.3. Диалог с двумя элементами для представления списка значений Переменные диалога сЗнач2 и сЗнач3 имеют тип СписокЗначений, и их не нужно создавать в программе, вызывая функцию СоздатьОбъект. При вставке элемента сЗнач2 активизируем на закладке Дополнительно (рис. 3.4, а) флаг Список с пометками, а в поле Формула занесем текст Удаление( ). Для элемента сЗнач3 на закладке Дополнительно (рис. 3.4, б) зададим формулу Перенос().
Рис. 3.4. Дополнительные свойства элементов сЗнач2 и сЗначЗ Формулы Удаление( ) и Перенос( ) - это имена процедур модуля обработки. При необходимости в поле Формула (рис. 3.4, б) можно разместить имя процедуры (функ ции) глобального модуля системы.
Напишем теперь код, состоящий из процедур ПриОткрытии, Удаление, Перенося и Выполнить. Первая процедура формирует начальные значения списков сЗнач2 и сЗнач3; вторая удаляет помеченные элементы списка сЗнач2 после двойного удара мыши по любому элементу списка или нажатия на Enter в момент расположения курсора на одном из элементов списка; третья переносит выбранные в списке сЗнач3 элементы в список сЗнач2. Процедура Выполнить, связанная с кнопкой диалога Пуск, восстанавливает, обращаясь к процедуре ПриОткрытии, начальные значения списков диалога. процедура ПриОткрытии( ) // Инициализация списков сЗнач2 и сЗнач3 перем ин; // Начальный диалог приведен на рис. 3.5 перем кодПодр[20], подр[20]; // Векторы для имен подразделений и их кодов перем числоПодр; // Число подразделений ОчиститьОкноСообщений(); числоПодр = 6; кодПодр[1] = "099"; подр[1] = "Отдел кадров"; кодПодр[2] = "100"; подр[2] = "Бухгалтерия"; кодПодр[3] = "111"; подр[3] = "Снабжение и сбыт"; кодПодр[4] = "001"; подр[4] = "Цех 1"; кодПодр[5] = "002"; подр[5] = "Цех 2"; кодПодр[6] = "ОН"; подр[6] = "Цех И"; сЗнач2.УдалитьВсе(); // Операторы необходимы для повторных вызовов процедуры сЗначЗ.УдалитьВсе(); для ин = 1 по числоПодр цикл // Добавляем значение (код подразделения и его название) в список сЗнач2 сЗнач2.ДобавитьЗначение(кодПодр[ин], подр[ин]); конецЦикла; // для сЗнач2.Сортировать(); сЗнач2.Выгрузить(сЗначЗ); конецПроцедуры // ПриОткрытии процедура Удаление() //Удаляет помеченные элементы списка сЗнач2 перем поз, размСпис; // При удалении очередного элемента размер списка уменьшается на единицу // Этот факт отразим в переменной размСпис размСпис = сЗнач2.РазмерСписка(); поз = 1; пока поз <= размСпис цикл если сЗнач2.Пометка(поз) = 1 тогда сЗнач2.УдалитьЗначение(поз); размСпис = размСпис - 1; // Размер списка сократился на единицу иначе поз = поз + 1; // Наращиваем позицию, если нет удаления конецЕсли; конецЦикла; // пока конецПроцедуры // Удаление // Переносит выбранный элемент списка сЗнач3 в список сЗнач2 // Перенесенный элемент из сЗнач3 удаляется процедура Перенос() перем поз, код, имя; поз = сЗначЗ.ТекущаяСтрока(); // Выбранная строка списка сЗнач3
// Код и название выбранного в сЗнач3 подразделения код = сЗначЗ.ПолучитьЗначение(поз, имя); сЗначЗ.УдалитьЗначение(поз); // Удаляем выбранное подразделение из сЗнач3 // Добавляем (переносим) удаленное из сЗнач3 подразделение в сЗнач2 сЗнач2.ДобавитьЗначение(код, имя); сЗнач2.Сортировать( ); конецПроцедуры // Перенос // Восстанавливает первоначальные значения списков сЗнач2 и сЗнач3 процедура Выполнить() ПриОткрытии(); конецПроцедуры // Выполнить
Рис. 3.5. Диалог обработки Проба после исполнения процедуры ПриОткрытии Замечание. Список значений с пометками можно употреблять для создания интерНапример, в отчете 1С анализ фейсов, уподобляя каждый элемент списка флажку счета 20 используется список значений, представленный на рис. 3.6.
Рис. 3.6. Список значений с пометками как элемент интерфейса Однако если число элементов диалога типа Флажок фиксированно, то число флаж ков в списке значений может быть переменным.
3.4. ТАБЛИЦА ЗНАЧЕНИЙ 3.4.1. АТРИБУТЫ ТАБЛИЦЫ ЗНАЧЕНИЙ Таблица значений, в отличие от списка значений, может иметь произвольное чис ло столбцов (колонок). Объект типа ТаблицаЗначений создается функцией СоздатьОбъект и имеет атрибуты номер Строки и идентификаторы столбцов. Первый содер жит номер выбранной строки; идентификатор столбца - значение ячейки таблицы в выбранной строке. Например, если идентификатор первого столбца таблицы тЗнач имеет имя Код, то для изменения значения элемента этого столбца в пятой строке при меним следующий код: тЗнач.НомерСтроки = 5; // Переходим к строке 5 таблицы тЗнач // Новое значение кода в пятой строке первого столбца таблицы тЗнач тЗнач.Код = "037";
Если в результате присваивания значение атрибута номерСтроки стало меньше нуля, то изменится код в первой строке; если оно стало больше числа строк в таблице, то код изменится в последней строке таблицы. Если столбец при создании имени не получил, то в методах для таблицы значений ссылка на ячейку осуществляется по номеру столбца. Например, те же изменения, что и произведенные выше, обеспечит вызов // Новое значение кода в пятой строке первого столбца таблицы тЗнач тЗнач.УстановитьЗначение(5, 1, "037"); Такое обращение возможно и в случае, когда столбец имеет имя. Однако недопус тимо присваивание тЗнач.1 = "037";
//
- недопустимое имя
тЗнач.1
Поскольку имя тЗнач.1 является недопустимым, то ошибка возникнет уже на эта пе компиляции модуля. Пример. В процедуре Выполнить обработки Проба создается таблица значений тЗнач, содержащая приведенные в табл. 3.4 данные; доступ к ячейкам таблицы осу ществляется через имена идентификаторов столбцов; выбор строки производится в р езультате изменения значения атрибута номерСтроки. Таблица 3.4 •
Состав таблицы значений Код подразделения
перем тЗнач;
Число сотрудников
Подразделение
099
Отдел кадров
10
100
Бухгалтерия
15
111
Снабжение и сбыт
20
001
Цех 1
100
002
Цех 2
150
011
Цех 11
200
//
тЗнач - переменная модуля
процедура Выполнить() // Отображает таблицу значений // Перед каждым запуском процедуры Выполнить будем очищать окно сообщений ОчиститьОкноСообщений(); // Выведем номер текущей строки, используя атрибут номерСтроки Сообщить(тЗнач.НомерСтроки); // Напечатает 6 // Покажем таблицу значений, выделив в ней четвертую строку // Результат приведен на рис. 3.7 тЗнач.ВыбратьСтроку(4, "Первый просмотр таблицы значений"); тЗнач.НомерСтроки = 5; // Переходим к строке 5 таблицы тЗнач // Новое значение кода в пятой строке первого столбца таблицы тЗнач тЗнач.Код = "037"; тЗнач.ВыбратьСтроку(, "Второй просмотр таблицы значений"); конецПроцедуры / Выполнить
процедура Заполнить( ) // Добавляет в таблицу значений строки и заполняет перем ин, номСтроки; // их данными из векторов кодПодр, подр и колВо; // Векторы для имен подразделений, их кодов и численности перем кодПодр[20], подр[20], колВо[20]; // Число подразделений перем числоПодр; числоПодр = 6; подр[1] = "Отдел кадров"; колВо[1] = 10; кодПодр[1] = "099" подр[2] = "Бухгалтерия"; колВо[2] = 15; кодПодр[2] = "100"; подр[3] = "Снабжение и сбыт"; колВо[3] = 20; кодПодр[3] = "111"; подр[4] = "Цех 1"; колВо[4] = 100; кодПодр[4] = "001"; подр[5] = "Цех 2"; колВо[5] = 150; кодПодр[5] = "002"; подр[6] = "Цех 1 Г; " колВо[6] = 200; кодПодр[6] = "011"; для ин = 1 по числоПодр цикл // Добавляем новую строку тЗнач.НоваяСтрока(); // Определяем, используя идентификаторы столбцов, ячейки новой строки тЗнач.Код = кодПодр[ин]; тЗнач.Имя = подр[ин]; тЗнач.Количество = колВо[ин]; конецЦикла; // для конецПроцедуры // Заполнить процедура ПриОткрытии() // Формирует таблицу значений // Формируем столбцы таблицы значений тЗнач.НоваяКолонка("Код", "Строка"); тЗнач.НоваяКолонка("Имя", "Строка"); тЗнач.НоваяКолонка("Количество", "Число"); Заполнить(); // Заполняем таблицу значений данными конецПроцедуры // ПриОткрытии // Основная программа модуля обработки Проба состоит из одного оператора тЗнач = СоздатьОбъект("ТаблицаЗначений");
Рис. 3.7. Таблица значений после цикла Для процедуры Заполнить Замечания: 1.
Переменная тЗнач.НомерСтроки, если ее употребить в качестве первого парамет ра метода ВыбратьСтроку, например тЗнач.ВыбратьСтроку(тЗнач.НомерСтроки, "Второй просмотр таблицы значений"); будет передана методу по значению, то есть номер выбранной строки не будет уста новлен в параметр тЗнач.НомерСтроки.
2.
Таблица, приведенная на рис. 3.7, приобрела такой вид после появления диалоге и изменения в нем при помощи мыши ширины столбцов. Однако надлежащую используемую при отображении таблицы ширину столбца можно установить заранее, задав ее в методе НоваяКолонка, например, так: // Формируем столбцы таблицы значений. Опуская имена необязательных параметров, // сохраняем разделяющие их запятые тЗнач.НоваяКолонка("Код", "Строка",,,, 10); // 10 - ширина первого столбца тЗнач.НоваяКолонка("Имя", "Строка",,,, 20); тЗнач.НоваяКолонка("Количество", "Число",,,, 15);
3.4.2. РЕДАКТИРОВАНИЕ ТАБЛИЦЫ ЗНАЧЕНИЙ В ДИАЛОГЕ Создадим в обработке Проба диалог, содержащий таблицу значений, дав ей имя тЗнач (рис. 3.8), и свяжем с таблицей процедуру Изменить, задав ее имя на закладке Дополнительно в поле Формула, в котором имя процедуры указывается с с охранением круглых скобок - Изменить().
Рис. 3.8. Диалог для вывода списка подразделений в таблицу значений тЗнач Напишем код процедуры Изменить, позволяющий редактировать значение ячейки таблицы тЗнач, после двойного удара мышью по этой ячейке или после выбора ячей ки и нажатия на клавишу Enter. Таблицу тЗнач заполним данными из табл. 3.4. Алгоритм процедуры Изменить: 1. Определить, вызывая методы ТекущаяСтрока и ТекущаяКолонка, номер строки номСтроки и номер столбца номСтолбца, на пересечении которых находится ре дактируемая ячейка. 2.
Используя метод ПолучитьПараметрыКолонки, определить тип данных столбца, с номером номСтолбца.
3.
Вызвать диалог ввода данных найденного в п. 2 типа, используя функцию ВвестиЗначение, и задать в нем новое значение ячейки. При открытии диалога передать в него старое значение ячейки. 4. Записать, применив метод УстановитьЗначение, в редактируемую ячейку введен ное в п. 3 значение. Также в модуль обработки войдут создающая таблицу процедура ПриОткрытии и инициализирующая таблицу процедура Заполнить, которую мы заимствуем из разд. 3.4.1. В процедуре Выполнить мы будем обращаться к процедуре Заполнить с тем, чтобы восстанавливать первоначальный вид таблицы значений. процедура Изменить() перем номСтроки, номСтолбца; перем типДан, флаг; //
Тип редактируемых данных
перем значен; // Значение редактируемой ячейки номСтроки = тЗнач.ТекущаяСтрока(); тЗнач.ТекущаяКолонка(, номСтолбца); тЗнач.ПолучитьПараметрыКолонки(номСтолбца, типДан); значен = тЗнач.ПолучитьЗначение(номСтроки, номСтолбца); // Передаем диалогу старое значение ячейки флаг = ВвестиЗначение(значен, "Введите новое значение ячейки", типДан); если флаг = 1 тогда тЗнач.УстановитьЗначение(номСтроки, номСтолбца, значен); конецЕсли; конецПроцедуры // Изменить процедура Заполнить()
//
Инициализирует таблицу значений // Берется из разд. 3.4.1
конецПроцедуры // Заполнить процедура ПриОткрытии() // Формирует таблицу значений // Формируем столоцы таблицы значений. Опуская имена необязательных параметров, // сохраняем разделяющие их запятые тЗнач.НоваяКолонка("Код", "Строка",,,, 10); //10 - ширина первого столбца тЗнач.НоваяКолонка("Имя", "Строка",,,, 20); тЗнач.НоваяКолонка("Количество", "Число",,,, 15); Заполнить(); // Заполняем таблицу значений данными тЗнач.ТекущаяСтрока(З); // Позиционируем курсор на ячейку в третьей строке тЗнач.ТекущаяКолонка(2); // и втором столбце таблицы значений конецПроцедуры // ПриОткрытии процедура Выполнить() // Восстанавливает таблицу значений // Перед каждым запуском процедуры Выполнить будем очищать окно сообщений ОчиститьОкноСообщений(); тЗнач.УдалитьСтроки(); // Удаляем все строки таблицы значений тЗнач Заполнить(); // Заполняем ячейки таблицы значениями // Диалог формы обработки Проба после ее загрузки приведен на рис. 3.9 конецПроцедуры Диалог формы обработки Проба после ее загрузки приведен на рис. 3.9.
Рис. 3.9. Таблица значений после загрузки обработки Проба Замечание. Ясно, что похожий механизм мы могли бы применить и для редакти рования элементов списка значений, рассмотренного в разд. 3.3.2.
3.4.3. ТЕКУЩАЯ СТРОКА ТАБЛИЦЫ ЗНАЧЕНИЙ При работе с таблицей значений мы, обращаясь к идентификаторам столбцов, ре дактируем или читаем значения ячеек текущей строки. Так, если мы работаем с вышерассмотренной таблицей значений, то исполнение оператора т.Знач.Код = "092"; приведет к изменению значения столбца под именем Код в текущей строке таблицы. Номер текущей строки содержит атрибут номерСтроки. После создания таблицы номерСтроки = 0 в таблице просто нет строк. Однако такое же значение атрибут но мерСтроки получит и при наличии в таблице значений строк после выполнения мето да ВыбратьСтроки. В этом случае мы будем говорить: таблица значений позиционир ована до первой строки. Чтобы перейти на первую строку, достаточно вызвать метод ПолучитьСтроку, который, если не достигнута последняя строка, увеличивает номер текущей строки на единицу. Если же метод ПолучитьСтроку применяется, когда те кущей является последняя строка, то таблица позиционируется вслед за ней, в атрибут номерСтроки устанавливается значение 0. Таким образом, по этому атрибуту, если он равен нулю, нельзя узнать, где мы находимся до первой строки таблицы значений или вслед за последней строкой. Приведенные сведения, иллюстрируются процедурой В ывестиНомСтроки, выводящей в окно сообщений значение атрибута номерСтроки. В процедуре переменная тЗнач - это любая таблица значений, например созданная и заполненная в разд. 3.4.2. процедура ВывестиНомСтроки() // тЗнач - переменная модуля, поэтому доступна в процедуре ВывестиНомСтроки // Позиционируемся перед первой строкой таблицы значений тЗнач тЗнач.ВыбратьСтроки(); // Напечатает 0, так как таблица позиционирована до первой строки Сообщить(тЗнач.НомерСтроки); // Перебор строк таблицы значений начинается с ее первой строки пока тЗнач.ПолучитьСтроку() = 1 цикл Сообщить(тЗнач.НомерСтроки); конецЦикла // пока // Вновь напечатает 0, хотя таблица позиционирована вслед за последней строкой Сообщить(тЗнач.НомерСтроки); конецПроцедуры // ВывестиНомСтроки Атрибут номерСтроки является ненадежным для определения номера текущей строки и в более широком смысле. Так, присваивание тЗнач.номерСтроки = значение; изменит значение атрибута, но не изменит номера текущей строки. Кроме методов ВыбратьСтроки и ПолучитьСтроку, текущую строку изменяют ме тоды ПолучитьСтрокуПоНомеру, НоваяСтрока и Сортировать. Метод СдвинутьСтр оку меняет текущую строку, если сдвигается текущая строка. В противном случае те кущая строка сохраняется. Также текущая строка меняется при переносе курсора по строкам в элементе диалога Таблица значений.
• •
Текущая строка становится неопределенной, когда: методы КоличествоСтрок или Свернуть уменьшают число строк до величины, мень шей чем значение (до применения метода) атрибута номерСтроки; метод УдалитьСтроку удаляет строку, номер которой меньше, чем значение (до применения метода) атрибута номерСтроки или равен ему.
Текущая строка становится равной нулю после выполнения методов ВыбратьСтроки и Очистить.
3.4.4. МЕТОДЫ ТАБЛИЦЫ ЗНАЧЕНИЙ Методы приведем в трех таблицах. В первой (табл. 3.5) перечислим сами методы с указанием их назначения; во второй (табл. 3.6) - синтаксис вызова методов, а в тре тьей (табл. 3.6) - описание (в алфавитном порядке) их параметров. Таблица 3.5 Методы таблицы значений Метод
Что делает
КоличествоКолонок
Устанавливает/возвращает число столбцов в таблице значений
НоваяКолонка
Добавляет в таблицу значений вслед за последним столбцом новый столбец
ВставитьКолонку
Вставляет в таблицу значений столбец с заданным номером
УдалитьКолонку
Удаляет заданный столбец из таблицы значений
УстановитьПараметры Колонки
Изменяет параметры столбца
ПолучитьПараметры Колонки
Возвращает номер или идентификатор столбца, а через параметры метода - параметры столбца таблицы значений
КоличествоСтрок
Устанавливает и/или возвращает число строк в таблице значений
НоваяСтрока
Добавляет строку в таблицу значений
УдалитьСтроку
Удаляет строку из таблицы значений
УдалитьСтроки
Удаляет все строки из таблицы значений
ВыбратьСтроки
Позиционирует таблицу значений до первой строки и позволяет перебирать, применяя метод ПолучитьСтроку, строки таблицы значений начиная с ее первой строки
ПолучитьСтроку
Осуществляет переход на следующую строку таблицы значений. Может употребляться как с методом ВыбратьСтроки, так и самостоятельно, например после метода ПолучитьСтрокуПоНомеру (см. пример 2 после табл. 3.7)
ВыбратьСтроку
Открывает диалог, позволяющий выбрать строку в таблице значений. Значение атрибута номерСтроки после выбора строки в диалоге не меняется
ПолучитьСтрокуПо Номеру
Позиционирует таблицу значений на заданную параметром номСтроки строку, которая после выполнения метода становится текущей
СдвинутьСтроку
Перемещает текущую или заданную параметром номСтроки строку таблицы значений на заданное число строк
Метод
Что делает
УстановитьЗначение
Устанавливает значение в заданной ячейке таблицы значений
ПолучитьЗначение
Возвращает значение ячейки таблицы значений
НайтиЗначение
Находит ячейку таблицы значений, содержащую заданное значение. Если в таблице несколько одинаковых значений, то находится первая от начала таблицы ячейка с искомым значением
Сортировать
Сортирует таблицу значений. Правила сортировки задаются символьным выражение, содержащим идентификаторы и/или номера столбцов, например "КодПоставщика, +Материал, -3". Если перемещается текущая строка, то и соответствующим образом меняется значение атрибута номерСтроки
Очистить
Удаляет из таблицы все строки и столбцы
Итог
Суммирует значения заданного столбца
Заполнить
Устанавливает значения в заданных столбцах таблицы значений в пределах заданного диапазона строк
Свернуть
Заменяет одинаковые в заданных столбцах строки на одну, суммируя данные в указанных столбцах. Таблица, к которой применяется метод, может быть неотсортированной
Выгрузить
Копирует таблицу значений или ее часть в другую таблицу значений или в столбец значение списка значений. Копирование осуществляется по строкам, если данные переносятся в список значений. После применения метода Выгрузить таблица значений, в которую выгружаются данные,будет содержать только те столбцы и строки, которые задаются параметрами метода
Загрузить
Копирует целиком одну таблицу в другую. После применения метода Загрузить таблица-приемник будет содержать только те столбцы и строки, которые имеет таблица-источник
ВидимостьКолонки
Отображает/скрывает столбцы таблицы значений, а также изменяет позицию указанного столбца. Метод оказывает воздействие на элемент диалога Таблица значений (рис. 3.9) и диалог, отображаемый методом ВыбратьСтроку
ТекущаяСтрока .
Устанавливает/возвращает в элементе диалога Таблица значений строку, в которой отображается курсор. Значение атрибута номерСтроки метод не меняет
ТекущаяКолонка
Устанавливает/возвращает в элементе диалога Таблица значений столбец, в котором отображается курсор. Результат применения методов ТекущаяСтрока и ТекущаяКолонка приведен на рис. 3.9
Фиксировать
Делает недоступными в элементе диалога Таблица значений заданное число строк и столбцов
Выводить Пиктограммы
Выводит в заданном столбце элемента диалога Таблица значений вместо занесенных в него данных пиктограммы из ВМР-файла, подсоединенного к элементу диалога в закладке Картинка в окне Свойства таблицы. Порядок создания и употребления пиктограмм разбирается в примере 8 после табл. 3.7
Таблица 3.6
Синтаксис вызова методов таблицы значений Синтаксис вызова числоСтобцовДо = тЗнач.КоличествоКолонок ([числоСтобцов]);
Комментарий Число столбцов не меняется, если параметр числоСтобцов не задан
номНовСтолбца = тЗнач.НоваяКолонка Если тип не задан, то в добавляемом столбце ([иден], [тип], [длина], [точность], можно хранить любой тип [заг], [ширина], [формат], [выравнивание]); номНовСтолбца = тЗнач.ВставитьКолонку ([иден], [номНовСтолбца], [тип], [длина], [точность], [заг], [ширина], [формат], [выравнивание]);
Если идентификатор не задан, то обращение к столбцу выполняется по его номеру
тЗнач.УдалитьКолонку(иден | номСтолбца);
В качестве параметра передается либо идентификатор, либо номер столбца
тЗнач.УстановитьПараметрыКолонки (иден | номСтолбца, [тип], [длина], [точность], [заг], [ширина], [формат], [выравнивание]);
Значение параметра столбца сохраняется, если опущен соответствующий ему параметр метода
иден | номСтолбца = тЗнач.ПолучитьПараметрыКолонки (номСтолбца | иден, [тип], [длина], [точность],[заг], [ширина], [формат], [выравнивание]);
Возвращает идентификатор столбца, если первый параметр - это номер столбца, и номер столбца, если первый параметр - это идентификатор столбца. Все параметры, кроме первого, являются выходными
числоСтрокДо = тЗнач.КоличествоСтрок ([числоСтрок]);
Если числоСтрок > числоСтрокДо, то добавляемые в конец таблицы строки содержат пустые значения. Если числоСтрок < числоСтрокДо, то атрибут номерСтроки обнуляется. Число строк не меняется, если параметр метода опущен
номНовойСтроки = тЗнач.НоваяСтрока ([номНовойСтроки]);
Если параметр номНовойСтроки отсутствует или больше числа строк в таблице, то строка добавляется в конец таблицы значений
тЗнач.УдалитьСтроку([намСтроки]);
Если параметр номСтроки не задан, то удаляется текущая строка. Изменение текущей строки осуществляется, например, методом ПолучитьСтрокуПоНомеру (разд. 3.4.3)
тЗнач.УдалитьСтроки();
После выполнения значение атрибута номерСтроки равно нулю
тЗнач.ВыбратьСтроки();
Часто предшествует методу ПолучитьСтроку
Синтаксис вызова флаг = тЗнач.ПолучитьСтроку();
флаг = тЗнач.ВыбратьСтроку ([номВыбСтроки], [загДиалога], [задержка]);
тЗнач.ПолучитьСтрокуПоНомеру (номСтроки);
номНовойСтроки = тЗнач.СдвинутьСтроку (числоСтрок, [номСтроки]);
тЗнач.УстановитьЗначение(номСтроки, иден | номСтолбца, знач);
знач = тЗнач.ПолучитьЗначение {номСтроки, иден | номСтолбца);
флаг = тЗнач.НайтиЗначение(знач, номСтроки, иден | номСтолбца);
Комментарий Вернет 1, если удалось переместиться на следующую строку, или 0 - если выполняется попытка переместиться за пределы таблицы значений Возвращает: 1, если нажата кнопка ОК; 0, если нажата кнопка Отмена; -1, если закончилось время, заданное параметром задержка. Параметр номВыбСтроки - входной/выходной. При вызове метода курсор позиционируется на строке, номер которой равен номВыбСтроки, или на первой строке, если значение номВыбСтроки лежит вне диапазона [1, тЗнач.КоличествоСтрок();] Если номер строки номСтроки выходит за пределы диапазона [1, тЗнач.КоличествоСтрок();], то возникнет завершающая ошибка, сопровождаемая сообщением "Номер за пределами значения!" Если номСтроки не задан, то перемещается текущая строка. Если числоСтрок > 0, то строка перемещается вниз таблицы. Иначе, если числоСтрок < 0, - в верх таблицы. Строка станет последней (первой), если числоСтрок задает позицию за пределами таблицы значений Ячейка, в которой устанавливается значение, задается номером строки и идентификатором или номером столбца. Если эти параметры заданы с ошибкой, то возникнет завершающая вычисления ошибка Ячейка, значение которой возвращается методом, задается номером строки и идентификатором или номером столбца. Завершающая ошибка возникает в тех же случаях, что и для метода УстановитьЗначение Возвращает 1, если значение знач найдено, или 0 - в противном случае. Параметры номСтроки и иден | номСтолбца являются входными/выходными. В качестве результата эти параметры возвращают координаты ячейки, содержащей искомое значение. Если номСтроки (иден | номСтолбца) при вызове метода задает существующую строку (столбец), то поиск осуществляется только в этой строке (этом столбце). Если на входе номСтроки (иден | номСтолбца) отличен от нуля или пустого значения (иден не является пустой строкой), и выходит за границы таблицы (или иден задает несуществующий идентификатор столбца), то возникнет завершающая ошибка
Синтаксис вызова
Комментарий
тЗнач.Сортировать(столбцы, [поДате]);
Если значения таблицы - это документы и поДате = 1, то документы сортируются по дате, в противном случае - по значению. Значение по умолчанию - 0
тЗнач.Очистить();
После выполнения метода атрибут номерСтроки принимает нулевое значение Вернет, если параметр метода - это числовой столбец, сумму элементов столбца, заданного параметром иден или номСтолбца, или нуль в противном случае. Вызов метода выполнен в примере 3 после табл. 3.7
тЗнач.Заполнить(знач, [начСтрока], [конСтрока], [столбцы]);
Если хотя бы один из последних трех параметров метода задан с ошибкой, то возникнет завершающая ошибка
тЗнач.Свернуть(групСтолбцы, сумСтолбцы);
Столбцы, которых нет в символьных выражениях групСтолбцы и сумСтолбцы, будут отсутствовать в результирующей таблице значений (см. пример 4 после табл. 3.7)
тЗнач.Выгрузить(новОбъект, [начСтрока], [конСтрока], [столбцы]);
Если хотя бы один из последних трех параметров метода задан с ошибкой, то возникнет завершающая ошибка. Порядок употребления метода иллюстрирует приводимый ниже пример 5
тЗначПриемник.Загрузить(тЗначИсточник);
Данные переносятся из таблицы тЗначИсточник в таблицу тЗначПриемник
флаг = тЗнач.ВидимостьКолонки(столбцы, [видимость], [позиция]);
Если параметр столбцы содержит один столбец, то возвращает флаг видимости этого столбца до вызова метода; флаг равен единице, если столбец виден, или нулю - в противном случае. Если параметр столбцы задает несуществующий столбец или параметр позиция указывает на несуществующую позицию, то метод игнорируется
номСтрокиДо = тЗнач.ТекущаяСтрока ([номСтроки]);
Вернет номер строки, на которой был позиционирован курсор до применения метода, или нуль, если такой строки нет. Текущая строка не меняется, если параметр номСтроки не задан
иденДо|номСтолбцаДо = тЗнач.ТекущаяКолонка ([иден|номСтолбца], [номСтолбцаДо]);
Вернет идентификатор, а если он не задан, то номер столбца, на котором позиционирован курсор до применения метода, или нуль, если такого столбца нет. Текущий столбец не меняется, если опущен первый параметр
Комментарий
Синтаксис вызова тЗнач.Фиксировать ([колСтрок], [колСтолбцов]);
Если параметр колСтрок задан, то после применения метода в элементе диалога Таблица значений будут недоступны первые колСтрок таблицы. Если параметр отсутствует, то число недоступных строк не изменяется. Аналогично на столбцы элемента диалога Таблица значений действует параметр колСтолбцов. Вызов тЗнач.Фиксировать(0, 0); делает доступными все ячейки таблицы значений
тЗнач.ВыводитьПиктограммы (идеи | номСтолбца, [начНомПиктограммы ]);
Пиктограммы, имеющиеся в подсоединенном к элементу диалога Таблица значений ВМРфайле, - это последовательность растровых образов размера 16x15 видеопикселов. Обращение к образу осуществляется по его номеру в этой последовательности. Если ячейка А столбца иден | номСтолбца содержит число а, то в этой ячейке отобразится пиктограмма с номером начНомПиктограммы + а - 1. Если начНомПиктограммы + а - 1 меньше единицы или больше числа пиктограмм в ВМР-файле, то ячейка А останется незаполненной
Замечания: 1. Имя тЗнач таблицы значений, употребленное в табл. 3.6 перед названиями мето дов, может быть произвольным. 2. Параметры методов, если это не оговаривается особо, являются входными. 3. Как и во всех методах, процедурах или функциях, входные параметры являются выражениями соответствующих типов. Таблица 3.7 Описание параметров методов таблицы значений Параметр
Описание
видимость
Если равен нулю, то столбцы, перечисленные в параметре столбцы метода ВидимостьКолонки, будут скрыты, или видимы в противном случае
выравнивание
Если выравнивание = 1, то данные в столбце выравниваются по левой стороне, и по правой, если выравнивание = 2
групСтолбцы
Столбцы, строки которых объединяются в одну при наличии в столбцах с одинаковыми номерами совпадающих значений
длина
Число символов для представления числовых или символьных данных
заг загДиалога
Заголовок столбца Заголовок диалогового окна
знач
Значение ячейки таблицы значений
Описание
Параметр идеи колСтрок
Идентификатор столбца Число фиксируемых строк элемента диалога Таблица значений
колСтолбов
Число фиксируемых столбцов элемента диалога Таблица значений
конСтрока
Номер строки, в которой завершается заполнение или копирование таблицы значений (методы Заполнить или Выгрузить). Если параметр отсутствует, то заполнение (копирование) выполняется до последней строки включительно
начНомПиктограммы
Номер первого образа BMP-файла, отображаемого в ячейках заданного столбца элемента диалога Таблица значений
начСтрока
Номер строки, с которой начинается заполнение или копирование таблицы значений (методы Заполнить или Выгрузить). Если параметр отсутствует, то заполнение (копирование) осуществляется с первой строки
новОбъект
Имя таблицы или списка значений, задающее объект, в который метод Выгрузить копирует данные
номНовСтолбца
Номер нового столбца
номСтолбца
Номер столбца таблицы значений, на который распространяется действие метода
номСтолбцаДо
Номер текущего столбца таблицы значений до применения метода ТекущаяКолонка
номСтроки
Номер строки таблицы значений, на которую распространяется действие метода
по Дате
Флаг задания сортировки документов, содержащихся в таблице значений, по дате
позиция
Позиция, в которой отображается столбец таблицы, заданный параметром столбцы метода ВидимостьКолонки. Если параметр позиция отсутствует, то положение столбца не меняется
числоСтобцов
Новое число столбцов, устанавливаемое методом КоличествоКолонок
числоСтобцовДо
Старое число столбцов, возвращаемое методом КоличествоКолонок
числоСтрок
Число строк в таблице значений, устанавливаемое методом КоличествоСтрок
столбцы
Символьное выражение, содержащее разделенные запятыми идентификаторы или номера столбцов, по которым выполняется сортировка (метод Сортировать), или в которых изменяются значения ячеек (метод Заполнить), или из которых копируются данные (метод Выгрузить). В методе Сортировать перед идентификатором (номером) столбца может стоять знак + или -.' Если перед идентификатором (номером) столбца знака нет или стоит знак +, то сортировка выполняется по возрастанию, если расположен знак -, то по убыванию (см. пример 3 после настоящей таблицы). Если переченьСтолбцов содержит неверный идентификатор или номер столбца, то возникнет завершающая ошибка
Параметр сумСтолбцы тип точность формат ширина номВыбСтроки
Описание Столбцы, данные в которых суммируются при объединении строк по столбцам, заданным параметром групСтолбцы Тип, возможно агрегатных, данных, отображаемых в столбце Число знаков после десятичной точки в представлении числовых данных Формат представления данных. Форматирование осуществляется по правилам, изложенным в разд. 2.8.5 Число символов, отводимое под столбец при отображении таблицы значений в диалоге На входе задает номер строки таблицы значений, на которой позиционируется курсор, на выходе - номер выбранной в диалоге строки
Пример 1. В окно сообщений выводятся значения первого столбца, имеющего имя Код, во всех строках таблицы значений, заданной в табл. 3.4. Процедура Вывести мо жет быть вызвана из процедуры Выполнить из разд. 3.4.1. процедура Вывести() // тЗнач - переменная модуля, поэтому доступна в процедуре Вывести // Позиционируемся перед первой строкой таблицы значений тЗнач тЗнач.ВыбратьСтроки(); // Перебор строк таблицы значений начинается с ее первой строки пока тЗнач.ПолучитьСтроку() = 1 цикл Сообщить(тЗнач.Код); конецЦикла; // пока конецПроцедуры // Вывести Пример 2. В окно сообщений выводятся начиная с третьей строки значения первого столбца таблицы значений, заданной в табл. 3.4. процедура Вывести2() // тЗнач - переменная модуля, поэтому доступна в процедуре Вывести // Позиционируемся на второй строке таблицы значений тЗнач тЗнач.ПолучитьСтрокуПоНомеру(2); // Перебор строк таблицы значений начинается с ее третьей строки пока тЗнач.ПолучитьСтроку() = 1 цикл Сообщить(тЗнач.Код); конецЦикла; // пока конецПроцедуры // Вывести2 Пример 3. Сортировка таблицы значений по трем столбцам. Первоначально созда ется и заполняется данными таблица значений, имеющая столбцы КодПоставщика, Материал и Количество. Затем выполняется сортировка по строке "КодПоставщика, +Материал, -3", означающей, что прежде данные упорядочиваются по возрастанию названий поставщиков, затем в пределах одинаковых поставщиков данные упорядочи ваются (так же по возрастанию) по названию материалов и, наконец, в пределах оди накового поставщика и одинакового материала данные упорядочиваются по убыванию по количеству.
процедура Выполнить() // Пример сортировки таблицы значений тЗнач перем тЗнач; перем пост[20], мат[20], колВо[20], ост[20]; перем ин, числоЗап; // числоЗап - число строк в таблице значений ОчиститьОкноСообщений(); тЗнач = СоздатьОбъект("ТаблицаЗначений"); числоЗап = 9; // Содержание таблицы значений пост[1]=1; мат[1] = "Сталь 45"; колВо[1]=10; ост[1] = 5; пост[2] = 2; мат[2] = "Чугун"; колВо[2] = 20; ост[2] = 10; пост[3] = 1; мат[3] = "Сталь 45"; колВо[3] = 90; ост[3] = 40; пост[4] = 3; мат[4] = "Свинец"; колВо[4] = 55; ост[4] = 25; пост[5] = 1; мат[5] = "Сталь 45"; колВо[5] = 150; ост[5] = 65; пост[6] = 4; мат[6] = "Олово"; колВо[6] = 70; ост[6] = 30; пост[7] = 3; мат[7] = "Свинец"; колВо[7] = 100; ост[7] = 70; пост[8] = 4; мат[8] = "Олово"; колВо[8] = 180; ост[8] = 100; пост[9] = 1; мат[9] = "Олово"; колВо[9] = 200; ост[9] = 120; // Формируем столбцы таблицы значений. Опуская имена необязательных параметров, // сохраняем разделяющие их запятые тЗнач.НоваяКолонка("КодПоставщика", "Число",,,, 10); тЗнач.НоваяКолонка("Материал", "Строка",,,, 10); // 10 - ширина второго столбца тЗнач.НоваяКолонка("Количество", "Число",,,, 8); тЗнач.НоваяКолонка("Остаток", "Число",,,, 8); для ин = 1 по числоЗап цикл тЗнач.НоваяСтрока( ); // Добавляем новую строку // Определяем, используя атрибут идентификатор столбца, ячейки новой строки тЗнач.Материал = мат[ин]; тЗнач.КодПоставщика = пост[ин]; тЗнач.Количество = колВо[ин]; тЗнач.Остаток = ост[ин]; конецЦикла; // для // Покажем таблицу значений до сортировки тЗнач.ВыбратьСтроку(1, "До сортировки"); // Строка " 1 , +2, -Количество" обеспечит тот же результат сортировки, что и строка // "КодПоставщика, +Материал, -3" тЗнач.Сортировать("КодПоставщика, +Материал, -3"); тЗнач.ВыбратьСтроку(1, "После сортировки"); Сообщить(тЗнач.Итог("Количество")); // Напечатает 875 конецПроцедуры
Результат приведен на рис. 3.10.
Рис. 3.10. Таблица значений: а - до сортировки; б - после сортировки по правилу "КодПоставщика, +Материал, -3"
Пример 4. Если до метода Сортировать в процедуру Выполнить примера 3 доба вить код а) тЗнач.Свернуть("1", "3"); тЗнач.ВыбратьСтроку(1, "После сворачивания 4, а"); б) тЗнач.Свернуть("1, 2", "3"); тЗнач.ВыбратьСтроку(1, "После сворачивания 4, б"); в) тЗнач.Свернуть("1, 2", "3,4"); тЗнач.ВыбратьСтроку(1, "После сворачивания 4, в"); то получим приведенный на рис. 3.11 и 3.12 результат.
Рис. 3.11. Таблица значений: а- до сворачивания; б - после сворачивания 4, а
Рис. 3.12. Таблица значений: а - после сворачивания 4, б; б - после сворачивания 4, в Пример 5. Если до метода Сортировать в процедуру Выполнить примера 3 добавить код сЗнач = СоздатьОбъект("СписокЗначений"); тЗнач.Выгрузить(сЗнач, 1, 4, "1, 2, 3"); сЗнач.ВыбратьЗначение(1, "Данные перенесены из таблицы тЗнач"); то получим приведенный на рис. 3.13 результат.
Рис. 3.13. Таблица и список значений: а - фрагмент таблицы, из которой копируются данные; б - список значений после переноса в него данных Пример 6. Неоднократное применение метода ВидимостьКолонки приводит к ис кажению представляемой методом ВыбратьЗначение таблицы значений. Так, код тЗнач.ВыбратьСтроку(1, "Исходное представление таблицы значений"); тЗнач.ВидимостьКолонки("2", 0); тЗнач.ВидимостьКолонки("3", 1, 2); тЗнач.ВыбратьСтроку(1, "После двукратного употребления метода ВидимостьКолонки"); добавленный до метода Сортировать в процедуру Выполнить примера 3, приведет к тому, что вместо трех столбцов метод ВыбратьСтроку отобразит два столбца табли цы значений (рис. 3.14, б)
а б Рис. 3.14. Таблица значений: а - до скрытия столбцов; б - с неверным числом отображаемых столбцов Замечание. С элементом диалога Таблица значений метод ВидимостьКолонки ра ботает корректно. Пример 7. Отображение таблицы значений, приведенной на рис. 3.9, после приме нения метода Фиксировать. Недоступные ячейки выводятся на сером фоне (рис. 3.15). тЗнач.Фиксировать(2, 1);
Рис. 3.15. Элемент диалога Таблица значений, в котором недоступны две первые строки и первый столбец
Пример 8. Отображение пиктограмм в ячейках элемента диалога Таблица значений. Возьмем таблицу значений, созданную в разд. 3.4.2 и представленную на рис. 3.9 и 3.14, и добавим в нее на третью позицию столбец под именем Состояние, использо вав код тЗнач.НоваяКолонка("Состояние",,,,, 5);
//
5 - ширина столбца
Запретим, применив метод Фиксировать, редактирование столбцов Код и Имя и будем выводить взятые из рис. 3.16 и приведенные в табл. 3.8 пиктограммы.
Рис. 3.16. Последовательность пиктограмм, выводимая в столбце Состояние рассматриваемой таблицы значений Таблица 3.8 Пиктограммы ячеек элемента диалога Таблица значений
Возможный вариант состояния таблицы значений приведен на рис. 3.17.
Рис. 3.17. Таблица значений с пиктограммами из табл. 3.8 Для управления таблицей значений и ее отображения создадим форму, по образцу представленному на рис. 3.18.
Рис. 3.18. Проект формы, управляющей таблицей значений Свяжем, используя закладку Дополнительно, кнопку Запретить с процедурой Запрет, устанавливающей в ячейку текущей строки столбца Состояние число 2, если редактиро вать количество нельзя, или прежнее значение ячейки. Для пояснения назначения кнопки на закладке Описание в окне Свойства кнопки введем приведенный на рис. 3.19 текст и активизируем флажок Использовать описание, что обеспечит вывод описания в качестве подсказки, когда в процессе работы с формой мышка задержится на кнопке Запретить.
Рис. 3.19. Используем описание в качестве подсказки Кнопка Запретить оказывает воздействие только на непомеченные для удаления записи. Первые 3 пиктограммы для таблицы значений имеются на картинке (рис. 3.20) из библиотеки картинок, открываемой на закладке Картинка окна Свойства таблицы после выбора кнопки Изменить картинку.
Рис. 3.20. Библиотека картинок Сохраним выделенную на рис. 3.20 картинку в файл p.bmp, выбрав на рис. 3.20 Загрузим файл p.bmp в Paint (графический редактор BMP-файлов, запус иконку каемый, например, после выполнения в Windows последовательности действий Пуск Стандартные - Paint, дорисуем в четвертом прямоугольнике картинки пиктограмму 4 из табл. 3.8, приведя таким образом картинку к рис. 3.16. Добавим далее, находясь
в окне Библиотека картинок (рис. 3.20), при помощи иконки в библиотеку файл p.bmp и выберем новую картинку для таблицы, воздействовав на иконку . Теперь после выполнения метода тЗнач.ВыводитьПиктограммы("Состояние", 1); в ячейках столбца Состояние будут выводиться пиктограммы из табл. 3.8. Причем но мер выводимой пиктограммы будет равен значению, хранящемуся в ячейке. Замечание. Доступ к библиотеке картинок также осуществляется после открытия конфигурации и выбора пункта Библиотека картинок в колонке Действия. Поскольку библиотека входит в конфигурацию, система предложит при закрытии конфигурации сохранить внесенные в библиотеку изменения. Алгоритм функционирования формы: 1. Начальные действия: •
заполним таблицу значений тЗнач начальными данными, записав во все ячейки столб ца Состояние число 1;
•
зададим (метод ВыводитьПиктограммы) режим отображения пиктограмм для столбца Состояние (первоначально во всех его ячейках будет видна пиктограмма 1);
•
запретим (метод Фиксировать) редактирование столбцов Код и Имя таблицы значе ний;
•
создадим список значений сЗнач и присвоим всем его элементам единичные значения. В этом списке будем хранить значения ячеек столбца Состояние. Причем в столбце Значение будем размещать числа 1 или 2, интерпретируя их в соответствии с табл. 3.8, а в столбце Представление - строку Да, если можно редактировать количество, или строку Нет - в противном случае. Все это нам понадобится для правильной смены пиктограмм при работе с таблицей значений.
2.
При нажатии на кнопку Пуск процедура Выполнить восстанавливает исходные со стояния переменных тЗнач и сЗнач.
3. •
При нажатии на кнопку Запретить выполняется процедура 3 апрет, которая: если выставляется запрет на редактирование количества, меняет в текущей строке зна чение ячейки Состояние на 2, а в столбец Представление для соответствующего эле мента списка сЗнач заносит Нет (нельзя редактировать количество); если запрет снимается, меняет в текущей строке значение ячейки Состояние на содер жимое столбца Значение для соответствующего элемента списка сЗнач (устанавли вает 1 или 3), а в столбец Представление для этого элемента сЗнач заносит Да (можно редактировать количество).
•
4. • •
При двойном ударе мыши по ячейке таблицы значений из столбца Состояние выполняется процедура Изменить, которая: если строка помечается для удаления, меняет в текущей строке значение ячейки Со стояние на 4; если снимается пометка удаления, меняет в текущей строке значение ячейки Состоя ние на содержимое столбца Значение для соответствующего элемента списка сЗнач (устанавливает 1 или 2), если в столбце Представление для этого элемента стоит Да, или устанавливает 3, если там находится Нет.
перем сЗнач;
//
Переменная доступна во всех процедурах модуля
Процедура Запрет( ) перем номСтроки; номСтроки = тЗнач.ТекущаяСтрока(); если тЗнач.Состояние = 4 тогда // Стока помечена на удаление Предупреждение("Строка, помеченная на удаление, не обрабатывается."); возврат; // Выставляется запрет на редактирование количества иначеЕсли тЗнач.Состояние о 2 тогда сЗнач.УстановитьЗначение(номСтроки, тЗнач.Состояние, "Нет"); тЗнач.Состояние = 2; // Пиктограмма 2 из табл. 3.8 иначе // Снимается запрет на редактирование тЗнач.Состояние = сЗнач.ПолучитьЗначение(номСтроки); сЗнач.УстановитьЗначение(номСтроки,, "Да"); конецЕсли конецПроцедуры // Запрет процедура Изменить( ) перем номСтроки, номСтолбца; перем типДан, флаг; // Тип редактируемых данных перем значен; // Значение редактируемой ячейки перем пред; // Представление (Да или Нет) элемента списка сЗнач номСтроки = тЗнач.ТекущаяСтрока(); тЗнач.ТекущаяКолонка(, номСтолбца); если номСтолбца = 4 тогда // Если выбран столбец Количество если тЗнач.Состояние = 4 тогда // Строка помечена на удаление Предупреждение("Данные в помеченной на удаление строке не редактируются."); возврат; // Запрет на редактирование количества иначеЕсли тЗнач.Состояние = 2 тогда Предупреждение("Редактирование запрещено."); возврат; иначе // Редактирование возможно тЗнач.ПолучитьПараметрыКолонки(номСтолбца, типДан); значен = тЗнач.ПолучитьЗначение(номСтроки, номСтолбца); // Передаем диалогу старое значение ячейки флаг = ВвестиЗначение(значен, "Введите новое значение ячейки", типДан); если флаг = 1 тогда // Если новое количество отлично от старого если значен о тЗнач.Количество тогда тЗнач.УстановитьЗначение(номСтроки, номСтолбца, значен); тЗнач.Состояние = 3; // Пиктограмма 3 из табл. 3.8 // Храним данные о столбце Состояние в списке сЗнач сЗнач.УстановитьЗначение(номСтроки, 3, "Да"); конецЕсли; // значен <> тЗнач.Количество конецЕсли; // флаг = 1 конецЕсли; // тЗнач.Состояние = 4 иначе // Выбран столбец 3 (Состояние) // Есть пометка об удалении. Снимаем пометку - заносим в ячейку // данные из списка сЗнач если тЗнач.Состояние = 4 тогда значен = сЗнач.ПолучитьЗначение(номСтроки, пред);
если пред = "Да" тогда // Можно редактировать количество тЗнач. Состояние = значен; иначе // Количество редактировать нельзя тЗнач. Состояние = 2; конецЕсли // пред = "Да" иначе // Строка не помечена на удаление тЗнач.Состояние = 4; // Выполняем пометку столбца пиктограммой 4 конецЕсли // тЗнач.Сосотяние = 4 конецЕсли; // номСтолбца = 4 конецПроцедуры // Изменить процедура Заполните( ) // Добавляет в таблицу значений строки и заполняет перем ин, номСтроки; //их данными из векторов кодПодр, подр и колВо; // Векторы для имен подразделений, их кодов и численности перем кодПодр[20], подр[20], колВо[20]; // Число подразделений перем числоПодр; числоПодр = 6; колВо[1]=10 подр[1] = "Отдел кадров"; кодПодр[1] = "099"; колВо[2]=15 подр[2] = "Бухгалтерия"; кодПодр[2] = "100"; колВо[3] = 20 подр[3] = "Снабжение и сбыт"; кодПодр[3] = "111"; колВо[4]=100; подр[4] = "Цех 1"; кодПодр[4] = "001"; колВо[5]=150; подр[5] = "Цех 2"; кодПодр[5] = "002"; колВо[6] = 200; подр[6] = "Цех 11" ; кодПодр[6] = "011"; для ин = 1 по числоПодр цикл тЗнач.НоваяСтрока(); // Добавляем новую строку // Определяем, используя атрибут идентификатор столбца, ячейки новой строки тЗнач.Код = кодПодр[ин]; тЗнач.Имя = подр[ин]; тЗнач.Состояние = 1 ; // Будет отображаться пиктограмма 1 из табл. 3.8 тЗнач.Количество = колВо[ин]; // Первоначально разрешено редактировать количество в таблице значений сЗнач.ДобавитьЗначение( 1, "Да"); конецЦикла; // для конецПроцедуры // Заполнить Процедура ПриОткрытии( ) // Формирует таблицу значений // Формируем столбцы таблицы значений. Опуская имена необязательных параметров, // сохраняем разделяющие их запятые тЗнач.НоваяКолонка("Код", "Строка",,,, 10); //10 - ширина первого столбца тЗнач.НоваяКолонка("Имя", "Строка",,,, 20); тЗнач.НоваяКолонка("Состояние",,,,, 5); // 5 - ширина столбца тЗнач.НоваяКолонка("Количество", "Число",,,, 10); , Заполнить(); // Заполняем таблицу значений данными // Столбцы Код и Имя недоступны для редактирования тЗнач.Фиксировать(0,2); , // Выводим пиктограммы в столбце Состояние тЗнач.ВыводитьПиктограммы("Состояние", 1); конецПроцедуры // ПриОткрытии процедура Выполнить() // Восстанавливает таблицу и список значений // Перед каждым запуском процедуры Выполнить будем очищать окно сообщений ОчиститьОкноСообщений(); тЗнач.УдалитьСтроки(); // Удаляем все строки таблицы значений тЗнач сЗнач.УдалитьВсе(); // Очищаем список значений сЗнач
.
// Заполняем ячейки таблицы тЗнач и элементы списка сЗнач значениями Заполнить(); конецПроцедуры
// Оператор основной программы модуля сЗнач = СоздатьОбъект("СписокЗначений");
Диалог формы обработки Проба после некоторых манипуляций с таблицей и кнопкой Запретить показан на рис. 3.17. Замечание. В 1С нет предопределенной процедуры, работающей с элементами диало га Список и Таблица значений, позволяющей отслеживать перемещение по строкам этих объектов и дополнительно по столбцам в случае таблицы значений. Это в ряде случаев н егативно сказывается на качестве создаваемых пользовательских интерфейсов.
3.5. ВЫВОДЫ 1. Агрегатные типы данных предназначены для создания моделей функционирова ния предприятия и его подразделений; создание агрегатных типов данных, помимо встроенных в 1С, невозможно. 2. Агрегатные типы разделены на две группы - специального и общего назначения. Объекты первой группы описаны в конфигурации, второй - нет. 3. Список значений отображает таблицу из трех столбцов, содержащих значения, представления и отметки элементов списка. Столбцы списка значений имен не имеют; доступ к элементам списка осуществляется только в результате исполь зования методов списка. 4. Список значений с пометками удобен для создания интерфейсов. В таком списке каждый его элемент можно рассматривать как флажок. 5. Таблица значений может содержать произвольное число столбцов, с которыми можно связать идентификаторы. Значение ячейки таблицы можно менять, модифицируя зн ачение идентификатора соответствующего столбца, например в результате выполнения оператора присваивания или вызывая метод УстановитьЗначение. 6. Текущую строку таблицы значений могут изменить методы ВыбратьСтроки, ПолучитьСтроку, ПолучитьСтрокуПоНомеру, НоваяСтрока, Очистить, Сортировать, а также метод СдвинутьСтроку, когда сдвигается текущая строка. Кроме того, значе ние атрибута номерСтроки меняется при перемещении курсора по строкам в элементе диалога Таблица значений. 7. Методы КоличествоСтрок, УдалитьСтроку и Свернуть могут сделать неопреде ленным значение текущей строки таблицы значений. 8. Методы Свернуть, Выгрузить и Загрузить могут изменить число строк и столбцов таблицы значений. Причем два последних метода эти изменения осуществляют в таблице-приемнике данных. 9. С каждым полем диалога можно связать формулу, имя которой - это имя процеду ры, (функции) в модуле диалога или глобальном модуле. Запуск этой процедуры (функции) возможен как из модуля, так и в результате воздействия на соответст вующий элемент диалога. 10. Нет предопределенной процедуры, позволяющей отслеживать перемещение по строкам элементов диалога Список и Таблица значений и дополнительно по столбцам в случае таблицы значений.
4. ПЕРЕЧИСЛЕНИЯ 4.1. ПЕРЕЧИСЛЕНИЯ 1С Перечисление - это агрегатный тип данных, содержащий список некоторых значе ний, например перечисление ДаНет может принимать два значения - Да и Нет. Имею щиеся в конфигурации Заработная плата и кадры перечисления (их синонимы или идентификаторы, если синоним не задан) выведет следующий код: // Процедура вывода списка имеющихся в конфигурации перечислений процедура Выполнить() // Связана с кнопкой Пуск обработки проба перем ин, всегоПереч; // всегоПереч - число перечислений в конфигурации ОчиститьОкноСообщений(); всегоПереч = Метаданные.Перечисление(); для ин = 1 по всегоПереч цикл Сообщить(Метаданные.Перечисление(ин)); конецЦикла; // для конецПроцедуры // Выполнить Результат: ВидЗаписи ВидыПлатежа ВидыПовышКвалиф Годность ДаНет ДопСведИТС ИнтервалСведений Категории ПФР Образование ОснованияВЛ ОснованияИТС ОснованияУвольнения ОсобыеУсловияТруда ОтношениеКВоинскойОбязанности ПериодичностьСкидки Пол ПричинаОтсутствия РасходыНаАвторские РезультатыАттестации СемейноеПоложение СпособПеречислПоИспЛисту СтепениРодства Территориальные условия ТипБЛ ТипБолезни ТипНалоговойЛьготы
ТипПланаСчетов ТипПлатежа ТипРасчетаБольничного ТипРасчетаОтпуска ТипРасчетаПремии ТипСотрудника ТипФормы ТипыПособий ТипыУсловийТруда Упорядочить формыДоковПерсУчета формыОбучения ФормыТруда ХарактерДоплаты ХарактерСкидки Характеру держания ЦелиПеречисления ОснованияОплатыПоСреднему ВидСтрокиСреднегоЗаработка Характер работы ТипыЧасов БухгалтерскиеКонфигурации ПричинаПростоя
4.2. ПЕРЕЧИСЛЕНИЕ СКИДКИ 4.2.1. СОЗДАЕМ ПЕРЕЧИСЛЕНИЕ СКИДКИ Создадим свое перечисление, дав ему имя Скидки, содержащее размеры скидок в процентах на оплату за обучение, например на курсах повышения квалификации, и представим затем все методы, применяемые при работе с перечислениями. Вызовем конфигуратор, откроем конфигурацию на закладке Метаданные, войдем в раздел Перечисления и в меню, всплывающем после нажатия на правую кнопку мы ши, выберем пункт Новое Перечисление. В появившемся окне зададим приведенные на рис. 4.1 данные.
Рис. 4.1. Ввод перечисления Скидки
Каждое новое значение перечисления вводим после выбора кнопки Новый. В п оявляющемся окне определяем идентификатор и представление элемента перечисления (рис. 4.2).
Рис. 4.2. Идентификатор и представление элемента перечисления
При обработке данных в качестве значения элемента перечисления используется его] представление, если оно задано, или его идентификатор - в противном случае. Значение перечисления возвращается методом ЗначениеПоНомеру или ЗначениеПоИдентификатору. Также оно содержится в полном имени идентификатора элемента перечисления. Ко личество значений в перечислении вернет функция КоличествоЗначений. Пример 1. Выводятся представления (значения) и идентификаторы элементов перечисления Скидки. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ном, пер; ОчиститьОкноСообщений(); // Выводим заголовок Сообщить("Значение" + СимволТабуляции + "Идентификатор"); для ном = 1 по Перечисление.Скидки.КоличествоЗначений() цикл // Переменная пер имеет тип Перечисление пер = Перечисление.Скидки.ЗначениеПоНомеру(ном); // Пер.Идентификатор() вернет идентификатор очередного элемента перечисления Сообщить(Строка(пер) + Символ Табуляции + пер.Идентификатор()); конецЦикла; // для конецПроцедуры // Выполнить Результат: Значение 0 20 40 100
Идентификатор НетСкидки Скидка20Проц Скидка40Проц Скидка 100Проц
Пример 2. Значения элементов перечисления Скидки выводятся по известным именам их идентификаторов. процедура Выполнить() . , перем ин, пер, иден[10]; ОчиститьОкноСообщений();
//
Связана с кнопкой Пуск обработки Проба
// иден - массив имен идентификаторов элементов перечисления Скидки
иден[1] = "НетСкидки"; иден[2] = "Скидка20Проц"; иден[3] = "Скидка40Проц"; иден[4] = "Скидка100Проц"; для ин = 1 по Перечисление.Скидки.КоличествоЗначений() цикл // Значение (представление) перечисления (переменная пер) имеет тип Перечисление пер = Перечисление.Скидки.ЗначениеПоИдентификатору(иден[ин]); Сообщить(Строка(пер) + СимволТабуляции + иден[ин]); конецЦикла; // для конецПроцедуры // Выполнить Результат тот же, что и в примере 1. Пример 3. Значения элементов перечисления скидки выводятся по полным именам их идентификаторов. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба ОчиститьОкноСообщений(); Сообщить(Перечисление.Скидки.НетСкидки); Сообщить(Перечисление.Скидки.Скидка20Проц); Сообщить(Перечисление.Скидки.Скидка40Проц); Сообщить(Перечисление.Скидки.Скидка100Проц); конецПроцедуры // Выполнить Результат: 0 20 40 100 Если представление элемента перечисления не задано, то в качестве значения ис пользуется имя идентификатора элемента перечисления. Так, очистив представления для двух последних элементов перечисления, получим после запуска программы из примера 3 следующий результат: 0 20 Скидка40Проц Скидка100Проц Замечание. Вся информация о перечислениях сосредоточена в файле конфигура ции 1CV7.MD.
4.2.2. МЕТОДЫ ПЕРЕЧИСЛЕНИЙ Назовем их в табл. 4.1, приведя после нее ряд примеров, иллюстрирующих не оп робованные в предшествующем разделе методы. В качестве объекта, к которому при меняется метод, используем только что созданное перечисление Скидки.
Таблица 4.1 Методы перечислений Метод
Описание
кол = Перечисление.Скидки. КоличествоЗначений();
Возвращает число элементов в указанном перечислении. Первый элемент перечисления имеет номер 1, последний - кол
знач = Перечисление.Скидки. ЗначениеПоНомеру(ном);
Возвращает значение элемента перечисления, расположенного в перечислении под номером ном. Если номер ном больше числа элементов в пере числении, то возникнет завершающая ошибка
Возвращает значение элемента перечисления, знач = Перечисление.Скидки. ЗначениеПоИдентификатору имеющего идентификатор иден (идем); ном = Перечисление. Скидки. <иден>. ПорядковыйНомер();
Возвращает номер элемента перечисления, имеющего идентификатор иден
иден = пер.Идентификатор();
Возвращает идентификатор элемента перечисления, значение которого содержится в переменной пер
иденП= пер.Вид();
Возвращает идентификатор перечисления по значению его элемента, которое записано в переменную пер
предстП= пер.ПредставлениеВида(); Возвращает представление перечисления, то есть его синоним, а если он не задан, то идентификатор перечисления по значению его элемента, которое записано в переменную пер флаг - пер.Выбран();
Вернет 1, если выбран элемент перечисления, значение которого занесено в переменную пер, или 0 в противном случае
Замечание. В качестве значения элемента перечисления используется его пред ставление, если оно имеет непустое значение, или идентификатор элемента - в против ном случае. Примеры для методов ПорядковыйНомер, Вид, ПредставлениеВида и Выбран: процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем ном, пер; ОчиститьОкноСообщений(); // Следующий метод установит в ном число 3 ном = Перечисление.Скидки.Скидка40Проц.ПорядковыйНомер(); // Переменная пер имеет тип Перечисление пер = Перечисление.Скидки.ЗначениеПоНомеру(ном); Сообщить(ТипЗначенияСтр(пер)); // Напечатает Перечисление Сообщить(пер.Вид()); // Напечатает Скидки Сообщить(пер.ПредставлениеВида()); // Напечатает Льготы по оплате Сообщить(пер.Выбран( )); // Напечатает 1 конецПроцедуры // Выполнить
Те же результаты получим, употребив вызовы методов Вид, ПредставлениеВида и Выбран, в которых вместо переменной пер стоит полное имя элемента перечисления Скидка40Проц: Сообщить(Перечисление.Скидки.Скидка40Проц.Вид()); Сообщить(Перечисление.Скидки.Скидка40Проц.ПредставлениеВида()); Сообщить(Перечисление.Скидки.Скидка40Проц.Выбран());
4.3. ВВОД ЗНАЧЕНИЙ ПЕРЕЧИСЛЕНИЯ Задание перечислений выполняется в конфигурации. Для получения значения п еречисления в программе используются его методы, а также встроенная функция ВвестиПеречисление. Функция открывает диалог со списком значений перечисления (рис. 4.3).
Рис. 4.3. Диалог, открываемый функцией ВвестиПеречисление для перечисления Скидки Функция имеет следующий синтаксис: флаг = ВвестиПеречисление(значПер, заг, [задержка]); Параметр значПер является входным/выходным. На входе он может иметь тип Перечисление. В этом случае он задает одно из значений перечисления, на котором после открытия диалога расположится курсор. Например: значПер = Перечисление.Скидки.Скидка40Проц; На выходе он также будет иметь тип Перечисление, но содержать уже выбранное значение. Также параметр значПер может на входе иметь символьный тип. В этой ситуации перед вызовом функции переменной значПер нужно присвоить строку, содержащую идентификатор перечисления, значение которого мы хотим выбрать, например значПер = "Скидки";
//
Скидки - идентификатор перечисления
На выходе, однако, мы, как и в первом случае, получим значение типа Перечисл ение. Смысл параметров заг и задержка разъяснен в табл. 2.3 Функция вернет 1, если значение выбрано, вернет 0, если не выбрано, и -1, если время отображения диалога превысило величину, заданную параметром задержка. Пример. Отображается диалог выбора значения перечисления Скидки: процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем флаг, значПер; ОчиститьОкноСообщений(); значПер = Перечисление.Скидки.Скидка40Проц;
// или значПер = "Скидки"; флаг = ВвестиПеречисление(значПер, "Выберите значение и нажмите ОК"); // Если нажали OK, Enter или дважды ударили мышью по выбранному значению если флаг = 1 тогда // Сообщаем значение выбранного перечисления Сообщить(значПер); иначе Предупреждение("Ничего не выбрано."); конецЕсли; конецПроцедуры // Выполнить Замечание. Ввод значения значПер перечисления можно также выполнить, приме нив встроенную функцию ВвестиЗначение (разд. 2.5), например, так: перем флаг, значПер; флаг = ВвестиЗначение(значПер, "Выбор скидки", "Перечисление.Скидки");
4.4. ВЫВОДЫ 1. Перечисление состоит из элементов, каждый из которых имеет идентификатор и представление. Само же перечисление задается идентификатором; для представ ления перечисления используется его синоним или сам идентификатор перечисл ения, если синоним не задан. 2.
Значением элемента перечисления является либо его представление, либо его идентификатор, если представление не задано.
3.
Перечисления хранятся в файле 1CV7.MD, содержащем конфигурацию системы.
4.
Интерактивный ввод значения перечисления осуществляется встроенной функ цией ВвестиПеречисление.
5. С П Р А В О Ч Н И К И 5.1. УСТРОЙСТВО СПРАВОЧНИКА 1С Опыт, приобретенный при работе с объектами типа СписокЗначений, ТаблицаЗначений и Перечисление, позволяет перейти к более серьезным объектам агрегатного типа справочникам. Их назначение - хранить достаточно стабильные данные и предоставлять их для различных целей. Так, данные справочника Подразделения используются при учете материалов, основных средств, на эти данные ссылается справочник Сотрудники и т. д. То есть справочники должны органично встраиваться в общую модель предприятия или его подразделения. Справочник 1С - это совокупность главной таблицы (DBF-файла) и связанных с ней других таблиц. Кроме того, справочник может иметь владельца, в качестве кото рого выступает другой справочник системы. Так, владельцем справочника Образов ание является справочник Физические лица, который уже владельца не имеет. Список справочников, подчиненных заданному владельцу, выведет следующий код: //Вывод справочников, подчиненных встроенному в 1С справочнику Сотрудники процедура Выполнить() ОчиститьОкноСообщений(); для ин = 1 по Метаданные.Справочник( ) цикл если СокрМетаданные.Справочник(ин).Владелец)•= "Сотрудники" тогда Сообщить(Метаданные.Справочник(ин).Идентификатор); конецЕсли; конецЦикла; // для конецПроцедуры //Выполнить Результат: Аттестация ВнутренниеСовместители Депонент КадровыеДанные Квалификация НалоговыеЛьготы Образование Переподготовка ПриказыДлительногоДействия СоставСемьи ТрудоваяДеятельность Доходы7 Вычеты7 Итоги7 ИтогиПоГоду7 МатВыгода7 ДоходыПФР
В общем случае справочники 1С отображают иерархическую структуру данных за счет организации главной таблицы, представленной в табл. 5.1 (на примере нового справочника Сотрудники_2 для предприятия из двух цехов, причем первый цех имеет 3 подразделения). Таблица 5.1 Принцип организации главной таблицы справочника Сотрудники_2 Группа
СсылкаНа Группу
Код
1 2 3 4 5 6 7 8 9 10 11 12 13 15 16
0 0 1 1 1 2 2 2 3 3 4 4 5 5 5
1 2 11 12 13 201 202 203 111 112 121 122 131 132 133
Наименование
01 Цех 02 Цех 01/1 01/2 01/3 Абрамова Лариса Сергеевна Куприкова Людмила Сергеевна Митина Ольга Владимировна Агальцов Юрий Алексеевич Добрецов Борис Юрьевич Волосков Михаил Андреевич Кузьмина Раиса Николаевна Васильева Елена Ивановна Смирнова Нина Федоровна Хохлов Евгений Николаевич
Флаг Папки (1 или 2)
Другие поля
2 2 2 2 2 2 2 2 2 2
Замечание. В таблице данные упорядочены по выражению СсылкаНаГруппу + Наименование. Такая организация главной таблицы позволяет отобразить состав справочника Сотрудники_2 по группам в виде иерархического списка (рис. 5.1), в качестве которых выступают имена цехов и их подразделений.
Рис. 5.1. Вывод справочника Сотрудники_2 по группам Если отменить вывод справочника по группам, нажав на расположенную в окне , выполнить, обратившись к главному меню, сортиров вывода справочника иконку ку по коду (Действия - Сортировка - По коду), то получим приведенное на рис. 5.2 представление справочника, отличающееся от табл. 5.1 порядком следования данных.
Рис. 5.2. Вывод справочника Сотрудники_2 в виде неиерархического списка Замечание. Чтобы быстро найти в справочнике сотрудника, следует переместиться на поле Наименование и начать набирать на клавиатуре его фамилию.
5.2. АТРИБУТЫ И КОНТЕКСТ СПРАВОЧНИКА Данные, доступ к которым обеспечивает справочник, называются его атрибутами. В их число входят реквизиты справочника (соответствующие им поля главной и связанной с ней таблиц, хранящих данные справочника), а также атрибуты Родитель и Владелец. Совокупность атрибутов и методов справочника образует его контекст, который может быть передан в качестве фактического параметра процедуры или функции, на пример предопределенной процедуры ОбработкаПодбора. Два реквизита (атрибута), Код и Наименование, присутствуют в любом справочнике. В случае справочника Сотрудники_2 реквизит Код может быть использован в качестве та бельного номера сотрудника или кода группы, а Наименование - для задания фамилии, имени и отчества сотрудника или имени группы. Список иных реквизитов стандартного справочника Сотрудники и программу их получения см. в разд. 3.1. Свойства реквизита можно просмотреть или изменить в конфигурации системы (рис. 5.3).
Рис. 5.3. Свойства периодического реквизита Оклад: а - общие; б - дополнительные История периодического реквизита просматривается после позиционирования на поле со значением реквизита (например, в окне, приведенном на рис. 5.2) и выбора на панели инструментов формы списка справочника иконки или нажатия на кла вишу F5.
Замечание. Проще обеспечить достоверность данных, если запретить ручное изменение периодических реквизитов справочников (рис. 5.3, б), а взамен создавать I и проводить для этих целей соответствующие документы. Рассматриваемый нами справочник Сотрудники_2 имеет 3 уровня. Это означа ет, что элемент 3-го, самого низкого уровня входит в группу, которая находится на 2-м уровне справочника. Имя группы, в которую входит текущий (выбранный) элемент справочника, содержит атрибут Родитель. Понятно, что элементы 1-го j уровня родителей не имеют. Сообщить о номере уровня справочника позволяет метод Уровень. Применим метод Уровень и определим атрибут Родитель для всех элементов спра вочника (табл. 5.1), употребив следующий код: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ВыбратьЭлементы(); // Перемещаемся на начало справочника // Заголовок таблицы результатов Сообщить("Наименование" + СимволТабуляции +"Уровень" + СимволТабуляции + "Родитель"); // Метод ПолучитьЭлемент выбирает элемент справочника и перемещает справочник // на следующую позицию выборки или за ее пределы, если выборка исчерпана пока сСотр_2.ПолучитьЭлемент() > 0 цикл Сообщить(сСотр_2.Наименование + СимволТабуляции + сСотр_2.Уровень() + СимволТабуляции + сСотр_2.Родитель); конецЦикла // пока конецПроцедуры // Выполнить Результат: Наименование 01 Цех 01/1 Агальцов Юрий Алексеевич Добрецов Борис Юрьевич 01/2 Волосков Михаил Андреевич Кузьмина Раиса Николаевна 01/3 Васильева Елена Ивановна Смирнова Нина Федоровна Хохлов Евгений Николаевич 02 Цех
1 2 3 3 2 3 3 2 3 3 3 1
01 Цех 01/1 01/1 01 Цех 01/2 01/2 01 Цех 01/3 01/3 01/3
Абрамова Лариса Сергеевна Куприкова Людмила Сергеевна
2 2
02 Цех 02 Цех
Уровень
Родитель
Замечание. Следует помнить, что атрибут Родитель имеет тип Справочник. Поэтому присваивание сСотр_2.Родитель = "02 Цех"; не приведет к изменению родителя текущего элемента. На самом деле в правой части приведенного оператора присваивания должно стоять значение, возвращаемое мето дом ТекущийЭлемент. Механизм изменения родителя изложен в разд. 5.5.3.2. Атрибут Владелец, также имеющий тип Справочник, связывает некоторое под множество записей справочника, например Дети, с определенной записью справочн ика-владельца. Ряд реквизитов справочника целесообразно сделать периодическими, то есть хранить их историю. Таким, например, должен быть реквизит Оклад справочника Сотрудники_2, тогда мы сможем не только проследить динамику изменения опла ты сотрудника, но и учесть эти изменения при расчете заработной платы даже в том случае, если значение реквизита изменилось в пределах заданного расчет ного периода (то есть в начале месяца вы имели один оклад, а где-то в середине уже другой). Замечание. Значения периодического атрибута Оклад заносятся, наряду с констан тами и другими периодическими реквизитами, в файл 1SCONST.DBF (поэтому не сле дует изумляться внушительным размерам этого файла). Если периодический реквизит ссылается на объект агрегатного типа данных, например на Справочник.Должности, то в файл 1SCONST.DBF заносится ссылка на соответствующий элемент объекта; зна чение этого элемента употребляется в качестве значения реквизита.
5.3. СОЗДАНИЕ СПРАВОЧНИКА 1С 5.3.1. ЭТАП 1. ДОБАВЛЕНИЕ СПРАВОЧНИКА В КОНФИГУРАЦИЮ Сконструируем теперь, следуя нашим учебным целям, представленный в разд. 5.1 справочник Сотрудники_2. Войдем в конфигурацию, раскроем на закладке Метадан ные пункт Справочники и, нажав на правую кнопку мыши, выберем в появившемся меню пункт Новый справочник. В окне Конструктор справочника в поле Идентифика тор внесем текст Сотрудники_2 и проследуем далее. Пропустим окно, где предлагает ся создать новый вид субконто, и завершим первый этап создания справочника, указав на необходимость добавления команды вызов справочника в меню интерфейса Ученик (рис. 5.4).
Рис. 5.4. Завершаем первый этап создания справочника Сотрудники_2
5.3.2. ЭТАП 2. ФОРМИРОВАНИЕ РЕКВИЗИТОВ СПРАВОЧНИКА В конфигураторе в списке справочников появится имя объекта Сотрудники_2, ударив по которому дважды мышью мы перейдем ко второму этапу создания справочника - формированию его реквизитов (рис. 5.5).
Рис. 5.5. Задание реквизитов справочника Сотрудники_2 Два реквизита, Код и Наименование, заданы по умолчанию, поэтому нам следует лишь скорректировать их свойства, в частности установить длину поля Наименов ания равной 30. Не забудем задать число уровней справочника равным трем. Тип кода, ког да есть возможность, лучше устанавливать числовым. Зададим пока один дополнительный периодический реквизит - Оклад, выбрав кнопку Новый и определив его свойства в соответствии с рис. 5.3. Замечание. Главную таблицу созданного справочника Сотрудники_2 система раз местит в DBF-файле, возможно с именем SC4194.DBF. Второй этап завершен. На третьем этапе создадим формы для просмотра и редак тирования элементов и групп справочника Сотрудники_2.
5.3.3. ЭТАП 3. СОЗДАНИЕ ДИАЛОГОВ ДЛЯ РЕДАКТИРОВАНИЯ ЭЛЕМЕНТОВ И ГРУПП 5.3.3.1. ФОРМЫ ЭЛЕМЕНТА И ГРУППЫ По умолчанию для просмотра, ввода и редактирования данных справочника ис пользуется форма списка (рис. 5.1, 5.2), которая содержит все реквизиты справочника. Бесспорно, просматривать данные в такой форме удобно. Однако редактирование или добавление данных лучше осуществлять посредством специальных форм для элемен-
тов и групп справочника. Чтобы обеспечить вызов этих форм, предусмотрим в окне рис. 5.5 редактирование справочника как при помощи форм для элементов и групп, так и в форме списка (рис. 5.6).
Рис. 5.6. Задание способов редактирования справочника Сотрудники_2 Замечание. По умолчанию после выбора для свойства Редактировать пункта Обоими способами редактирование и добавление элементов и групп осуществляется в диалоге, то есть через их формы. В форме списка будет доступен только просмотр справочника. Для получения возможности редактирования в списке следует после запуска формы спи ска выполнить цепочку Действия - V Редактировать в диалоге либо употребить, например в предопределенной процедуре ПриОткрытии, при вызове формы списка справочника ее метод РедактироватьВДиалоге, установив в его первый параметр нуль. В то же время до бавим, что для надежного контроля данных лучше использовать единственный источник их поступления - диалог элемента (группы) и отказаться от более простого на первый взгляд способа их редактирования в списке формы. Чтобы создать Формы элементов и групп, нажмем, оставаясь в диалоге рис. 5.5, кнопку Форма элемента и воспользуемся окном (рис. 5.7) для размещения реквизитов справочника в диалоге формы элемента.
Рис. 5.7. Помощник размещения реквизитов справочника в диалоге формы элемента Приведем диалог к виду, представленному на рис. 5.8, сделав дополнительно поле Код недоступным для редактирования (это выполняется на закладке Общие окна свойств элемента Код).
Рис. 5.8. Диалог формы редактирования элемента справочника Сотрудники_2
Отказ от редактирования поля Код обусловлен тем, что код сотрудника генериру ется автоматически, поэтому придумывать и вводить код нет необходимости. Доста точно ограничиться его просмотром. Замечание. Если для атрибута Код установить символьный тип, то, используя встроенную процедуру ПрефиксАвтонумерации, или методы ПрефиксКода (для спра вочника), или ПрефиксНомера (для документа), можно задать префикс, который будет использоваться при автоматическом назначении кода. Процедура ПрефиксАвтонуме рации имеет следующий синтаксис: ПрефиксАвтонумерации(разновидность, префикс); где параметр разновидность - это символьное выражение, задающее разновидность типа справочника или документа, например "Справочник.Сотрудники" или "Документ.ИзменениеОклада". Если нужно применить префикс ко всем справочникам (до кументам), то для задания значения параметра разновидность используется строка "Справочник.*" ("Документ.*"). Параметр префикс также имеет символьный тип и за дает, как это следует из его названия, префикс. Пример: // Установим префикс номера для документа БольничныйЛист ПрефиксАвтонумерации("Документ.БольничныйЛист", "БЛ-"); Второй способ сделать атрибут Код доступным только для просмотра - это заме нить редактируемое поле на элемент диалога Текст (рис. 5.9).
Рис. 5.9. Диалог, в котором Код выводится с использованием элемента диалога Текст
Замена выполнена следующим образом. Во-первых, удаляется поле с иденти фикатором Код. Во-вторых, вставляется элемент диалога Текст с пустыми данны ми на закладке Общие, но с определенной на закладке Дополнительно формулой Код (рис. 5.10).
Рис. 5.10. Задание формулы для элемента диалога Текст
Такие свойства позволят выводить в диалоге формы элемента справочника разный текст, а точнее - значение реквизита Код, изменяющееся при переходе от одного эле мента к другому. Чтобы обеспечить единообразие с имеющимися элементами диалога, прижмем выводимый текст влево и отцентрируем его по вертикали. Замечание. В любой момент в диалог можно добавить отсутствующий в нем рек визит справочника, выбрав на панели инструментов Элементы диалога иконку Ввод и редактирование групп будем выполнять при помощи отдельного, пред ставленного на рис. 5.11 диалога, в котором также запретим редактирование поля Код.
Рис. 5.11. Диалог формы редактирования группы справочника Сотрудники_2 Этот диалог создается после нажатия кнопки Форма группы приведенного на рис. 5.5 окна. При этом флаг Одна форма для элемента и группы, расположенный в этом окне, должен быть отключен. 5.3.3.2.
ФОРМЫ СПИСКА
Формы списка изменяются после выбора на рис. 5.5 одноименной кнопки. Форм может быть несколько. Одну можно использовать как основную, открывае мую по умолчанию из меню по команде СправочникСотрудники_2.Открыть, вто рую - для использования методом ОткрытьПодбор. Так и поступим. При этом в первую форму списка мы вставим дерево групп (см. рис. 5.1), а во вторую - нет (рис. 5.12).
Рис. 5.12. Форма списка справочника Сотрудники_2 без дерева групп Кроме того, в первой форме мы сохраним запрет на выбор групп при вызове мето да Выбрать (этот запрет в методе Выбрать задан по умолчанию), а во второй, приме нив в предопределенной процедуре ПриОткрытии метод ВыборГруппы(1), этот запрет снимем. (Описание методов справочника см. в разд. 5.12.2.) Замечание. Заголовок столбца, например Наименование, можно при желании за менить на иной, например на ФИО (аббревиатура слов фамилия, имя, отчество). Для этого столбец выделяется и затем осуществляется вызов окна Свойства поля ввода (Наименование), в котором и реализуется намеченное мероприятие.
Создавая вторую форму, мы после выбора Формы списка - Редактировать должны нажать копку Новый и дать форме имя, например ФормаДляВыбора. Затем, выбирая имена форм и применяя кнопки Основная и Для выбора, разделить формы по назначе нию (рис. 5.13).
Рис. 5.13. Разделение форм списков по назначению Пока что ФормаДляВыбора не создана. Чтобы устранить этот недостаток, вы берем на рис. 5.13 кнопку Изменить и в появившемся окне (рис. 5.14) сразу наж мем ОК.
Рис. 5.14. Помощник создания формы списка Теперь после запуска программы из разд. 5.4 отображаемая в результате примене ния метода ОткрытьПодбор("Справочник.Сотрудники_2",, контПодбора); форма будет иметь приведенную на рис. 5.12 структуру. Тот же список откроется и при следующем вызове: ОткрытьПодбор("Справочник.Сотрудники_2", "ФормаДляВыбора", контПодбора); Впрочем, если метод ОткрытьПодбор вызвать так: // Указываем явно имя основной формы списка для метода ОткрытьПодбор ОткрытьПодбор("Справочник.Сотрудники_2", "ФормаСписка", контПодбора); то им будет использована основная, использующая дерево групп форма списка спра вочника Сотрудники_2.
5.3.4. Э Т А П 4 . Н А П И С А Н И Е М О Д У Л Е Й Ф О Р М 5.3.4.1.
ТРЕБОВАНИЯ К ПРОЦЕДУРАМ МОДУЛЕЙ ФОРМ СПРАВОЧНИКА
В модулях форм элемента и группы полезно написать процедуры, осуществляющие проверку вводимых данных и, по возможности, устраняющие имеющиеся в них ошибки. Например, в модуле формы элемента связанная через поле Формула с реквизитом Наиме нование функция или процедура может преобразовывать вводимые ФИО таким образом, что первые буквы ФИО будут прописными, а последующие - строчными. Это позволит нам, в частности, вводить ФИО на одном регистре, поскольку после ввода кузьмина раиса николаевна функция преобразует ФИО в надлежащий вид: Кузьмина Раиса Николаевна Чтобы исключить сокращения в ФИО, можно осуществить проверку на наличие в ФИО точки и при ее обнаружении запрещать запись введенных данных. Тогда не будет возможности ввести ФИО следующим образом: Кузьмина Р. Н. Дополнительно функция может удалять избыточные пробелы между составляю щими ФИО компонентами. Функция, связанная с реквизитом Наименование в форме для группы, должна пре пятствовать созданию одноименных групп в пределах одного родителя. Тогда в преде лах группы 01 Цех эта функция запретит создание двух одинаковых подразделений. Напишем для примера в модуле формы элемента справочника Сотрудники_2 функцию КонтрольФИО, связав ее, как и планировалось, с элементом диалога Наиме нование (это выполняется на закладке Дополнительные в окне задания свойств эле мента). В модуле формы группы разместим функцию Уникальность, также связанную с элементом диалога Наименование, но уже в форме группы. Вызовы этих функций необходимо разместить и в процедуре, связанной с кнопкой ОК. По умолчанию формула для этой кнопки такова: #3аписать?3акрыть Она обеспечивает действия, выполняемые методом для справочника Записать и методом для формы Закрыть, и вдобавок предлагает изменить даты для имеющихся в диалоге периодических реквизитов. Чтобы предварить запись элемента справочника вызовом функции КонтрольФИО (Уникальность), используем в обеих формах предопределенную процедуру ПриЗаписи, разместив в ней вызов функции КонтрольФИО (Уникальность). Это нужно, по скольку функция КонтрольФИО (Уникальность) вызывается: 1)
всегда, когда выбран реквизит диалога Наименование, значение реквизита измене но и нажата клавиша Enter;
2)
только единожды, когда покидается реквизит диалога Наименование в результате нажатия на любую (кроме Enter) меняющую положение курсора клавишу, напри мер на табуляцию, или в результате применения мыши. Если затем вновь выбрать поле Наименование, а вслед переместиться с него, не прибегая к Enter, на другой элемент диалога, связанная функция (формула) вызвана не будет.
А это означает, что если не вызывать КонтрольФИО (Уникальность) при нажатии на ОК, то мы можем осуществить запись неверного значения реквизита Наименова ние: с точками в случае формы элемента или дублирующего имя существующего под разделения в случае формы группы. Второй способ избежать ошибочных записей - это сделать кнопку ОК недоступной при неверных значениях наименования, употребив метод форма.ОКИден.Доступность(0);
//
ОКИден - идентификатор копки ОК
Но он хуже, поскольку в процессе редактирования записи пользователь может забыть, по какой причине кнопка ОК стала недоступной. Третий, пожалуй, наиболее удачный вариант - это наложить запрет на перемеще ние с поля диалога, содержащего ошибочные данные. Но он в 1С неосуществим (ме тод модуля формы Активизировать, который можно было бы использовать для реали зации предложенного запрета, нельзя употреблять в формулах элементов диалогов, кроме кнопок). Также в предопределенной процедуре ПриЗаписи разместим проверки, не позво ляющие сохранять пустые значения реквизитов. Запрет на запись ошибочных данных наложим, употребив в процедуре ПриЗаписи функцию СтатусВозврата(0);
//
Запрещает запись ошибочных данных
Выполняемая после нажатия кнопки Закрыть в формах элемента и группы команда предлагает, если были изменения данных, приведенный на рис. 5.15 диалог.
Рис. 5.15. Диалог после нажатия на кнопку Закрыть (при наличии изменений в данных) Однако нам удастся избежать ввода неверных данных, если нажать Да, поскольку в этом случае сработает предопределенная процедура ПриЗаписи, выполняющая поиск ошибок. Замечание. Команду #3акрыть, выполняемую при нажатии на кнопку Закрыть, можно заменить вызовом метода Форма.Закрыть(0). Равный нулю аргумент метода обеспечит за крытие формы без представленного на рис. 5.15 вопроса. Однако этот вопрос все равно будет появляться при попытке закрыть форму элемента или группы с измененными дан ными, нажимая на Esc, или используя крестик, или применяя Ctrl+F4. Также в обоих модулях осуществим в предопределенной процедуре ПриОткрытии вызовы методов ПанельИнструментов и Заголовок. Первый поможет нам изъять из диалога ненужные вспомогательные иконки, а второй - задать заголовок диалога.
5.3.4.2. ПРОЦЕДУРЫ МОДУЛЯ ФОРМЫ ЭЛЕМЕНТА Проверяют и корректируют ФИО по изложенным в предыдущем разделе правилам. процедура ПриОткрытии( ) ОчиститьОкноСообщений(); форма.ПанельИнструментов(0); // форма.Заголовок("Сотрудник предприятия"); конецПроцедуры // ПриОткрытии
Отключаем панель инструментов
функция КонтрольФИО(место = 1) далее процедура УбратьПробелы() далее процедура ВНРег() далее // Проверяет, все ли данные введены и вызывает функцию КонтрольФИО // и, если нет ошибок, осуществляется запись элемента справочника Сотрудники_2 процедура ПриЗаписи() если (ПустоеЗначение(Наименование) = 1) или (ПустоеЗначение(Оклад) = 1) или (ПустоеЗначение(Образование) = 1) тогда Предупреждение("В диалоге есть неопределенные реквизиты."); СтатусВозврата(0); // Не записываем данные // Функция КонтрольФИО вернет 0, если в ФИО есть точки иначеЕсли КонтрольФИО(2) = 0 тогда СтатусВозврата(0); // Запрещает запись ошибочных данных конецЕсли; конецПроцедуры // ПриЗаписи функция КонтрольФИО(место = 1) // Параметр место равен 1, если вызов осуществляется из формы, то есть // формула имеет вид КонтрольФИО() или КонтрольФИО(1), // и равен 2, если КонтрольФИО вызывается из процедуры ПриЗаписи // Если вызов осуществляется из процедуры ПриЗаписи, тогда нет необходимости вызывать // процедуры УбратьПробелы и ВНРег, корректирующие при необходимости // Наименование, поскольку все необходимые изменения Наименования функцией // КонтрольФИО уже произведены (см. выше порядок вызова КонтрольФИО в диалоге) если место = 1 тогда // Вызов осуществляется из диалога УбратьПробелы(); // Удаляет избыточные пробелы между словами // Процедура ВНРег преобразовывает ФИО так, что первые буквы каждого // составляющего ФИО слова - прописные, а остальные - строчные ВНРег(); конецЕсли; если Найти(Наименование,".") о 0 тогда Предупреждение("Точки в ФИО недопустимы."); возврат 0; иначе // Точек в имени нет возврат 1; конецЕсли; , конецФункции // КонтрольФИО процедура УбратьПробелы() перем длина, поз, к; // Удаляем возможные ведущие и завершающие пробелы Наименование = СокрЛП(Наименование); поз = Найти(Наименование, " "); // Ищем 2 подряд идущих пробела // Цикл продолжается, пока в строке есть хотя бы одна пара подряд идущих пробелов
пока поз > 0 цикл длина = стрДлина(Наименование); // Длина ФИО к = 1; // Число лишних пробелов пока (поз < длина) и (Сред(Наименование, поз + к, 1) = " ") цикл к = к + 1; конецЦикла; // пока (поз < длина). .. // Удаляем лишние пробелы и заменяем старое значение ФИО на новое Наименование = Лев(Наименование, поз) + Прав(Наименование, длина- поз - к + 1); поз = Найти(Наименование, " "); // Ищем 2 подряд идущих пробела конецЦикла; // пока поз > О конецПроцедуры // УбратьПробелы Замечание. В процедуре УбратьПробелы можно было бы избавиться от возмож ных точек в ФИО, добавив в ее начало код // Заменяем точки на пробелы ФИО = СтрЗаменить(ФИО,"."," "); однако тогда окажется возможным запись ФИО в виде Кузьмина Р Н // Процедура ВНРег преобразовывает ФИО так, что первые буквы каждого // составляющего ФИО слова - прописные, а остальные - строчные процедура ВНРег() перем к, длина; // Обработаем каждое слово ФИО встроенными функциями Врег и Нрег // Теперь, когда слова разделены одним пробелом и в конце ФИО нет пробелов, // выделение слов тривиально // Алгоритм: // 1. Преобразовываем все символы наименования в нижний регистр // 2. Находим первый символ каждого слова и преобразовываем его в верхний регистр. // Чтобы иметь возможность употреблять функцию Найти для поиска // очередного пробела (после него следует первая буква слова), будем // заменять найденный пробел на символ % (таких символов в ФИО быть не должно), // а после выполнения всех преобразований заменим символы % на пробелы длина = стрДлина(Наименование); //Длина ФИО если длина = 0 тогда возврат; конецЕсли; Наименование = Нрег(Наименование); // Первый шаг алгоритма // Подготовка к выполнению второго шага алгоритма // Первая буква ФИО - прописная Наименование = Врег(Лев(Наименование, 1)) + Прав(Наименование, длина- 1); // к - позиция пробела, или нуль, если пробел не найден к = Найти(Наимен(звание, " "); пока к > 0 цикл // Найден пробел. Формируем заново Наименование, в котором, // найденный пробел заменен на %, а первая буква очередного слова // преобразована в прописную Наименование = Лев(Наименование, к - 1) + "%" + Врег(Сред(Наименование, к + 1, 1)) + Прав(Наименование, длина - к - 1); к = Найти(Наименование, " "); конецЦикла; // пока к < длина
// Заменяем символы % на пробелы Наименование = СтрЗаменить(Наименование, "%", " "); конецПроцедуры // ВНрег
Замечание. При записи измененного оклада, который является периодическим ре квизитом справочника Сотрудники_2, метод Записать должен вызвать приведенный на рис. 5.16 диалог, позволяющий либо принять изменение оклада, либо нет.
Рис. 5.16. Изменение периодического реквизита Оклад
Однако такой диалог отображается не всегда, что не позволяет редактировать пе риодический реквизит Оклад. Чтобы гарантированно иметь возможность изменять ок лад сотрудников, в код модуля формы элемента, например в предопределенную про цедуру ПриОткрытии, нужно добавить оператор СохранениеПериодическихРеквизитов(к, "Оклад"); в котором к - это число, равное 2, 3, 4 или 5. 5.3.4.3.
ПРОЦЕДУРЫ МОДУЛЯ
ФОРМЫ ГРУППЫ
Проверяют на уникальность имя вновь вводимой или редактируемой группы. Для проверки (при вводе новой группы) мог бы подойти, например, следующий алгоритм: 1. Выполнить поиск группы по наименованию (функция НайтиПоНаименованию). 2.
Если группа найдена, то запретить ввод записи (нарушена уникальность).
Однако такой алгоритм непосредственно реализовать нельзя, поскольку он пред полагает изменение позиции справочника, что при работе с формой группы (элемента) недопустимо. Попытка изменения позиции справочника, скажем, в результате вызова НайтиПоНаименованию вызовет ошибку, сопровождаемую сообщением: "Объект не может быть перепозиционирован!" Чтобы это ограничение обойти, следует создать объект сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); с которым и употребить метод НайтиПоНаименованию: // Второй параметр метода - 0, следовательно, выполняем поиск во всем справочнике флаг = сСотр_2.НайтиПоНаименованию(Наименование, 0); где Наименование - имя вводимой или редактируемой группы. процедура ПриОткрытии( ) // Подготовительные действия ОчиститьОкноСообщений(); форма.ПанельИнструментов(0); // Отключаем панель инструментов форма.Заголовок("Подразделение предприятия"); конецПроцедуры // ПриОткрытии
функция Уникальность( ) далее // Вызывает функцию Уникальность, и если она возвращает 1, // то осуществляется запись группы справочника Сотрудники_2 процедура ПриЗаписи() если (ПустоеЗначение(Наименование) = 1) тогда Предупреждение("Выберите подразделение."); СтатусВозврата(0); // Не записываем данные // Если нарушена уникальность имен подразделений иначеЕсли Уникальность() = 0 тогда СтатусВозврата(0); // Запрещает запись ошибочных данных конецЕсли; конецПроцедуры // ПриЗаписи функция Уникальность() перем сСотр_2, флаг; // Выполняем поиск группы по ее имени // Вариант 1 // Группа не найдена, запись разрешена // Вариант 2 // Группа найдена и ее код равен коду редактируемой группы (при вводе новой группы // это невозможно, поскольку коды всех записей уникальны), запись разрешена // Вариант 3 // Группа найдена и ее код отличен от кода редактируемой // или новой группы, запись запрещена сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); флаг = сСотр_2.НайтиПоНаименованию(Наименование, 0); если флаг = 0 тогда // Вариант 1 возврат 1; иначеЕсли (флаг = 1) и (сСотр_2.код = Код) тогда // Вариант 2 возврат 1; иначеЕсли (флаг = 1) и (сСотр_2.код <> Код) тогда //Вариант 3 Предупреждение("Неуникальное имя подразделения."); возврат 0; конецЕсли; // То же можно записать компактнее: // если (флаг = 1) и (сСотр_2.код <> Код) тогда // Вариант 3 // Предупреждение("Неуникальное имя подразделения."); // возврат 0; // конецЕсли; // возврат 1; // Варианты 1 и 2 конецфункции // Уникальность Замечание. Такой простой способ контроля уникальности названия подразделения предприятия выбран потому, что в справочнике Сотрудники_2 не может быть записей с одинаковым кодом. При этом мы предполагаем, что названия подразделений и ФИО сотрудников совпадать не могут.
5.4. ПРОСМОТР И ВЫБОР ЭЛЕМЕНТОВ СПРАВОЧНИКА • • • •
Справочники можно открыть в диалоге следующими способами: обращаясь к меню системы; применяя метод справочника Выбрать; вызывая в модуле произвольной формы функции ОткрытьФорму или ОткрытьФормуМодально; употребляя метод ОткрытьПодбор, который вызывает форму списка, отображающую заданный справочник.
Дополнительно функция ОткрытьФорму и метод ОткрытьПодбор позволяют, ис пользуя контекст открытой формы (подбора), манипулировать открытой формой. В случае метода ОткрытьПодбор для этих целей применяется предопределенная про цедура ОбработкаПодбора. Функции ОткрытьФорму и ОткрытьФормуМодально позволяют открыть как формы элемента и группы справочника, так и формы списка. Методы Выбрать и ОткрытьПолбор используют только форму списка справочника, имя которой передается методами качестве второго параметра. Замечание. Размеры диалога модально открытой формы в отличие от формы, от крытой немодально, не могут быть изменены. Пример 1. Форма списка основная справочника Сотрудники_2 открывается мето дом Выбрать. Если элемент выбран, в окне сообщений печатается ФИО сотрудника. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); если сСотр_Выбрать("Выберите сотрудника", "ФормаСписка") = 1 тогда Сообщить(сСотр_2.Наименование); иначе // Метод Выбрать вернул 0 Сообщить("Сотрудник не выбран."); конецЕсли; конецПроцедуры // Выполнить Форма, открываемая методом Выбрать, приведена на рис. 5.17; для выбора можно употребить клавишу Enter, двойной удар мыши, а также иконку
Рис. 5.17. Форма, открываемая методом Выбрать
Замечание. Область интерактивного выбора элементов ограничивается методом ИспользоватьРодителя. Так, вызовы сСотр_2.ИспользоватьРодителя(род, 0); сСотр_2.Выбрать("Родителя сменить нельзя", "ФормаСписка"); в которых параметр род метода ИспользоватьРодителя имеет значение группы спра вочника, откроют диалог формы списка справочника, в котором можно выбрать эле мент только из группы, заданной методом ИспользоватьРодителя. 'Переместиться за пределы этой группы нельзя. Пример 2. Выбор сотрудников ограничивается подразделением 01/1 цеха 1. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, флаг, род; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); //11 - код подразделения 01/1 первого цеха флаг = сСотр_2.НайтиПоКоду( 11,0); // Поиск во всем справочнике если флаг = 0 тогда Предупреждение("Подразделение с кодом 11 не найдено."); возврат; конецЕсли; // Ограничиваем выбор сотрудников подразделение 01/1 // Будет после вызова метода Выбрать открыт приведенный на рис. 5.17 диалог род = сСотр_2.ТекущийЭлемент(); сСотр_2.ИспользоватьРодителя(род, 0); // Вместо двух последних операторов можно употребить один: // сСотр_2.ИспользоватьРодителя(сСотр_2, 0); если сСотр_2.Выбрать("Родителя сменить нельзя", "ФормаСписка") = 1 тогда Сообщить(сСотр_2.Наименование); иначе // Метод Выбрать вернул 0 Сообщить("Сотрудник не выбран."); конецЕсли; конецПроцедуры // Выполнить Пример 3. Форма списка основная справочника Сотрудники 2 открывается функцией ОткрытьФорму. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем контПодбора; // Результат работы процедуры см. на рис. 5. ОчиститьОкноСообщений(); ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка", контПодбора); // По умолчанию открывается форма списка основная, поэтому следующий вызов // откроет ту же форму, что и предыдущий // ОткрытьФорму("Справочник.Сотрудники_2", контПодбора); Сообщить(контПодбора.Наименование); //Напечатает 01 Цех конецПроцедуры // Выполнить Пример 4. Форма ввода нового сотрудника второго цеха в справочник Сотрудники_2 открывается функцией ОткрытьФорму. Вслед открывается форма ввода новой группы первого меха.
процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем сСотр_2, конт1, конт2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); если сСотр_2.НайтиПоКоду(2, 0) = 1 тогда // 2 - код второго цеха // Открываем форму ввода нового сотрудника второго цеха // Третий параметр функции задает родительскую группу ОткрытьФорму('Элемент.Сотрудники_2'', конт1, сСотр_2.ТекущийЭлемент( ), 0); Сообщить(конт1 .Код); // Напечатает код нового сотрудника иначе . Предупреждение("Цех 2 не найден."); возврат; конецЕсли; еслисСотр_2.НайтиПоКоду(1, 0) = 1 тогда //1 - код первого цеха // Открываем форму ввода новой группы первого цеха ОткрытьФорму("Элемент.Сотрудники_2". конт2, сСотр_2.ТекущийЭлемент(), 1); Сообщить(конт2.Код); // Напечатает код новой группы иначе Предупреждение("Цех 1 не найден."); возврат; конецЕсли; конецПроцедуры // Выполнить Замечание. В примере 4 форма группы будет находиться поверх формы элемента. Это определяется порядком их вызова. Пример 5. Открывается форма редактирования текущей записи справочника (группы или элементах. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, конт; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Ищем по коду во всем справочнике если сСотр_2.НайтиПоКоду(201, 0) = 1 тогда // При этом вызове параметр функции ОткрытьФорму имеет тип Справочник ОткрытьФорму(сСотр 2ТекущийЭлемент(), конт. 0); иначе Предупреждение("Элемент не найден."); возврат; конецЕсли; конецПроцедуры // Выполнить Пример 6. Форма списка справочника Сотрудники_2 открывается методом ОткрытьПодбор. Предопределенная процедура ОбработкаПодбора запускается при выборе (двойном ударе мыши или нажатии на Enter) ячейки третьего уровня и выво дит в окно сообщений атрибуты открытой формы. процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем контПодбора; // Результат работы процедуры см. на рис. 5.2 ОчиститьОкноСообщений(); // По умолчанию открывается форма списка для выбора ОткрытьПодбор("Справочник.Сотрудники_2",, контПодбора); конецПроцедуры // Выполнить
// Запускается при выборе (двойном ударе мыши или нажатии на Enter) ячейки третьего уровня I // Принимает в качестве параметра контекст справочника процедура ОбработкаПодбора(конт) Сообщить(конт.Наименование); Сообщить(конт.Родитель); // Дата для доступа к периодическому реквизиту Оклад конт.ИспользоватьДату(РабочаяДата()); Сообщить(конт.Оклад); конецПроцедуры // ОбработкаПодбора •
Возможный вариант сообщений, поступающих из процедуры ОбработкаПодбора: Добрецов Борис Юрьевич 01/1 2300
5.5. ДОБАВЛЕНИЕ, РЕДАКТИРОВАНИЕ И УДАЛЕНИЕ ЗАПИСЕЙ 5.5.1. УПРАВЛЕНИЕ ЗАПИСЯМИ СРЕДСТВАМИ ИНТЕРФЕЙСА Для решения обозначенных в заголовке раздела задач можно использовать воз можности, предоставляемые формой списка справочника, появляющейся, например, после вызова функции ОткрытьФорму. При этом можно пользоваться иконками инст рументальной панели инструментов (рис. 5.18), пунктами меню колонки Действия или меню, появляющемся при нажатии на правую кнопку мыши, установленной на записи справочника. .
Рис. 5.18. Панель инструментов формы списка справочника Замечание. Положение панели инструментов формы списка изменяется в резуль тате выполнения цепочки Сервис - Панели инструментов - Дополнительные - Инстру ментальные панели окон (рис. 5.19).
Рис. 5.19. Задание положения панели инструментов формы списка справочника
5.5.2. ПОРЯДОК УДАЛЕНИЯ ЗАПИСЕЙ В ФОРМЕ СПИСКА Рассмотрим подробнее процесс удаления записей. Для удаления выбранного элемента нажмите иконку Физически, однако, эле мент из справочника не удаляется, а лишь помечается как кандидат на удаление. При этом система позволяет выполнять его редактирование и выбор. Проставленная по метка об удалении снимается в результате повторного задействования той же иконки.
Если удаляется группа, то будут удалены (помечены для удаления) и все ее подчи ненные, входящие в нее элементы. Снять же пометку удаления можно как с группы, так и со всех ее элементов. При простановке пометки удаления система заносит в поле Ismark DBF-таблицы справочника символ * (табл. 5.2). Таблица 5.2 фрагмент DBF-таблицы справочника Сотрудники_2 с помеченными записями Id
Parentid
Code
F
7
М N
7 7 7
203 204
О
Descr
Isfolder
Ismark Verstamp
Митина Ольга Владимировна
2
6
205
Удаляемый Сергей Николаевич Изымаемый Сергей Петрович
1 1
206
Скрываемый Сергей Михайлович
2 * 2 * 2 *
1
Такая пометка является внутренней пометкой 1С и не позволяет осуществить фи зическое удаление записей DBF-файла, хранящих данные справочника. Чтобы получить возможность удалять помеченные записи физически, добавим в меню интерфейса Ученик системную колонку Сервис, разместив в ней пункт &Удаление помеченных объектов (рис. 5.20). (Амперсант перед прописной буквой У обеспечивает интерактивный выбор пункта меню по этой букве.)
Рис. 5.20. Модифицированное меню интерфейса Ученик Свойства нового пункта меню зададим в соответствии с рис. 5.21.
Рис. 5.21. Свойства пункта Удаление помеченных объектов После сохранения конфигурации и загрузки 1С:Предприятия введем в справочник Сотрудники_2 новые записи, пометив их для удаления (рис. 5.22).
Рис. 5.22. Три помеченные для удаление записи Воспользовавшись затем новым пунктом меню, мы обнаружим, что его действие распространяется на все такие объекты системы, как Документы и Справочники. На первом этапе решения задачи об удалении помеченных объектов система находит эти объекты и выводит их список (рис. 5.23).
Рис. 5.23. Список объектов для удаления В диалоге, содержащем этот список, следует нажать кнопку Контроль, чтобы оп ределить объекты, которые можно удалить из базы, не нарушая ее целостности (рис. 5.24).
Рис. 5.24. Результат контроля помеченных на удаление объектов Критерий выбора удаляемых объектов прост: объект можно удалить, если на него нет ссылок, и нет - в противном случае. Если есть необходимость максимально осво бодить систему от ненужных объектов, то следует поработать со ссылками, препятст вующими удалению. Подход к каждой ссылке индивидуален. Удаление можно произ вести программой, применив соответствующие методы для разных объектов.
Нажав кнопку Удалить, мы получим в окне сообщений текст об удаленных объек тах, содержащий в том числе и информацию о нашем справочнике: Удаленные записи:. Справочник: Сотрудники_2 02 Цех/Удаляемый Сергей Николаевич Справочник: Сотрудники_2 02 Цех/Изымаемый Сергей Петрович Справочник: Сотрудники_2 02 Цех/Скрываемый Сергей Михайлович Справочник.Сотрудники_2: удалено объектов 3 После таких действий мы сделаем очередной шаг к физическому удалению запи сей, а точнее, пометим их для удаления так, как это принято в DBF-файлах. Визуально такая пометка отображается в первом столбце таблицы (такой столбец имеется и в табл. 5.2) и видна при открытии DBF-файла, например, в FoxPro. Приведем одну строчку таблицы с помеченными таким образом записями: м
7
204
Удаляемый Сергей Николаевич
2 *
1
Замечание. Пометки удаления, проставляемые 1С в поле Ismark (см. табл. 5.2), в дальнейшем будем называть 1С-пометками удаления. Пометки удаления, которые 1С заносит в первый столбец DBF-файла, будем называть DBF-пометками удаления. Только теперь можно перейти к физическому удалению помеченных записей. Для это го нужно закрыть 1С:Предприятие, открыть конфигуратор и вызвать, выполнив цепочку Администрирование - Тестирование и исправление ИБ, приведенный на рис. 5.25 диалог, активизировав в нем флаг Упаковка таблиц информационной базы.
Рис. 5.25. Запуск физического удаления помеченных записей После выполнения упаковки произойдет физическое удаление помеченных запи сей. Восстановить удаленные таким образом записи нельзя. Замечание. Встроенные процедуры НайтиПомеченныеНаУдаление и НайтиСсылки, осуществляющие поиск помеченных для удаления записей и имеемых ими ссылок, а также процедура УдалитьОбъекты, удаляющая помеченные объекты (проставляю щая DBF-пометки удаления), рассмотрены в разд. 5.15.
5.5.3. УПРАВЛЕНИЕ ЗАПИСЯМИ ИЗ ПРОГРАММЫ Управлять записями справочников, то есть добавлять, удалять, редактировать, выбирать, искать и сортировать записи, можно, пользуясь имеющимися в 1С методами Для справочников. В этом разделе мы рассмотрим методы, имеющие отношение к до бавлению, редактированию и удалению записей.
5.5.3.1.
ДОБАВЛЕНИЕ
ЗАПИСЕЙ
Новый элемент и группа добавляются в справочник соответственно методами Новый и НоваяГруппа. Они применимы только с переменными типа Справочник, определенными функцией СоздатьОбъект. Для записи нового элемента употребляется метод Записать. Пример. В справочнике Сотрудники_2 создается группа 03 Цех, в которую заносится информация о сотруднике этого цеха. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, кодЦеха; ОчиститьОкноСообщений(); кодЦеха = 3; // Определяем переменную сСотр_2 как объект типа Справочник с разновидностью // типа Справочник.Сотрудники_2 сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); если сСотр_2.НайтиПоКоду(кодЦеха, 0) = 1 тогда Предупреждение(" Цех с кодом " + кодЦеха + " уже есть."); возврат; конецЕсли; сСотр_2.НоваяГруппа(); // Создаем новую группу сСотр_2.Код = кодЦеха; // и определяем ее реквизиты сСотр_2.Наименование = "0" + Строка(кодЦеха) + " Цех"; сСотр_2.3аписать(); // Записываем новую группу // Текущим элементом является только что созданная группа 03 Цех // Чтобы занести сотрудника в эту группу, необходимо применить // метод ИспользоватьРодителя. Его параметром должно быть значение группы, // имеющее тип Справочник, возвращаемое, например, методом ТекущийЭлемент // или задаваемое именем объекта-справочника сСотр_2.ИспользоватьРодителя(сСотр_2); //или // сСотр_2.ИспользоватьРодителя(сСотр_2.ТекущийЭлемент()); // Записываем периодический реквизит Оклад на рабочую дату // Этот оператор должен быть размещен до вызова метода Новый сСотр_2.ИспользоватьДату(РабочаяДата()); сСотр_2.Новый(); // Создаем новый элемент // Атрибут Код, если его не определить, будет вычислен автоматически // Мы же определим код явно, равным, например, 301 сСотр_2.Код = Число(Строка(кодЦеха * 100)) + "1"; // Определяем иные реквизиты нового элемента сСотр_2.Наименование = "Безверхний Игорь Петрович"; сСотр_2.0клад = 3100; сСотр_2.3аписать(); // Записываем новый элемент // Просмотр результата ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка"); конецПроцедуры Результат приведен на рис. 5.26.
Рис. 5.26. Новое подразделение и новый сотрудник
5.5.3.2.
ПЕРЕНОС ЗАПИСИ В ДРУГУЮ ГРУППУ
Если в приведенной в предшествующем разделе процедуре не вызывать метод ИспользоватьРодителя, то новый сотрудник попадет на верхний уровень (рис. 5.27). Что бы переместить сотрудника в третий цех, следует выделить и сотрудника и группу, обозначающую цех, так, как это показано на рис. 5.27, и воспользоваться иконкой или соответствующим пунктом меню в колонке Действия, или сочетанием клавиш Ctrl+F5.
Рис. 5.27. Перевод нового сотрудника в третий цех Программно перевод осуществляется в результате выполнения следующего кода: процедура Выполнить() //Связана с кнопкой Пуск обработки Проба перем сСотр_2, гр; // Переносит сотрудника в указанную группу 0,чиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Находим группу, в которую переносится сотрудник если сСотр_2.НайтиПоКоду(3, 0) = 1 тогда // Запоминаем ссылку на найденную группу гр = сСотр_2.ТекущийЭлемент(); // Находим сотрудника если сСотр_2.НайтиПоКоду(301, 0) = 1 тогда если сСотр_2.Родитель = гр тогда Предупреждение(сСотр_2.Наименование + " уже работает в " + гр); возврат; конецЕсли; // Меняем для нового сотрудника значение атрибута Родитель, // присваивая ему значение переменной гр, имеющей тип Справочник сСотр_2.Родитель = гр; сСотр_2.3аписать(); // Записываем измененный элемент Предупреждение(сСотр_2.Наименование + " переведен в " + гр); // Просмотр результата ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка"); иначе Предупреждение("Сотрудник не найден."); конецЕсли; иначе Предупреждение("Группа не найдена."); конецЕсли; конецПроцедуры // Выполнить
Замечание. Поскольку в каждой группе код ее подчиненного элемента связан с кодом группы, то при переносе элемента из одной группы в другую необходимо соответствующим образом изменять код элемента. Алгоритм вычисления нового кода приведен в разд. 5.8.2.2. При интерактивном перемещении элемента из одной группы в другую его код следует модифицировать в предопределенной процедуре ПриПереносеЭлементаВДругуюГруппу модуля формы списка справочника. Поря док ее употребления рассмотрен в разд. 5.12.6. 5.5.3.3.
РЕДАКТИРОВАНИЕ
ЗАПИСЕЙ
Редактированию записи предшествует ее поиск, который может быть выполнен одним из четырех следующих методов: НайтиЭлемент, НайтиПоКоду, НайтиПоНаименованию и НайтиПоРеквизиту. Изменение непериодического реквизита осуществляется либо в результате при сваивания ему нового значения, либо методом УстановитьАтрибут. Изменение пре фикса кода осуществляется методом УстановитьНовыйКод; с числовым кодом приме нять эту функцию смысла не имеет. Периодический реквизит также можно изменить простым присваиванием или ме тодом УстановитьАтрибут. Однако предварительно до поиска элемента методом ИспользоватьДату нужно установить дату записи такого реквизита. Если в коде метод ИспользоватьДату ни разу для справочника не применялся, то для изменения значения периодического реквизита можно использовать метод Установить. Пример. Изменяется фамилия сотрудника, код которого равен 203, и его оклад. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, новФИО; ОчиститьОкноСообщений(); новФИО = "Костина Ольга Владимировна"; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Записываем периодический реквизит Оклад на рабочую дату сСотр_2.ИспользоватьДату(РабочаяДата()); // Ищем во всем справочнике если сСотр_2.НайтиПоКоду(209, 0) = 1 тогда если СокрЛП(сСотр_2.Наименование) = новФИО тогда Предупреждение("Старая и новая фамилии совпадают."); возврат;. конецЕсли; сСотр_2.Наименование = новФИО; сСотр_2.0клад = 2900; // Новый оклад сСотр_2.3аписать(); // Записываем изменения иначе Предупреждение("Сотрудник не найден."); конецЕсли; // Просмотр результата ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка"); конецПроцедуры // Выполнить Тот же результат получим, заменив присваивания на метод УстановитьАтрибут: сСотр_2.УстановитьАтрибут("Наименование", "Костина Ольга Владимировна"); сСотр_2.УстановитьАтрибут("Оклад", 2900);
Также вместо методов ИспользоватьДату и УстановитьАтрибут можно для перио дического реквизита Оклад применить вызов сСотр_2.0клад.Установить(РабочаяДата(), 2900); Замечание. Если не задана дата периодического реквизита, то возникнет ошибка, сопровождаемая сообщением Не определена дата! Элемент не может быть записан! 5.5.3.4.
УДАЛЕНИЕ ЗАПИСЕЙ
Запись можно снабдить пометкой для удаления, употребляемой в 1С или в DBFфайлах, применив метод Удалить соответственно с аргументом 0 или 1. Снять пометку 1С позволяет метод СнятьПометкуУдаления. Метод-функция ПометкаУдаления вер нет 1, если элемент имеет 1С-пометку удаления, или 0 -в противном случае. Пример. Помечается для удаления в смысле 1С группа 01 /1 и все ее подчиненные элементы. Затем снимается отметка об удалении с элемента этой группы, имеющего код 112. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Ищем во всем справочнике если сСотр_2.НайтиПоНаименованию("01 / 1 " , 0) = 1 тогда // Если выбрана кнопка "Да" если Вопрос("Пометить для удаления группу", 4) = 6 тогда сСотр_2.Удалить(0); // Помечаем всю группу // Ищем во всем справочнике если сСотр_2.НайтиПоКоду(112, 0) = 1 тогда если сСотр_2.ПометкаУдаления() = 1 тогда сСотр_2.СнятьПометкуУдаления(); конецЕсли; иначе Предупреждение("Сотрудник не найден."); конецЕсли; конецЕсли; иначе Предупреждение("Подразделение не найдено."); конецЕсли; // Просмотр результата ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка"); конецПроцедуры // Выполнить
5.6. ПОЗИЦИЯ СПРАВОЧНИКА. ВЫБОР ДАННЫХ Справочник, если число его записей больше нуля, может быть позиционирован либо перед своей первой записью, либо на записи, либо вслед за последней записью. Сразу после создания справочника его позиция не определена. Позицию справоч ника меняют методы поиска данных, например НайтиЭлемент или НайтиПоКоду. Зна чение элемента в текущей позиции возвращает функция ТекущийЭлемент.
В процессе работы можно выбрать все или часть элементов справочника, открыв выборку методом ВыбратьЭлементы или ВыбратьЭлементыПоРеквизиту. В результа те их применения справочник позиционируется на своей первой записи. Перемещение по записям осуществляется методом ПолучитьЭлемент. Пример 1. Выводится список цехов предприятия. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если (сСотр_2.ЭтоГруппа() = 1) и (сСотр_2.Уровень() = 1) тогда Сообщить(сСотр_2. Наименование); конецЕсли; конецЦикла; конецПроцедуры // Выполнить Результат: •
01 Цех 02 Цех 03 Цех Пример 2. Выводится список подразделений первого цеха предприятия. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Если не найден первый цех (его код равен единице) если сСотр_2.НайтиПоКоду(1, 0) = 0 тогда Предупреждение("Первый цех не найден."); возврат; конецЕсли; // Ограничиваем выборку первым цехом сСотр_2.ИспользоватьРодителя(сСотр_2); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда Сообщить(сСотр_2.Наименование); конецЕсли; конецЦикла; конецПроцедуры // Выполнить Результат: 01 / 1 02/2 03/3
Выбор по реквизиту методом ВыбратьЭлементыПоРеквизиту возможен только для непериодического_реквизита, если для него на закладке Дополнительные установлено, свойство Сортировка. (По умолчанию свойство Сортировка имеет реквизиты Код и Наименование.) С перечисленными в этом разделе методами при выборе данных употребляются методы ОбратныйПорядок, ПорядокНаименований, ПорядокКодов, ВключатьПодчиненные, ИспользоватьРодителя, ИспользоватьВладельца и ИспользоватьДату.
5.7. ИЗМЕНЕНИЕ СТРУКТУРЫ И ФОРМ СПРАВОЧНИКА Приспособим справочник Сотрудники_2 для хранения информации об образова нии сотрудника. Для этого потребуется провести некоторую дополнительную работу создать справочник Образование_2. Она необходима, поскольку встроенный справоч ник Образование имеет владельца - справочник Сотрудники. Структуру справочника Образование_2 сделаем максимально простой, использо вав в нем только установленные по умолчанию реквизиты Код и Наименование. Далее внесем в поле Наименование приведенные на рис. 5.28 записи.
Рис. 5.28. Состав справочника Образование_2 Значения этих записей будем использовать для определения (еще несуществующе го) реквизита Образование_2 справочника Сотрудники_2. При создании справочника Образование_2 добавим команду его вызова в колонку Справочники меню интерфейса Ученик. Переместимся вновь в конфигурацию и пополним справочник Сотрудники_2 реквизи том Образование_2, сделав его непериодическим, установив для него тип Справочники с разновидностью типа Образование_2 и задав свойство Сортировка (рис. 5.29).
Рис. 5.29. Свойства реквизита Образование: а - общие; б - дополнительные
Изменим соответствующим образом и формы элемента и списка справочника (основную и для выбора), открыв их в конфигураторе, выбрав на панели инструментов и добавив в них реквизит Образование (рис. 5.30). Элементы диалога иконку
Рис. 5.30. Форма элемента с добавленным полем Образование Сохраним конфигурацию, воспользуемся новой формой и отредактируем все эле менты справочника Сотрудники_2, определив для них значение реквизита Образова ние. Для облегчения работы можно задать одинаковое значение этого реквизита всем сотрудникам, использовав следующий код: // Все сотрудники после выполнения этой процедуры в поле Образование // будут иметь значение Высшее процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем сСотр_2, сОбр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сОбр_2 = СоздатьОбъект("Справочник.Образование_2"); если сОбр_2.НайтиПоНаименованию("Высшее") = 0 тогда Предупреждение("3начение не найдено."); возврат; конецЕсли; сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() > 0 цикл если сСотр_2.ЭтоГруппа() = 0 тогда // Используем в правой части присваивания метод ТекущийЭлемент сСотр_2.0бразование = сОбр_2.ТекущийЭлемент(); сСотр_2.3аписать(); // Фиксируем изменения конецЕсли; конецЦикла; // Просмотр результата ОткрытьФорму("Справочник.Сотрудники_2.ФормаСписка"); конецПроцедуры // Выполнить Выполним теперь ручное редактирование значения реквизита Образование и изоб разим измененный справочник на рис. 5.31.
Рис. 5.31. Пополненный справочник Сотрудники_2
Выберем теперь данные по реквизиту Образование. процедура Выполнить() // Выбирает данные по реквизиту Образование перем сСотр_2, сОбр_2; перем обр, флаг; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сОбр_2 = СоздатьОбъект("Справочник.Образование_2"); если сОбр_2.НайтиПоНаименованию("Высшее") = 0 тогда Предупреждение("3начение не найдено."); возврат; конецЕсли; обр = сОбр_2.ТекущийЭлемент(); // Выбираем сотрудников, имеющих высшее образование, по порядку (без учета иерархии) флаг= сСотр_2.ВыбратьЭлементыПоРеквизиту("Образование", обр, 0, 0); если флаг = 0 тогда Предупреждение("Нет выбранных сотрудников."); возврат; конецЕсли; пока сСотр_2.ПолучитьЭлемент() > 0 цикл Сообщить(сСотр_2.Наименование + символТабуляции + сСотр_2.Код + символТабуляции + сСотр_2.Родитель + символТабуляции + сСотр_2.Образование); конецЦикла;конецПроцедуры // Выполнить Результат: Абрамова Лариса Сергеевна Агальцов Юрий Алексеевич Безверхний Игорь Петрович Кузьмина Раиса Николаевна Смирнова Нина Федоровна
201 111 301 122 132
02 Цех 01/1 03 Цех 01/2 01/3
Высшее Высшее Высшее Высшее Высшее
Если перед вызовом метода ВыбратьЭлементыПоРеквизиту обратиться к методу ОбратныйПорядок с равным единице параметром: сСотр_2.0братныйПорядок(1); то результирующая выборка будет упорядочена в обратном порядке: Смирнова Нина Федоровна Кузьмина Раиса Николаевна Безверхний Игорь Петрович Агальцов Юрий Алексеевич Абрамова Лариса Сергеевна
132 122 301 111 201
01/3 01/2 03 Цех 01/1 02 Цех
Высшее Высшее Высшее Высшее Высшее
Замечание. Попытка применить с методом ВыбратьЭлементыПоРеквизиту метод ПорядокКодов, предполагающий вывод данных по возрастанию значений атрибута Код, успехом не увенчалась.
5.8. У П Р А В Л Е Н И Е З А П И С Я М И С П Р А В О Ч Н И К А ПРИ ПОМОЩИ ДОКУМЕНТОВ 5.8.1. НАЗНАЧЕНИЕ ДОКУМЕНТОВ Процессы, протекающие на предприятии, регулируются документами. Так, прием на работу осуществляется на основании соответствующего приказа. Поступление ма териалов на склад фиксируется приходными ордерами, а их отпуск в производстве выполняется на основании требований-накладных и т. д. Не меньшее, чем на реальном предприятии, значение придается документам и в 1С. Фактически они лежат в основе реализованных в 1С моделей - модели склада, бухгалтерского учета и др. Кадровый учет и начисление заработной платы также основываются на документах: на приказах о приеме на работу, перемещении по служебной лестнице, об измене ниях оклада, о премировании и др. Отсюда следует, что большинство изменений спра вочника Сотрудники_2, хранящего кадровые данные, должно осуществляться на осно вании соответствующих документов. Создадим и мы документы, добавляющие данные в справочник Сотрудники_2 и изменяющие в нем данные об окладах работников предприятия. Понятно, такими документами могут быть Приказ о приеме на работу и Приказ об изменении оклада. Документы, попавшие в зону нашего внимания, учитывая скромный объем хранящей ся в справочнике Сотрудники_2 информации, весьма просты, что вполне отвечает учебным целям. Документ 1С состоит из формы и модуля документа. Форма документа, как и формы других объектов 1С, имеет диалог, модуль и таблицы. Диалог формы доку мента в общем случае содержит шапку и табличную часть. В модуле документа в общем случае создаются процедуры ОбработкаПроведения и ОбработкаУдаленияПроведения, выполняющие предусмотренные при проведении и удалении доку мента действия. (Проведение документа - это процесс, приводящий к изменению связанных с документов объектов.) Так, проведение Приказа о приеме должно приводить к появлению в справочнике Сотрудники_2 новой записи, а проведение Приказа об изменении оклада - к изменению значений периодического реквизита Оклад справочника. Выпущенные документы будем сохранять в имеющемся в конфигурации Заработная плата и кадры журнале ПриказыКадровые. Поскольку Приказ о приеме на работу касается одного человека, то в диалоге его формы табличная часть будет отсутствовать. Приказ об изменении окладов может распространяться на несколько сотрудников, ФИО и новые оклады которых фиксиру ются в табличной части диалога формы документа. Чтобы установить связь между создаваемыми документами и записями справоч ника Сотрудники_2, добавим в справочник еще два реквизита - ПриказПрием и ПриказОклад, присвоив им тип Документ. Теперь справочник Сотрудники_2 имеет приведенные на рис. 5.32 реквизиты, а также реквизиты Код и Наименование.
Рис. 5.32. Реквизиты справочника Сотрудники_2 Такая связь позволит нам обращаться к созданным документам из формы списка справочника.
5.8.2. ПРИКАЗ О ПРИЕМЕ НА РАБОТУ 5.8.2.1. РЕКВИЗИТЫ И ФОРМА ДОКУМЕНТА Начнем с Приказа о приеме. Откроем конфигурацию, выделим раздел Документы, нажмем на правую кнопку мыши и выберем пункт Новый документ. Определим реквизиты шапки документа в соответствии с табл. 5.3. Таблица 5.3 Реквизиты шапки Приказа о приеме на работу Реквизит Код ФИО Подразделение Образование Оклад ДатаПриема Сотрудник
Тип (разновидность типа) Числовой Символьный Справочник.Сотрудники_2 Справочник.Образование_2 Числовой Дата Справочник.Сотрудники_2
Длина.Точность 5.0 30 10.2 -
Замечания: 1. Тип (разновидность типа) и характеристики типа (длина, точность) должны соот ветствовать аналогичным реквизитам справочника Сотрудники_2. 2.
Код сотрудника формируется автоматически при добавлении новой записи. Редак тированию значение кода не подлежит, поэтому в диалоге формы документа поле Код доступно только для чтения.
3.
Реквизит Сотрудник введен в документ для установления связи документа с кон кретной записью справочника Сотрудники_2. Для позиционирования справочника в режиме редактирования документа можно использовать вызов сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.НайтиЭлемент(Сотрудник); // // Устанавливаем значения реквизитов записи сСотр_2.Наименование = ФИО; сСотр_2.3аписать();
Сотрудник - реквизит документа
4.
Помимо перечисленных в табл. 5.3 полей в Приказе о приеме, как, впрочем, и во всех иных документах, присутствуют реквизиты (атрибуты) НомерДок и ДатаДок, хранящие соответственно номер и дату документа. Свойства этих реквизи тов задаются системой и не могут быть изменены пользователем. Поскольку но мер документа назначается автоматически, то запретим редактирование реквизита : НомерДок. Дату нового документа будем по умолчанию устанавливать равной ра бочей дате. Остальные характеристики приказа определим в соответствии с рис. 5.33.
Рис. 5.33. Свойства документа - Приказа о приеме на работу Откроем затем форму документа, вставим в ее диалог реквизиты и приведем диа лог к виду, представленному на рис. 5.34.
Рис. 5.34. Диалог формы Приказа о приеме на работу Замечание. При добавлении документа в конфигурацию 1С создаст в информаци онной базе для хранения данных шапки документов ПриказОПриеме 2 файла, воз можно DH4216.DBF и DH4216.CDX. Если бы документ содержал вдобавок и таблич ную часть, то для хранения данных из этой части 1С пополнил бы информационную базу еще двумя файлами - DT4216.DBF и DT4216.CDX.
5.8.2.2.
МОДУЛЬ
ФОРМЫ ДОКУМЕНТА
С кнопкой ОК диалога документа связаны команды #Записать?Провести?Закрыть Они реализуют последовательность методов Записать(); // Запись документа // Проведение документа. Вызывается процедура ОбработкаПроведения модуля документа Провести(); форма.Закрыть(); Перед записью мы условились выполнять проверки введенных данных. Воспользуем ся для этого предопределенной процедурой модуля формы документа ПриЗаписи, размес тив в ней все проверки и завершая ее со статусом возврата 0 при наличии ошибок. По-прежнему часть проверок будем производить функцией КонтрольФИО, разра ботанной для формы элемента в разд. 5.3.4.2. Только на этот раз функция КонтрольФИО связывается через свойство Формула с полем ФИО. Дополнительно введем про верки, не позволяющие сохранять документ, если в диалоге формы документа (рис. 5.34) есть незаполненные поля. При вводе нового документа полезно выполнять начальную установку даты доку мента и даты приема на работу. Используем для этого предопределенную процедуру ВводНового. При добавлении записи (прием нового сотрудника) значение реквизита Код будем формировать автоматически, используя в качестве его первых цифр код родителя, если родитель расположен на втором уровне справочника Сотрудники_2, или код родителя, увеличенный в 10 раз, если родитель лежит на первом уровне (всего, напомним, в справочнике Сотрудники_2 три уровня). Последующие цифры кода сотрудника об разуют возрастающую последовательность целых положительность чисел. Поступая таким образом, мы сохраним принцип формирования кодов, применяемый 1С для справочника Сотрудники_2. Формирование кода выполним в процедуре СоздатьКод, которую свяжем с полем Подразделение, задав для него в окне задания свойств поля на закладке Дополнитель но формулу СоздатьКод(). Тогда процедура СоздатьКод будет вызываться каждый раз при изменении подразделения. Если нужного подразделения в справочнике Сотрудники_2 нет, то его придется ввести, применив форму группы справочника (разд. 5.3.3.1). Таким образом, модуль формы Приказа о приеме на работу содержит следующие процедуры и функции: функция КонтрольФИО(место = 1) далее процедура УбратьПробелы() далее процедура ВНРег() далее процедура СоздатьКод() далее // Выполняет проверки вводимых данных процедура ПриЗаписи() если ((ПустоеЗначение(Подразделение) = 1) или (ПустоеЗначение(ФИО) = 1) или (ПустоеЗначение(Оклад) = 1) или (ПустоеЗначение(Образование) = 1) тогда Предупреждение("В документе есть неопределенные реквизиты."); СтатусВозврата(0); // Не записываем данные
// Функция КонтрольФИО при таком вызове вернет 0, если в ФИО есть точки иначеЕсли КонтрольФИО(2) = 0 тогда СтатусВозврата(0); конецЕсли; конецПроцедуры // ПриЗаписи // Подробный комментарий к функции и ее процедурам см. в разд. 5.3.4.2 функция КонтрольФИО(место = 1) если место = 1 тогда // Вызов осуществляется из диалога УбратьПробелы(); // Удаляет избыточные пробелы между словами ВНРег(); конецЕсли; если Найти(ФИО,".") <> 0 тогда Предупреждение("Точки в ФИО недопустимы."); возврат 0; иначе // Точек в имени нет возврат 1; конецЕсли; конецФункции // КонтрольФИО процедура УбратьПробелы() перем ФИО2, длина, поз, к; // Удаляем возможные ведущие и завершающие пробелы // Вводим ФИО2, поскольку СокрЛП не меняет длину ФИО ФИО2 = СокрЛП(ФИО); поз = Найти(ФИО2, " "); // Ищем два подряд идущих пробела // Цикл продолжается, пока в строке есть хотя бы одна пара подряд идущих пробелов пока поз > 0 цикл длина = стрДлина(ФИО2); // Длина ФИО2 к = 1; // Число лишних пробелов пока (поз < длина) и (Сред(ФИО2, поз + к, 1) = " ") цикл к = к + 1; конецЦикла; // пока (поз < длина)... // Удаляем лишние пробелы и заменяем старое значение ФИО на новое ФИО2 = Лев(ФИО2, поз) + Прав(ФИО2, длина - поз - к + 1); поз = Найти(ФИО2, " "); // Ищем 2 подряд идущих пробела конецЦикла; // пока поз > 0 ФИО = ФИО2; // Результат конецПроцедуры // УбратьПробелы // Преобразовывает ФИО так, что первые буквы каждого // составляющего ФИО слова прописные, а остальные - строчные процедура ВНРег() перем к, длина; длина = стрДлина(ФИО); // Длина ФИО если длина = 0 тогда возврат; конецЕсли; ФИО = Нрег(ФИО); // Первый шаг алгоритма ФИО = Врег(Лев(ФИО, 1)) + Прав(ФИО, длина - 1); // Позиция пробела или нуль, если пробел не найден к = Найти(ФИО, " "); пока к > 0 цикл ФИО = Лев(ФИО, к - 1) + "%" +
Врег(Сред(ФИО, к + 1, 1)) + Прав(ФИО, длина - к - 1); к - Найти(ФИО, " "); конецЦикла; // пока к < длина // Заменяем символы % точки на пробелы ФИО = СтрЗаменить(ФИО, "%", " "); конецПроцедуры // ВНрег // Формирует и возвращает при добавлении новой записи код сотрудника // Связана через свойство Формула с полем Подразделение диалога формы документа // Вызывается после выбора подразделения предприятия // Алгоритм: //1. Начало. // 2. Прочитать кодПодр - код подразделения и его уровень //3. Найти максКод - максимальное значение кода сотрудника, входящего в подразделение // 4. Найти двеЦифр - первые две цифры кода сотрудника по следующему правилу: // если уровень = 1, то // двеЦифр = кодПодр * 10 // иначе // двеЦифр = кодПодр // конец если // 5. Найти послЦифр - цифры максКод, следующие после его первых двух цифр // 6. Последующие цифры нового кода: послЦифр = послЦифр + 1 // 7. Новый код: Код = Число(Строка(двеЦифр) + Строка(послЦифр)) //8. Конец. процедура СоздатьКод() перем сСотр_2, кодПодр, уровень, максКод, двеЦифр, послЦифр; перем двеЦифрУмнНаК, к; если ПустоеЗначение(Подразделение) = 1 тогда Подразделение = ПолучитьПустоеЗначение("Справочник.Сотрудники_2"); Предупреждение("Подразделение не выбрано."); возврат; конецЕсли; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // В справочнике Сотрудники_2 можно в поле Подразделение выбрать как сотрудника, // так и подразделение. В первом случае мы перейдем от сотрудника к подразделению, // использовав атрибут Родитель. Во втором, если подразделение на следующем уровне // имеет сотрудников, мы имеем верный выбор, если же это не так, выбор // подразделения придется продолжить // Если выбран сотрудник если Подразделение.ЭтоГруппа() = 0 тогда // Найдем родительскую группу; реквизит Подразделение имеет тип Справочник сСотр_2.НайтиЭлемент(Подразделение); // Переходим от сотрудника к подразделению // и устанавливаем на него позицию справочника Подразделение = сСотр_2.Родитель; сСотр_2.НайтиЭлемент(Подразделение); // Проверим, что на следующем уровне подразделения иначе сСотр_2.НайтиЭлемент(Подразделение); сСотр_2.ИспользоватьРодителя(сСотр_2.ТекущийЭлемент()); сСотр_2.ВыбратьЭлементы(1); // Задаем выборку с учетом иерархии // Если на следующем уровне подразделение, то это не годится если сСотр_2.ПолучитьЭлемент() = 1 тогда
если сСотр_2.ЭтоГруппа() = 1 тогда Подразделение = ПолучитьПустоеЗначение("Справочник.Сотрудники_2"); Предупреждение("Выбрано подразделение не того уровня."); возврат; иначе // Возвращаемся к верному подразделению сСотр_2.НайтиЭлемент(Подразделение); конецЕсли; конецЕсли; конецЕсли; // сСотр_2.Код может иметь символьный тип кодПодр = ?(ТипЗначения(сСотр_2.Код) = 2, Число(сСотр_2.Код), сСотр_2.Код); уровень = сСотр_2.Уровень(); двеЦифр = ?(уровень = 1, кодПодр * 10, кодПодр); // Найдем максКод - максимальный код сотрудника в пределах подразделения // Осуществи это, перебирая все элементы подразделения, что достигается // в результате употребления метода ИспользоватьРодителя сСотр_2.ИспользоватьРодителя(Подразделение); // Осуществим перебор в порядке возрастания кодов элементов сСотр_2.ПорядокКодов(); сСотр_2.ВыбратьЭлементы(); // максКод останется равным нулю, если в группе нет элементов максКод = 0; пока сСотр_2.ПолучитьЭлемент() = 1 цикл // сСотр_2.Код может иметь символьный тип максКод = ?(ТипЗначения(сСотр_2.Код) = 2, Число(сСотр_2.Код), сСотр_2.Код); конецЦикла; // пока // Ищем последующие цифры максКод и затем последующие цифры кода // нового сотрудника. Будем умножать двеЦифр на 10, пока не превысим максКод к=1; двеЦифрУмнНаК = двеЦифр; пока двеЦифрУмнНаК < максКод цикл двеЦифрУмнНаК = двеЦифрУмнНаК * 10; конецЦикла; // пока // Последующие цифры кода нового сотрудника послЦифр = ?(максКод = 0, 1, максКод - двеЦифрУмнНаК /10 + 1); // Итак, код нового сотрудника Код = Число(Строка(двеЦифр) + Строка(послЦифр)); конецПроцедуры // СоздатьКод // ВводНового - предопределенная процедура, вызываемая, если присутствует // в коде модуля формы, при вводе нового документа. Используем ее для начальной // установки даты нового документа и даты приема на работу процедура ВводНового() ДатаДок = РабочаяДата(); ДатаПриема = РабочаяДата(); конецПроцедуры // ВводНового процедура ПриОткрытии() ОчиститьОкноСообщений( ); ПриЗаписиПерепроводить(1); конецПроцедуры // ПриОткрытии
Замечания: 1. В форме списка для выбора справочника Сотрудники_2, используя которую мы определяем значение поля Подразделения документа (см. рис. 5.34), в предопреде ленной процедуре ПриОткрытии размещен вызов метода ВыборГруппы(1), позво ляющий выбирать группы. Поэтому в общем случае мы можем выбрать как под разделение, так и сотрудника. Чтобы поправить пользователя, в процедуру СоздатьКод добавлены операторы, либо осуществляющие переход от сотрудника к подразделению, либо препятствующие выбору подразделения не того уровня (в нашем случае это Цех 01). Вот они: // Если выбран сотрудник если Подразделение.ЭтоГруппа() = 0 тогда // Найдем родительскую группу; реквизит Подразделение имеет тип Справочник // Далее см. процедуру СоздатьКод // Проверим, что на следующем уровне подразделения // Если на следующем уровне подразделение, то это не годиться иначе // Далее см. процедуру СоздатьКод конецЕсли; Более совершенный способ выбора подразделения по справочнику Сотрудники_2 мы рассмотрим в разд. 7.4.3 при разработке документа Табель. 2.
Хотя реквизит Код справочника Сотрудники_2 имеет числовой тип, вызов сообщить(ТипЗначенияСтр(сСотр_2 .Код)); напечатает в строке сообщений слово Строка. Поэтому в процедуре СоздатьКод вместо операторов присваивания кодПодр = сСотр_2.Код; максКод = сСотр_2.Код; употреблены операторы кодПодр = ?(ТипЗначения(сСотр_2.Код) = 2, Число(сСотр_2.Код), сСотр_2.Код); максКод = ?(ТипЗначения(сСотр_2.Код) = 2, Число(сСотр_2.Код), сСотр_2.Код); преобразовывающие сСотр_2.Код в число, если сСотр_2.Код имеет символьный тип. Вспомнив, однако, что тип выражения в 1С определяется типом его первого операнда (разд. 2.5.1), запишем более простые (по форме) операторы, в которых кодПодр и максКод имеют числовой тип: кодПодр = 1 * сСотр_2.Код; максКод = 1 * сСотр_2.Код; 5.8.2.3.
МОДУЛЬ ДОКУМЕНТА. ПРОВЕДЕНИЕ И ДОКУМЕНТА
УДАЛЕНИЕ
При проведении документа вызывается создаваемая программистом предопреде ленная процедура модуля документа ОбработкаПроведения. В ней для нашего случая учитываются следующие состояния документа:
1) проводится новый документ; 2) проводится документ, с которого снята пометка удаления (при простановке помет ки удаления документ становится непроведенным). Такой документ может иметь соответствующую ему запись в справочнике Сотрудники_2; 3) перепроводится ранее проведенный документ. Удаление непроведенного документа о приеме на работу при отсутствии на него ссылок в других объектах можно выполнить методом Удалить(1); сразу проставляющим DBF-пометку об удалении. При упаковке базы документ будет удален из нее физически. Удаление проведенного документа - более сложный процесс. Здесь возможны ва рианты: 1) в системе нет ссылок ни на документ (кроме ссылки в созданной им записи в спра вочнике Сотрудники_2), ни на запись в справочнике Сотрудники_2 (кроме как в самом удаляемом документе); 2) такие ссылки имеются, например в журнале расчетов Заработная плата. В первом случае следует удалить и документ, и порожденную им запись в спра вочнике Сотрудники_2. Во втором - ни документ, ни запись в справочнике удалять нельзя, поскольку это приведет к нарушению целостности данных. Поскольку анализ ссылок на предназначенные для удаления объекты имеется в 1С и осуществляется по сле выбора в меню Сервис - Удаление помеченных объектов, то для удаления доку мента следует использовать подход, при котором проставляется пометка 1С на удале ние как документа, так и созданной им в справочнике Сотрудники_2 записи, а анализ ссылок оставляется 1С. Код, реализующий этот подход, располагается в предопреде ленной процедуре ОбработкаУдаленияПроведения, которая вызывается при удалении документа или при исполнении метода СделатьНеПроведенным. Код модуля документа с учетом вышеприведенного комментария может быть таким: // Выполняет проведение документа, то есть добавляет или корректирует запись // в справочнике Сотрудники_2, связывает запись справочника с документом, а // документ - с соответствующей записью справочника Сотрудники_2 процедура ОбработкаПроведения() перем сСотр_2, гр, флаг, флаг2; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Храним в гр подразделение, куда принимается или переводится сотрудник сСотр_2.НайтиЭлемент(Подразделение); гр = сСотр_2.ТекущийЭлемент(); флаг = Проведен(); // Находим в справочнике Сотрудники_2 запись, с которой связан данный приказ флаг2 = сСотр_2.НайтиЭлемент(Сотрудник); если флаг + флаг2 = 0 тогда . // Если документ не проведен сСотр_2.Новый(); конецЕсли;
//
и не снимается пометка на удаления
сСотр_2.Родитель = гр; // Фиксируем подразделение (родителя) // Определяем и сохраняем непериодические реквизиты сСотр_2.Наименование = ФИО; сСотр_2.Код = Код; сСотр_2.Образование = Образование; сСотр_2.ПриказПрием = ТекущийДокумент(); сСотр_2.3аписать(); // Устанавливаем связь документа с записью справочника Сотрудники_2 Сотрудник = сСотр_2.ТекущийЭлемент(); // Записываем периодический реквизит Оклад с привязкой к документу УстановитьРеквизитСправочника(Сотрудник, "Оклад", Оклад, ДатаПриема); // Снимем, если она есть, пометку удаления записи о сотруднике // в справочнике Сотрудники_2. Такая пометка может появиться при удалении // документа, породившего эту запись (ом. процедуру ОбработкаУдаленияПроведения) если (флаг2 = 1) и (сСотр_2.ПометкаУдаления() = 1) тогда сСотр_2,СнятьПометкуУдаления(); конецЕсли; если флаг = 0 тогда // Если документ не проведен // Ограничиваем время показа окна с предупреждением тремя секундами Предупреждение("Документ проведен.", 3); иначе Предупреждение("Документ перепроведен.", 3); конецЕсли; конецПроцедуры // ОбработкаПроведения // Вызывается при удалении документа или при исполнении метода СделатьНеПроведенным процедура ОбработкаУдапенияПроведения() перем сСотр_2, флаг; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); флаг = сСотр_2.НайтиЭлемент(Сотрудник); если флаг = 1 тогда // Помечаем для удаления запись в справочнике Сотрудники_2 сСотр_2.Удалить(0); конецЕсли; конецПроцедуры // ОбработкаУдаленияПроведения Замечания: 1.
Предопределенные процедуры модуля документа ОбработкаПроведения и Обработ каУдаленияПроведения 1С выполняет с использованием транзакций (разд. 8.4.3), во время которых блокируются DBF-таблицы, в транзакциях участвующие. При мно гопользовательском режиме работы блокировка порождает конфликтные ситуации: иной пользователь не может проводить или удалять аналогичные документы. Чтобы снизить вероятность конфликтов, в процедуры ОбработкаПроведения и ОбработкаУ даленияПроведения не следует включать вызовы процедур, приостанавливающих вы числения, например, такой, как Предупреждение, заменяя ее на вызов процедуры Со общение.
2.
Реквизит приказа о приеме Сотрудник после проведения документа позволяет не посредственно обращаться к созданной им в справочнике Сотрудники_2 записи, например, так: док = СоздатьОбъект("Документ.ПриказОПриеме"); флаг = док.НайтиПоНомеру(2, Дата(0)); если флаг = 1 тогда //док.Сотрудник.Образование - значение реквизита Образование в справочнике // Сотрудники_2 для сотрудника, проведенного приказом № 2 Сообщить(док.Сотрудник.Образование); // Напечатает Высшее иначе Предупреждение("Приказ № 2 не найден."); конецЕсли;
Теперь, когда есть все процедуры, управляющие документом о приеме на работу, включим пункты Приказ о приеме и Изменение оклада (документ для этого пункта бу дет создан ниже), вызывающие соответственно команды Документы.ПриказОПриеме.Ввести и Документы.ИзменениеОклада.Ввести в колонку Документы меню интерфейса Ученик, связав их соответственно с аксе лераторами Alt+б и Alt+7 (рис. 5.35), и добавим колонку Журналы с пунктом Кадро вые приказы, позволяющим при помощи команды Журналы.ПриказыКадровые.Открыть открыть журнал, хранящий созданные документы.
Рис. 5.35. Модифицированное меню интерфейса Ученик Сохраним изменения, откроем 1С:Предприятие и введем несколько документов. 5.8.2.4.
ВЫЗОВ ДОКУМЕНТА ИЗ ФОРМЫ СПИСКА СПРАВОЧНИКА СОТРУДНИКИ_2
Пока что с записью справочника Сотрудники_2 может быть связан только один документ - Приказ о приеме на работу. Для его вызова из формы списка справочника
добавим в модуль двух имеющихся форм списка (разд. 5.3.3.2) код следующей предо пределенной процедуры: процедура ПриНачалеРедактированияСтроки( ) если ПустоеЗначение(ПриказПрием) = 0 тогда // Открываем форму документа ОткрытьФорму(ПриказПрием); иначеЕсли РедактироватьВДиалоге() = 0 тогда // Открываем форму элемента справочника для редактирования текущей записи ОткрытьФорму(ТекущийЭлемент(),, 0); // Отказываемся от возможности редактирования в строке формы списка справочника иначе Предупреждение("Редактировать в строке формы списка справочника нельзя."); конецЕсли; СтатусВозврата(0); конецПроцедуры // ПриНачалеРедактированияСтроки Чтобы этим кодом воспользоваться, надо после вызова справочника Сотрудники_2, используя колонку Действия, убрать флаг с пункта Редактировать в диалоге. Те перь, остановившись на записи и дважды ударив по ней мышью, вы попадете в доку мент, с этой записью связанный, или, если такового не имеется, в форму элемента или группы справочника Сотрудники_2. Замечания: 1.
Вызов СтатусВозврата(0); в предопределенной процедуре ПриНачалеРедактированияСтроки, задающий ста тус Отменить Действие, позволяет не переходить в режим редактирования строки непосредственно в форме списка справочника Сотрудники_2, если в диалоге от крытой формы приказа о приеме нажать Закрыть или если просто переместить фо кус на диалог формы списка справочника.
2.
При отсутствии документа, если отменен режим редактирования в диалоге, проце дурой ПриНачалеРедактированияСтроки будет вызвана функция ОткрытьФорму(ТекущийЭлемент()); открывающая форму элемента или группы. При этом может возникнуть сообщение
Однако никаких препятствий для редактирования в форме элемента или группы выбранной записи не будет.
5.8.3. ЖУРНАЛ КАДРОВЫХ ПРИКАЗОВ 5.8.3.1. ФОРМА СПИСКА ЖУРНАЛА КАДРОВЫХ ПРИКАЗОВ Журнал кадровых приказов с идентификатором ПриказыКадровые имеет приведенный на рис. 5.36 диалог формы списка, отображающий занесенные в журнал доку- \ менты.
Рис. 5.36. Форма списка журнала кадровых приказов Поскольку в журнале могут храниться приказы, порождаемые различными до кументами, имеющими реквизит Сотрудник, то необходимо указать все возможные значения этого реквизита. Выполним это для созданного нами документа ПриказОПриеме. Откроем конфигурацию, доберемся до пункта Журналы документов, а затем до его подпункта ПриказыКадровые и ударим дважды мышью. Появившийся диалог (рис. 5.37) позволит нам осуществить намеченную цель.
Рис. 5.37. Диалог задания свойств журнала кадровых приказов Выберем в поле Графы запись Сотрудник и нажмем кнопку Изменить. Появив шийся диалог приведем к виду, представленному на рис. 5.38, и дважды ударим по элементу Сотрудник в поле Возможные значения, или нажмем на Enter, или вос пользуемся кнопкой
Рис. 5.38. Добавление значения Документ.ПриказОПриеме. Сотрудник
В результате в списке Выбранные значения появится элемент Документ.ПриказОПриеме.Сотрудник. Теперь, если сохранить изменения, при записи доку мента ПриказОПриеме в графе Сотрудник диалога формы списка журнала кадровых перемещений будет отображаться значение реквизита Сотрудник этого документа (рис. 5.39).
Рис. 5.39. Журнал кадровых приказов после ввода трех документов
Галочка в первом столбце говорит о том, что документ проведен. Первый документ, если открыть его для просмотра или редактирования, содержит данные, представленные на рис. 5.40.
Рис. 5.40. Приказ номер 1
В справочнике Сотрудники_2 (в форме списка) он породит приведенную на рис. 5.41 запись, которую можно отредактировать как в форме элемента, так и в документе (см. разд. 5.8.2.4).
Рис. 5.41. Проводка приказа номер 1
Замечания: 1.
При удалении проведенного документа о приеме на работу обнуляется внесенное документом значение реквизита Оклад в соответствующей ему записи справочни ка Сотрудники_2. При перепроведении документа это значение восстанавливается. Автоматическое обнуление выполняется, так как периодический реквизит Оклад связывается с документом в процессе его проведения.
2.
По умолчанию в журнале документов отображаются документы, даты которых на ходятся в пределах текущего месяца. Чтобы изменить временной диапазон журна ла, называемый интервалом журнала, следует воспользоваться либо пунктом Ин тервал из колонки меню Действия, либо иконкой панели инструментов откры того журнала. Программно интервал журнала задается методом УстановитьИнтервал.
3.
В программе журнал кадровых приказов можно открыть в результате следующего вызова: ОткрытьФорму("Журнал.ПриказыКадровые");
5.8.3.2.
МОДУЛЬ
ФОРМЫ СПИСКА
ЖУРНАЛА
КАДРОВЫХ ПРИКАЗОВ
Разместим в нем предопределенную процедуру ПриОткрытии, в которой, во-пер вых, определим стандартный список действий для кнопки Действия диалога формы и, во-вторых, установим интервал журнала, характеризуемый двумя датами: начала ин тервала и конца интервала. Список действий разместим в переменной сДейст типа СписокЗначений, а с кнопкой Действия диалога формы списка свяжем на закладке Дополнительно в поле формула вызов процедуры глобального модуля глДействия(ТекущийДокумент, сДейст). Начало интервала положим равным дате первого документа ПриказОПриеме, а ко нец - текущей дате. Если документов ПриказОПриеме в журнале кадровых приказов нет, то дату начала интервала определим по первому приказу об изменении окладов. Если нет и таковых, то дату начала установим равной текущей дате. перем сДейст;
//
Список действий по документу
// Формирует список действий и устанавливает интервал журнала, отображающий // все введенные документы видов ПриказОПриеме и ИзменениеОклада процедура ПриОткрытии() перем дНач; // Дата начала интервала журнала кадровых приказов перем флаг, док; // Определяем список действий для кнопки Действия сДейст.ДобавитьЗначение("Структура подчиненности"); сДейст.ДобавитьЗначение("Ввести на основании"); сДейст.ДобавитьЗначение("Движения документа"); флаг = 1; // Равен единице, если удалось создать док попытка док = СоздатьОбъект("Документ.ПриказОПриеме");
исключение попытка док = СоздатьОбъект("Документ.ИзменениеОклада"); исключение флаг = 0; конецПопытки; конецПопытки; // Если создан либо документ ПриказОПриеме, либо документ ИзменениеОклада если флаг = 1 тогда // Находим документ с наименьшей датой. По умолчанию документы располагаются // в выборке по возрастанию их дат док.ВыбратьДокументы(); если док.ПолучитьДокумент() = 1 тогда дНач = док.ДатаДок; иначе дНач = ТекущаяДата(); конецЕсли; иначе // Искомых документов нет дНач = ТекущаяДата(); конецЕсли; УстановитьИнтервал(дНач, ТекущаяДата()); конецПроцедуры // ПриОткрытии // В основной программе модуля всего один оператор сДейст = СоздатьОбъект("СписокЗначений");
5.8.4. ПРИКАЗ ОБ ИЗМЕНЕНИИ ОКЛАДА 5.8.4.1. ПОРЯДОК РАБОТЫ С ДОКУМЕНТОМ Этот приказ интересен тем, что имеет табличную часть, поскольку в общем случае затрагивает группу сотрудников. Поэтому диалог формы Приказа об изменении окла да может иметь следующий вид:
Рис. 5.42. Диалог формы документа Приказ об изменении оклада Заданные для документа идентификаторы реквизитов (кроме номера и даты доку мента), а также идентификаторы самого документа и его журнала отображены на рис. 5.43.
Рис. 5.43. Идентификаторы документа, его реквизитов и журнала Полный перечень реквизитов создаваемого документа и имя самого документа пе речислены в табл. 5.4. Таблица 5.4 Реквизиты Приказа об изменении оклада Реквизит ДатаДок (задан по умолчанию) НомерДок (задан по умолча нию) ДатаНовОклада Сотрудник ПрежнийОклад НовыйОклад
Тип/разновидность типа
Длина.Точность
Дата Числовой
5.0
Дата Справочник.Сотрудники_2 Числовой Числовой
10.2 10.2
Замечание. Для нового и прежнего окладов заданы свойства Разделять триады и Неотрицательный. Порядок работы с документом таков: 1. Реквизит НомерДок сделаем, как и ранее, недоступным для редактирования. 2. Запретим редактирование и графы ПрежнийОклад. 3. При вводе нового документа имеющиеся в нем даты устанавливаются по умолча нию равными рабочей дате. 4. Кнопка Подбор открывает форму списка справочника Сотрудники_2 для множест венного выбора, в результате выполнения которого добавляются данные в две пер вые графы таблицы; ранее перенесенные в таблицу данные сохраняются. 5. Кнопки Очистить, Удалить и ОК доступны только при наличии в табличной части хотя бы одной строки. 6. Графы Сотрудник и Прежний оклад табличной части документа заполняем после выбора сотрудника из справочника Сотрудники_2. Для этого употребим процеду ру ВыборОдного, задав на закладке Дополнительные в окне свойств графы Со трудник формулу ВыборОдного( ), и процедуру Подбор, связанную с одноимен ной кнопкой диалога и позволяющую делать множественный выбор данных. 7. Выбранная в справочнике Сотрудники_2 запись не может переноситься в таблич ную часть документа более одного раза; проверка уникальности записи в таблич ной части документа выполняется, если осуществляется множественный выбор, в процедуре ОбработкаПодбора или в предопределенной процедуре модуля формы
8. 9.
документа ПриОкончанииРедактированияСтроки, если выбор выполняется из таб личной части документа. Проведение документа не выполняется, если действующий и новый оклады имеют одинаковые значения. При редактировании проведенного документа, когда удаляется запись из его таб личной части, в справочнике Сотрудники_2 удаляется соответствующая ему за пись периодического реквизита Оклад. Это достигается за счет того, что значение периодического реквизита Оклад в справочнике Сотрудники_2 устанавливается с привязкой к документу.
10. При замене в табличной части проведенного документа одного сотрудника на ино го удаляются проводка, связанная с заменяемым сотрудником. Это также достига ется за счет того, что значение периодического реквизита Оклад устанавливается с привязкой к документу. 11. При удалении документа удаляются все выполненные им проводки. Читатель, имеющий опыт программирования в 1С, реализует приведенный механизм управления документом без особого труда. Нам же придется приложить некоторые уси лия, но не напрасные, а продвигающие к следующему уровню освоения системы. Итак, добавим в конфигурацию документ, употребляя уже имеющиеся навыки, и приведем его форму к виду, изображенному на рис. 5.42. Чтобы регулировать доступность кнопок Очистить, Удалить и ОК, дадим им иден тификаторы, совпадающие с названиями кнопок (рис. 5.44).
Рис. 5.44. Задание идентификатора кнопки Очистить Добавим соответствующий пункт в колонку Документы меню интерфейса Ученик, установив для команды открытия документа акселератор Alt+7. С кнопками диалога и с графой Сотрудник Приказа об изменении оклада через их свойство Формула свя жем перечисленные в табл. 5.5 процедуры и команды. Таблица. 5.5 Процедуры и команды, связанные в элементами диалога документа Кнопка Сотрудник Подбор Очистить Удалить ОК Закрыть
Процедура/команда ВыборОдного() Подбор() ОчиститьТабл() УдалитьСтрокуТабл() #Записать?Провести?Закрыть #3акрыть
5.8.4.2. МОДУЛЬ ФОРМЫ ДОКУМЕНТА Содержит следующие процедуры: процедура ВводНового( ) // Задает начальные значения дат при вводе ДатаДок = РабочаяДата(); // нового документа ДатаНовОклада = РабочаяДата(); // При вводе нового документа табличная часть пуста, поэтому делаем кнопки // Очистить, Удалить и ОК недоступными форма.Очистить.Доступность(0); форма.Удалить.Доступность(0); форма.ОК.Доступность(0); конецПроцедуры // ВводНового процедура ПриОткрытии() Очистить0кноСообщений(); ПриЗаписиПерепроводить(1); конецПроцедуры // ПриОткрытии // Связана графой Сотрудник табличной части документа // Открывает подбор элемента из справочника Сотрудники_2 // После выбора заполняются поля Сотрудник и Прежний оклад // Добавление строки происходит при нажатии на клавишу клавиатуры Ins процедура ВыборОдного() перем сСотр_2, контПодбора; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Дата для периодического реквизита Оклад справочника Сотрудники_2 сСотр_2.ИспользоватьДату(РабочаяДата()); // Сотрудник и ПрежнийОклад - реквизиты табличной части // диалога формы рассматриваемого документа если сСотр_2.НайтиЭлемент(Сотрудник) = 1 тогда ПрежнийОклад = сСотр_2.Оклад; конецЕсли; // При добавлении хотя бы одной строки следующие кнопки делаем доступными если КоличествоСтрок() > 0 тогда форма.Очистить.Доступность(1); форма.Удалить.Доступность(1); форма.ОК.Доступность(1); конецЕсли; конецПроцедуры // ВыборОдного процедура ПриОкончанииРедактированияСтроки() перем старСтрока, сотр; // Проверяет введенную запись на уникальность // Если она нарушена, введенная запись удаляется старСтрока = НомерСтроки; // Запоминаем номер введенной строки сотр = Сотрудник; // и значение графы Сотрудник ВыбратьСтроки( ); пока ПолучитьСтроку() > 0 цикл если (Сотрудник = сотр) и (НомерСтроки о старСтрока) тогда Предупреждение("Сотрудник уже выбран."); УдалитьСтроку(); конецЕсли;
конецЦикла // пока КОнецПроцедуры // ПриОкончанииРедактированияСтроки // Связана с кнопкой Подбор диалога формы документа // Открывает множественный подбор из справочника Сотрудники_2 // Каждый выбор элемента добавляет строку в табличную часть документа, // заполняя в ней поля Сотрудник и Прежний оклад // Добавление строки осуществляется предопределенной процедурой // ОбработкаПодбора модуля формы
процедура Подбора( ) перем сСотр_2, контПодбора; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Дата для периодического реквизита Оклад справочника Сотрудники_2 сСотр_2.ИспользоватьДату(РабочаяДата()); // Указываем явно имя основной формы списка для метода ОткрытьПодбор ОткрытьПодбор("Справочник.Сотрудники_2", "ФормаСписка", контПодбора); форма.Очистить.Доступность(1); форма.Удалить. Доступность(1); форма.ОК.Доступность(1); конецПроцедуры // Подбор // Добавляет строку в табличную часть документа, определяя в ней значения // полей Сотрудник и Прежний оклад процедура ОбработкаПодбора(текЭл, конт) // Проверяем, есть ли выбранная запись в табличной части документа ВыбратьСтроки(); пока ПолучитьСтроку() > 0 цикл если Сотрудник = текЭл тогда Предупреждение("Сотрудник уже выбран."); возврат; конецЕсли; конецЦикла; // пока НоваяСтрока(); // Добавляем строку в табличную часть документа Сотрудник = текЭл; ПрежнийОклад - конт.Оклад; // конт - контекст формы списка справочника конецПроцедуры // ОбработкаПодбора // Удаляет все строки табличной части. Связана с кнопкой Очистить диалога формы приказа процедура ОчиститьТабл() если КоличествоСтрок() = 0 тогда Предупреждение("Нет строк для удаления."); возврат; конецЕсли; если Вопрос("Очистить таблицу документа?", "Да+Нет") = "Да" тогда УдалитьСтроки(); // Поскольку в приказе нет сотрудников, следующие кнопки делаем недоступными форма.Очистить.Доступность(0); форма.Удалить.Доступность(0); форма.ОК.Доступность(0); конецЕсли; конецПроцедуры // ОчиститьТабл
// Удаляет текущую строку табличной части документа // Связана с кнопкой Очистить диалога формы приказа процедура УдалитьСтрокуТабл() если КоличествоСтрок() = 0 тогда Предупреждение("Нет строк для удаления."); возврат; конецЕсли; если Вопрос("Удалить текущую строку?", "Да+Нет") = "Да" тогда УдалитьСтроку(); если КоличествоСтрок() = 0 тогда // Поскольку в приказе не осталось сотрудников, // следующие кнопки делаем недоступными форма.Очистить.Доступность(0); форма.Удалить.Доступность(0); форма.ОК.Доступность(0); конецЕсли; конецЕсли; конецПроцедуры // УдалитьСтрокуТабл // Предопределенная процедура. Осуществляет контроль введенных данных процедура ПриЗаписи( ) если (ПустоеЗначение(ДатаНовОклада) = 1) тогда Предупреждение("Определите дату нового оклада."); СтатусВозврата(0); // Не записываем данные // Перемещаемся на элемент диалога ДатаНовОклада Активизировать("ДатаНовОклада", 1); возврат; конецЕсли; если КоличествоСтрок() = 0 тогда Предупреждение("Список сотрудников пуст."); СтатусВозврата(0); // Запрещаем запись документа возврат; конецЕсли; // Проверим, для всех ли сотрудников задан оклад // и отличается ли он от прежнего оклада ВыбратьСтроки(); пока ПолучитьСтроку() > 0 цикл если НовыйОклад = 0 тогда Предупреждение("Сотруднику " + Сотрудник + " не установлен оклад."); СтатусВозврата(0); // Запрещаем запись документа возврат; конецЕсли; если НовыйОклад = ПрежнийОклад тогда Предупреждение("Новый и прежний оклады сотрудника " + РазделительСтрок + Сотрудник +" одинаковы."); СтатусВозврата(0); // Запрещаем запись документа возврат; конецЕсли; конецЦикла; // пока конецПроцедуры // ПриЗаписи
5.8.4.3. МОДУЛЬ ДОКУМЕНТА Содержит предопределенную процедуру ОбработкаПроведения. Проведение или перепроведение проведенного документа выполняется каждый раз при его записи. Процедура ОбработкаУдаленияПроведения может быть опущена, так как при удале нии документа порожденные им новые оклады также будут удаляться. Это обусловлено тем, что они устанавливаются в справочнике Сотрудники_2 с привязкой к док ументу. // Устанавливает значения периодического атрибута Оклад для всех попавших в приказ // сотрудников, а также значение реквизита справочника ПриказОклад процедура ОбработкаПроведения() перем сСотр_2; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Дата для периодического реквизита Оклад справочника Сотрудники_2 сСотр_2.ИспользоватьДату(РабочаяДата()); ВыбратьСтроки(); пока ПолучитьСтроку() > 0 цикл если сСотр_2.НайтиЭлемент(Сотрудник) = 0 тогда Предупреждение("Сотрудник " + Сотрудник + РазделительСтрок + " в справочнике Сотрудники_2 не найден."); продолжить; конецЕсли; // Устанавливаем связь документа с записью справочника Сотрудники_2 сСотр_2.ПриказОклад = ТекущийДокумент(); сСотр_2.3аписать(); // Сохраняем изменения // Записываем периодический реквизит Оклад с привязкой к документу УстановитьРеквизитСправочника(Сотрудник, "Оклад", НовыйОклад, ДатаНовОклада); конецЦикла; // пока // Ограничиваем время показа окна с предупреждением тремя секундами // См. также замечание 1 к процедурам ОбработкаПроведения // и ОбработкаУдаленияПроведения в разд. 5.8.2.3 если Проведен() = 0 тогда // Если документ проводится впервые Предупреждение("Документ проведен.", 3); иначе Предупреждение("Документ перепроведен.", 3); конецЕсли; конецПроцедуры // ОбработкаПроведения 5.9. О Т Б О Р И Ф И Л Ь Т Р А Ц И Я Д А Н Н Ы Х СПРАВОЧНИКА Применяется для вывода в форме списка справочника данных, удовлетворяющих некоторым условиям.
5.9.1. ОТБОР ПО РЕКВИЗИТУ СПРАВОЧНИКА Позволяет, например, в справочнике Сотрудники_2 отобразить только сотрудни ков, имеющих, скажем, высшее образование. Эта возможность предоставляется бла годаря тому, что для реквизита Образование заданы свойства Сортировка и Отбор по реквизиту (рис. 5.29, б). Причем такие свойства можно задать только для неперио дического реквизита. Отбор по реквизиту, не имеющему таких свойств, невозможен.
5.9.1.1. ИНТЕРАКТИВНОЕ УПРАВЛЕНИЕ ОТБОРОМ Отбор данных по реквизиту может быть реализован либо интерактивно, либо про-. граммно. В первом случае следует использовать иконки панели инстру откроет диалог (рис. 5.45), ментов формы списка справочника. Выбор иконки в котором задается значение (критерий) отбора и запускается или отключается отбор.
Рис. 5.45. Отбор по реквизиту Образование Список существующих значений реквизита Образование заполняется данными из справочника Образование_2. Он же является источником данных для поля Значение отбора. Заполнив эти список и поле (или что-нибудь одно) и нажав на кнопку Устано вить отбор, устанавливающую отбор по заданному значению, мы вместо полного списка сотрудников получим отфильтрованный приведенный на рис. 5.46 список (выво дится без существующей иерархии данных).
Рис. 5.46. Сотрудники с высшим образованием После нескольких отборов по разным значениям реквизита (или нескольких ре кви- I зитов) мы имеем возможность установить отбор по списку, выводимому при выборе j отображающей историю отборов (рис. 5.47). иконки
Рис. 5.47. Установка отбора в списке окна История отборов
Замечания: отображаются на панели инструментов формы списка спра
1. Иконки
вочника при наличии в нем реквизитов, имеющих свойство Отоор по реквизиту. 2.
История отборов запоминается со справочником и не может быть отредактирована. 5.9.1.2.
ЗАДАНИЕ ОТБОРА
В ПРОГРАММЕ
Открывая форму списка справочника в программе, используем для задания крите рия отбора метод УстановитьОтбор, который может быть вызван только в модуле формы списка справочника, например в его предопределенной процедуре ПриОткрытии. Также в ней можно задать и закладки отбора, например: // Создана для формы списка справочники Сотрудники_2 // Устанавливает закладки отбора по реквизиту Образование процедура ПриОткрытии() перем обр; обр = СоздатьОбъект("Справочник.Образование_2"); // Ищем начальное образование если обр.НайтиПоНаименованию("Н", 0) = 1 тогда // Второй параметр метода ЗакладкиОтбора должен иметь разновидность типа // Справочник.Образование_2 ЗакладкиОтбора("Образование", обр.ТекущийЭлемент()); ИерархическийСписок(0, 1); // Можно менять режим отображения иначе Предупреждение ("В справочнике Образование_2 нет записи, начинающейся с буквы Н."); конецЕсли; конецПроцедуры // ПриОткрытии Диалог формы,списка справочника С6трудники_2 с закладками отбора по реквизи ту Образование приведен на рис. 5.48.
Рис. 5.48. Закладки отбора по реквизиту Образование
Замечания: 1. Список закладок отбора по реквизиту, если справочник отображается без учета иерархии (не по группам), содержит все существующие значения реквизита (рис. 5.48), включая пустое, и редактированию не подлежит. Так, из него нельзя удалить закладку <Пустое значение> или добавить закладку Все сотрудники. 2. При выводе иерархического списка сотрудников отображаются только закладки, позволяющие отобразить непустой список. Так, если в подразделении 01/1 есть сотрудники только с высшим и средним образованием, то диалог формы списка примет приведенный на рис. 5.49 вид.
Рис. 5.49. Закладки отбора для подразделения 01/1 при выводе данных с учетом иерархии
Закладки отбора исчезнут из диалога, если метод ЗакладкиОтбора вызвать следую щим образом: ЗакладкиОтбора("");
//
Убираем закладки отбора
Таким образом, чтобы отображать диалог с закладками отбора или без них, мы должны иметь возможность в модуле списка справочника вызывать метод ЗакладкиОтбора с аргументами, устанавливающими или убирающими закладки. Чтобы эту возможность реализовать, введем в диалог формы списка справочника Сотрудники_2 флажок Закладки отбора по образованию (рис. 5.50), дадим ему имя закл и свяжем с ним формулу (процедуру) ПоказатьЗакладки, которую, так же как и предопределен ную процедуру ПриОткрытии, разместим в модуле формы списка справочника.
Рис. 5.50. Диалог формы списка справочника Сотрудники_2 с флажком Закладки отбора по образованию Код процедуры ПоказатьЗакладки прост: // Показывает/убирает закладки отбора по образованию процедура ПоказатьЗакладки( ) перем значОтбора; если закл = 1 тогда // Показать закладки значОтбора = ?(ЭтоГруппа() = 0, Образование, ""); ЗакладкиОтбора("Образование", значОтбора);
иначе // ЗакладкиОтбора(""); конецЕсли; конецПроцедуры // ПоказатьЗакладки
Убрать закладки
Замечание. Вместо процедуры ПоказатьЗакладки с флажком закл можно связать формулу ?(закл = 0, ЗакладкиОтбора(""), ЗакладкиОтбора("Образование", ?(ЭтоГруппа() = 0, Образование, ""))) разместив ее в поле Формула окна задания свойств флажка. Код предопределенной процедуры ПриОткрытии можно дополнить следующими операторами: закл = 1 ; // ЗакладкиОтбора("Образование",""); конецПроцедуры // ПриОткрытии
Добавляются в процедуру ПриОткрытии
Также напомним, что модуль формы списка справочника Сотрудники_2 содержит приведенную в разд. 5.8.2.4 предопределенную процедуру ПриНачалеРедактированияСтроки. Помимо рассмотренного метода ЗакладкиОтбора для организации отбора могут быть также употреблены методы УстановитьОтбор, ПолучитьОтбор и ВидыОтбора. Они, так же как и метод ЗакладкиОтбора, могут появляться только в модуле формы списка справочника. Описание методов см. в разд. 5.12.5.
5.9.2. ФИЛЬТРАЦИЯ ДАННЫХ Осуществляется методом ИспользоватьСписокЭлементов, принимающим в качестве параметра список элементов справочника, которые следует отобразить в форме его списка. Заносимые в список значения должны иметь тип Справочник. В случае пустого списка выводятся все данные справочника. Пример. В форме списка справочника Сотрудники_2 отображаются только со трудники, оклад которых больше или равен 2500 руб. // Содержится в модуле формы списка справочника Сотрудники_2 процедура ПриОткрытии() перем сЗнач, сСотр_2; сЗнач = СоздатьОбъект("СписокЗначений"); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Дата для периодического реквизита Оклад сСотр_2.ИспользоватьДату(РабочаяДата()); // Позиционируемся на первой записи справочника Сотрудники_2 сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.0клад >= 2500 тогда // Добавляем в список элемент справочника сЗнач.ДобавитьЗначение(сСотр_2.ТекущийЭлемент()); конецЕсли;
конецЦикла; // пока сСотр_2 = 0; // Задаем фильтр для справочника Сотрудники_2 ИспользоватьСписокЭлементов(сЗнач); конецПроцедуры // ПриОткрытии Замечание. Не удается одновременно применить и отбор и фильтрацию данных.
5.10. АТРИБУТ ВЛАДЕЛЕЦ 5.10.1. СПРАВОЧНИК ДЕТИ Добавим в конфигурацию справочник Дети и назначим ему в качестве владельца справочник Сотрудники_2. В справочнике будем хранить данные о детях, возраст ко торых не более 18 лет. В новом справочнике для наших учебных целей достаточно иметь 3 реквизита (не считая двух, Код и Наименование, заданных по умолчанию): Имя, ДатаРождения и Возраст (рис. 5.51).
Рис. 5.51. Свойства и реквизиты справочника Дети Длину символьных реквизитов Имя и Возраст установим соответственно равной 30 и 10 символам. Реквизит ДатаРождения, понятно, имеет тип Дата. Реквизит Возраст может содержать, например, такие значения: 5 месяцев, 8 лет и т. п. Реквизит Наименование использовать не будем, поэтому сократим его длину до нуля (заданный по умолчанию реквизит удаляется). Длину кода установим равной трем, что вполне достаточно для кода, задаваемого в пределах подчинения (дети лю бого сотрудника будут представляться под номерами 1, 2 и т. д.).
Для отображения данных справочника используем форму списка, представленную на рис. 5.52.
Рис. 5.52. Форма списка справочника Дети
На самом деле для каждого ребенка вводится только дата рождения, а возраст вы числяется. Причем такие вычисления будем выполнять при каждом открытии спра вочника, обновляя значения реквизита Возраст. Если в процессе вычислений обнару жится, что ребенок существенно повзрослел (его возраст превысил 18 лет), то соответ ствующая запись в справочнике Дети помечается для удаления без предупреждений. При вводе и редактировании данных используем диалог, представленный на рис. 5.53.
Рис. 5.53. Диалог формы элемента справочника Дети
Причем поле Возраст сделаем доступным только для чтения. Вычисление возраста будем производить функцией НайтиВозраст, которую свяжем на закладке Дополнительно с полем ДатаРождения. Тогда эта функция будет вызываться каждый раз после смены даты. Добавим в диалог поле дТ, также доступное только для чтения, установив его равным текущей дате, возвращаемой встроенной функцией ТекущаяДата( ). Это поле предназначено для визуального контроля установленной в компью тере текущей даты, от значения которой, в частности, зависит и результат, возвращае мый функцией НайтиВозраст. Теперь ясно, какими процедурами следует снабдить модули форм элемента и списка.
5.10.2. МОДУЛЬ ФОРМЫ ЭЛЕМЕНТА СПРАВОЧНИКА ДЕТИ Содержит функцию НайтиВозраст и предопределенные процедуры ПриОткрытии и ПриЗаписи. Первая устанавливает значение элемента диалога дТ, вторая контроли рует заданные в диалоге значения. процедура ПриОткрытии() дТ = ТекущаяДата(); конецПроцедуры // ПриОткрытии // Возвращает возраст ребенка // Результат имеет вид, например, 4 года или 5 лет, если возраст больше одного года, // или, например, 8 месяцев - в противном случае
функция НайтиВозраст() перем нгр, нгт, нмр, нмт, числоЛет, числоМес, возр; если ПустоеЗначение(ДатаРождения) = 1 тогда Предупреждение("Введите дату рождения."); возврат""; конецЕсли; нгр = ДатаГод(ДатаРождения); // Возвращает год рождения нгт = ДатаГод(дТ); числоЛет = нгт - нгр; если числоЛет > 18 тогда Предупреждение("Возраст ребенка не должен быть более 18 лет."); возврат""; конецЕсли; если числоЛет < 0 тогда Предупреждение("Неверная дата рождения."); возврат ""; конецЕсли; если числоЛет = 0 тогда нмр = ДатаМесяц(ДатаРождения); нмт = ДатаМесяц(дТ); числоМес = нмт - нмр; если числоМес < 0 тогда Предупреждение("Неверная дата рождения."); возврат ""; конецЕсли; если числоМес = 1 тогда возр = " 1 месяц" иначеЕсли (числоМес = 2) или (числоМес = 3) или (числоМес = 4) тогда // Например, 3 месяца возр = строка(числоМес) + " месяца"; иначе // числоМес > 4 или числоМес = О // Например, 7 месяцев возр = строка(числоМес) + " месяцев"; конецЕсли; иначе // числоЛет > 1 если числоЛет = 1 тогда возр = "1 год" иначеЕсли (числоЛет = 2) или (числоЛет = 3) или (числоЛет = 4) тогда // Например, 3 года возр = строка(числоЛет) + " года"; иначе // числоЛет > 4 // Например, 7 лет возр = строка(числоЛет) + " лет"; конецЕсли; конецЕсли; Возраст = возр; возврат возр; конецФункции // НайтиВозраст
процедура ПриЗаписи( ) // Предопределенная процедура если ПустоеЗначение(Возраст) = 1 тогда Предупреждение("Не определен возраст."); СтатусВозврата(0); // Запрещаем запись данных возврат; конецЕсли; если ПустоеЗначение(Имя) = 1 тогда Предупреждение("Введите имя."); СтатусВозврата(0); // Запрещаем запись данных форма.Активизировать(Имя); // Устанавливаем курсор на поле Имя возврат; конецЕсли; если (ПустоеЗначение(Имя) = 1) или (ПустоеЗначение(ДатаРождения) = 1) тогда Предупреждение("Введите дату рождения."); // Устанавливаем курсор на поле ДатаРождения форма.Активизировать(ДатаРождения); СтатусВозврата(0); // Запрещаем запись данных конецЕсли; конецПроцедуры // ПриЗаписи
5.10.3. МОДУЛЬ ФОРМЫ СПИСКА СПРАВОЧНИКА ДЕТИ Содержит предопределенную процедуру ПриОткрытии, вычисляющую и обнов ляющую в результате вызова процедуры ИзменитьВозраст значение реквизита Возраст всех записей справочника Дети. Запись о ребенке старше 18 лет помечается для удале ния. Если форма списка справочника Дети открывается не из справочника-владельца, а, например, встроенной функций ОткрытьФорму, то в качестве владельца формы ус танавливается последний сотрудник из справочника Сотрудники_2, имеющий данные о детях в справочнике Дети. процедура ИзменитьВозраст(Дети, дТ) далее // Обновляет, вызывая процедуру ИзменитьВозраст, // значение реквизита Возраст в справочнике Дети роцедура ПриОткрытии() перем сСотр_2, Дети, послВлад, дТ; дТ = ТекущаяДата(); // Создаем объект Дети. Он необходим, поскольку открываемый справочник Дети // не может быть перепозиционирован Дети = СоздатьОбъект("Справочник.Дети"); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Перемещаемся на первый элемент справочника Сотрудники_2 сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда продолжить; // Пропускаем группы справочника Сотрудники_2 конецЕсли; Дети.ИспользоватьВладельца(сСотр_2.ТекущийЭлемент()); // или проще: Дети.ИспользоватьВладельца(сСотр_2); // Встаем на первой, относящейся к элементу-владельцу записи справочника Дети ,
если Дети.ВыбратьЭлементы( ) = 1 тогда // Используем переменную послВлад при автономном открытии // формы списка справочника Дети послВлад = сСотр_2.ТекущийЭлемент(); конецЕсли; ИзменитьВозраст(Дети, дТ); конецЦикла; // пока сСотр_2.ПолучитьЭлемент() = 1 // Если справочник Дети открывается автономно, например, так: //ОткрытьФорму("Справочник.Дети"); // то владелец отсутствует. Установим в качестве такового // элемент послВлад справочника Сотрудники_2 // Метод формы списка справочника ИспользоватьВладельца, возвращает текущий // элемент-владелец формы списка справочника Дети. Если владельца нет если ПустоеЗначение(ИспользоватьВладельца()) = 1 тогда ИспользоватьВладельца(послВлад); конецЕсли; Дети = 0; сСотр_2 = 0; конецПроцедуры // ПриОткрытии // Вычисляет и обновляет значение реквизита Возраст для записей справочника Дети, // подчиненных ранее заданному владельцу процедура ИзменитьВозраст(Дети, дТ) перем нгр, нгт, нмр, нмт, числоЛет, числоМес; // Выбор элементов справочника Дети, связанных с элементом-владельцем пока Дети.ПолучитьЭлемент() = 1 цикл нгр = ДатаГод(Дети.ДатаРождения); нгт = ДатаГод(дТ); числоЛет = нгт - нгр; если числоЛет > 18 тогда // Проставляем пометку удаления без предупреждения Дети.Удалить(0); конецЕсли; если числоЛет = 0 тогда нмр = ДатаМесяц(Дети.ДатаРождения); нмт = ДатаМесяц(дТ); числоМес = нмт - нмр; если числоМес = 1 тогда Дети.Возраст = "1 месяц" иначеЕсли (числоМес = 2) или (числоМес = 3) или (числоМес = 4) тогда // Например, 3 месяца Дети.Возраст = строка(числоМес) + " месяца"; иначе // числоМес > 4 или числоМес = 0 // Например, 7 месяцев Дети.Возраст = строка(числоМес) + " месяцев"; конецЕсли; иначе // числоЛет > 1 если числоЛет = 1 тогда Дети.Возраст = "1 год"
иначеЕсли (числоЛет = 2) или (числоЛет = 3) или (числоЛет = 4) тогда // Например, 3 года Дети.Возраст = строка(числоЛет) + " года"; иначе // числоЛет > 4 // Например, 7 лет Дети.Возраст = строка(числоЛет) + " лет"; конецЕсли; конецЕсли; Дети.3аписать( ); // Не забываем сохранить данные конецЦикла; // пока Дети.ПолучитьЭлемент() = 1 конецПроцедуры // ИзменитьВозраст Вычисление и обновление реквизита Возраст можно было бы выполнять только для конкретного владельца в предопределенной процедуре ПриВыбореВладельца. Тог да в предопределенной процедуре ПриОткрытии подчиненного справочника достаточ но модифицировать реквизит Дети.Возраст только для записей, подчиненных элемен ту-владельцу, который возвращается методом ИспользоватьВладельца. При таком подходе модуль формы списка справочника будет иметь следующий код: процедура ИзменитьВозраст(Дети, дТ) далее процедура ПриВыбореВладельца(влад) далее процедура ПриОткрытии() перем влад; влад = ИспользоватьВладельца(); // Получаем владельца при открытии // Вычисляем и обновляем значение реквизита Возраст для записей справочника Дети, // подчиненных владельцу влад ПриВыбореВладельца(влад); конецПроцедуры // ПриОткрытии // Вызывает процедуру ИзменитьВозраст, которая вычисляет и обновляет // значение реквизита Возраст для записей справочника Дети, подчиненных владельцу влад процедура ПриВыбореВладельца(влад) // Предопределенная процедура перем Дети, дТ; // Запускается при смене элемента-владельца дТ = ТекущаяДата(); // Создаем объект Дети. Он необходим, поскольку открываемый справочник Дети // не может быть перепозиционирован Дети = СоздатьОбъект("Справочник.Дети"); Дети.ИспользоватьВладельца(влад); // Встаем на первой, относящейся к элементу-владельцу записи справочника Дети если Дети.ВыбратьЭлементы() = 1 тогда ИзменитьВозраст(Дети, дТ); конецЕсли; Дети = 0; конецПроцедуры // ПриВыбореВладельца процедура ИзменитьВозраст(Дети, дТ) // Текст процедуры см. выше конецПроцедуры // ИзменитьВозраст
5.10.4. ЗАПОЛНЕНИЕ СПРАВОЧНИКА ДЕТИ Введем несколько значений в справочник Дети в интерактивном режиме. Для это го откроем справочник Сотрудники 2, которому справочник Дети подчинен, выберем сотрудника, например Горюнову Ульяну, и воспользуемся иконкой , позволяющей отобразить форму списка справочника Дети. Вызвав дважды форму элемента этого справочника (рис. 5.54), введем данные о детях (рис. 5.55).
Рис. 5.54. Заполненный диалог формы элемента справочника Дети Из рис. 5.55 видно, что в подчиненном справочнике Дети отображаются только строки, содержащие данные о детях сотрудника, выбранного в справочнике-владельце Сотрудники_2.
Рис. 5.55. Ульяна Горюнова и ее дети В информационной базе для справочника Дети будет создан файл, возможно SC4233.DBF, имеющий структуру, представленную в табл. 5.6. Таблица 5.6 Структура DBF-файла справочника Дети Id Code 1 1 2 1 3 2
Descr
Parentext Ismark A
VerStamp
Sp4237 Васильева Лена Горюнов Алеша Горюнова Маша
Sp4235 Sp4236 09/21/1999 2 года 06/12/2001 5 месяцев 11/12/1998 3 года
1Q 1Q Справочник Дети связан со справочником-владельцем (справочником Сотрудники_2) через атрибут Владелец; связь между записью владельца и записями подчинен ного справочника фиксируется соответственно в полях Id и Parentext их DBF-таблиц. В частности, значения поля Id Горюновой Ульяны Валерьевны в справочнике Сотрудники_2 и полей Parentext ее детей в справочники Дети совпадают и равны 1Q.
5.10.5. ВЫБОР ДАННЫХ ПОДЧИНЕННОГО СПРАВОЧНИКА При выборе данных из справочника Дети предварительно надо указать, сослав шись на справочник Сотрудники_2, сотрудника, детей которого мы хотим выбрать. Такая привязка к сотруднику осуществляется методом ИспользоватьВладельца. Пример. Обработкой Проба выводятся сообщения о детях Горюновой Ульяны. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, Дети, род; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // 2010 - код Горюновой Ульяны. Выполняем поиск во всем справочнике если сСотр_2.НайтиПоКоду(2010, 0) = 0 тогда Предупреждение("Сотрудник не найден."); возврат; конецЕсли; род = сСотр_2; // Родитель // или род = сСотр_2.ТекущийЭлемент(); Дети = СоздатьОбъект("Справочник.Дети"); // Фиксируем сотрудника, для которого выводятся данные о его детях Дети.ИспользоватьВладельца(род); Дети.ВыбратьЭлементы(); // Перемещаемся на начало справочника Дети // Заголовок таблицы результатов Сообщить(сСотр_2.Наименование + ". Ее дети:"); // Метод ПолучитьЭлемент выбирает очередной элемент справочника, // связанный с записью-владельцем пока Дети.ПолучитьЭлемент() = 1 цикл Сообщить(Дети.Имя + СимволТабуляции + Дети.Возраст); конецЦикла // пока конецПроцедуры // Выполнить Результат: Горюнова Ульяна Валерьевна. Ее дети: Горюнов Алеша 5 месяцев Горюнова Маша 3 года
5.11. ПРОСТОЙ ОТЧЕТ ДЛЯ СПРАВОЧНИКА 5.11.1. ДИАЛОГ И ТАБЛИЦА ОТЧЕТА Сформируем отчет, содержащий данные о сотрудниках справочника Сотрудники_2, имеющих заданное образование обр и оклад, не меньший заданной вели чины окл. Вывод осуществим с разбивкой по цехам и их подразделениям. В преде лах каждого подразделения данные отобразим в алфавитном порядке фамилий сот рудников. Для задания значений переменных обр и окл диалог обработки Проба приведем к представленному на рис. 5.56 виду.
Рис. 5.56. Диалог отчета по справочнику Сотрудники _2 Переменная обр имеет разновидность типа Справочник.Образование_2, а перемен ная окл - числовой тип формата 10.2. В таблице отчета будем выводить отображенные на рис. 5.57 сведения.
Рис. 5.57. Макет отчета о сотрудниках В секциях заг, подр и датаОтч тип выводимых сообщений - шаблон, в секции сотр - выражение. Читателям, испытывающим затруднения при построении таблиц отчета, рекомендуем вновь обратиться к разд. 1.9, в котором конструируется отчет о непериодических константах.
5.11.2. МОДУЛЬ ОТЧЕТА Модуль отчета, как это уже повелось, содержит связанную с кнопкой Пуск проце дуру Выполнить, извлекающую на этот раз данные о сотрудниках и направляющую их в только что построенную таблицу Сотрудники_2. Кроме процедуры Выполнить, включим в модуль отчета предопределенную процедуру ПриОткрытии, устанавли вающую начальные значения переменных диалога обр и окл. Вывод секции подр бу дем осуществлять, если в подразделении есть хотя бы один сотрудник, удовлетворяю щий условиям, заданным переменными обр и окл. Для сокращения кода разместим его часть в функции ЕслиПродолжить. Алгоритм формирования отчета: 1.
Начало.
2.
Создать объект сСотр_2, имеющий разновидность типа Справочник.Сотрудники_2.
3.
Установить дату для периодического реквизита Оклад.
4.
Открыть выборку из справочника Сотрудникик_2.
5.
Если текущий элемент - это группа, то найти, нужно ли выводить секцию подр. Критерий прост: если в группе есть хотя бы один сотрудник, удовлетворяющий условиям, заданным переменными обр и окл, то ее имя выводится в секции подр. Ответ на вопрос, нужно ли выводить имя группы (подразделения), возвращает
функция НайтиФлагПодр. Предварительно, когда найдено первое подразделение (числоПодр = 1), создается объект табл, имеющий тип Таблица, и выводится за головок отчета - секция заг. 6.
Если текущий элемент - это сотрудник, то вывести секцию сотр, если сотрудник удовлетворяет заданным условиям, или перейти на начало цикла - в противном случае.
7.
После завершения выборки, если в отчете есть данные о сотрудниках (числоПодр > 0), вывести дату отчета и показать отчет, иначе вывести предупреждение о том, что отчет пуст.
8.
Конец.
// Установим начальные значения переменных диалога обр и окл процедура ПриОткрытии() перем видОбр; ОчиститьОкноСообщений(); видОбр = СоздатьОбъект("Справочник.Образование_2"); видОбр.ВыбратьЭлементы(); видОбр.ПолучитьЭлемент(); // Используем в качестве начального значения переменной обр // первый элемент справочника Образование_2 обр = видОбр.ТекущийЭлемент(); окл = 2000.0 // руб. конецПроцедуры // ПриОткрытии функция ЕслиПродолжить(сСотр_2) далее функция НайтиФлагПодр(кодПодр) далее процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, сотр, код, дП, ставка, подр, текПодр; перем табл, числоПодр; // Создаем объекты сСотр_2 и табл сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Дата для периодического реквизита Оклад справочника Сотрудники_2 сСотр_2.ИспользоватьДату(РабочаяДата()); // Позиционируемся на первой записи справочника Сотрудники_2 сСотр_2.ВыбратьЭлементы(1); // Задаем выборку с учетом иерархии числоПодр = 0; // Число выбранных подразделений // Включаем в выборку все подчиненные элементы пока сСотр_2.ПолучитьЭлемент(1)= 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда // Найдем, нужно ли выводить секцию подр если НайтиФлагПодр(сСотр_2.Код) = 0 тогда продолжить; // В этом подразделении нет сотрудников, иначе // удовлетворяющих условиям обр и окл числоПодр = числоПодр + 1; // Если есть хотя бы один сотрудник, удовлетворяющий заданным условиям, // то начинаем формировать тотчет
если числоПодр = 1 тогда табл = СоздатьОбъект("Таблица"); // Свяжем переменную табл с таблицей Сотрудники_2, // содержащей макет отчета табл.ИсходнаяТаблица("Сотрудники_2"); // При выводе применяем заданные по умолчанию параметры таблицы; // для их изменения следует обратиться к методу Опции табл.ВывестиСекцию("заг"); // Выводим секцию заг конецЕсли; подр = сСотр_2.Наименование; табл.ВывестиСекцию("подр"); продолжить; конецЕсли; конецЕсли; если ЕслиПродолжить(сСотр_2) = 1 тогда продолжить; // Переход на начало цикла конецЕсли; сотр = сСотр_2.Наименование; код = сСотр_2.Код; если ПустоеЗначение(сСотр_2.ПриказПрием) = 0 тогда // Дату трудоустройства берем из соответствующего приказа о приеме на работу дП = сСотр_2.ПриказПрием.ДатаПриема; иначе дП = "-"; конецЕсли; ставка = сСотр_2.0клад; // Вывод очередной строки в отчет табл.ВывестиСекцию("сотр"); конецЦикла; // пока если числоПодр = 0 тогда Предупреждение("Сотрудников, отвечающих заданным условиям, нет."); иначе // В отчете есть сотрудники табл.ВывестиСекцию("датаОтч"); // Дата отчета // Запрещаем редактирование результирующей таблицы табл .ТолькоПросмотр(1); // Задаем в методе Показать заголовок окна с результирующей таблицей табл.Показать("Выборка из справочника Сотрудники_2"); конецЕсли; конецПроцедуры // Выполнить. Результат приведен в табл. 5.7 // Возвращает 1, если сотрудник не удовлетворяет требованиям обр и окл функция ЕслиПродолжить(сСотр_2) если (сСотр_2.Оклад < окл) или (сСотр_2.Образование <> обр) тогда возврат 1; // Условия, заданные в диалоге, не выполняются иначе возврат 0; // Цикл не прерывается конецЕсли; конецФункции // ЕслиПродолжить // Функция НайтиФлагПодр вернет 1, если в подразделении есть // хотя бы один сотрудник, удовлетворяющий условиям,
// заданным переменными обр и окл, или 0 -в противном случае Функция НайтиФлагПодр(кодПодр) перем сСотр_2а; сСотр_2а = СоздатьОбъект("Справочник.Сотрудиики_2"); // Дата для периодического реквизита Оклад справочника Сотрудники_2 сСотр_2а.ИспользоватьДату(РабочаяДата()); // Найдем группу. Ищем во всем справочнике сСотр_2а.НайтиПоКоду(кодПодр, 0); // Задаем родителя, в пределах которого будет осуществляться последующая выборка сСотр_2а.ИспользоватьРодителя(сСотр_2а.ТекущийЭлемент()); // или проще: сСотр_2а.ИспользоватьРодителя(сСотр_2а); сСотр_2а.ВыбратьЭлементы(1); // Задаем выборку с учетом иерархии // Включаем в выборку все подчиненные элементы пока сСотр_2а.ПолучитьЭлемент(1) = 1 цикл если ЕслиПродолжить(сСотр_2а) = 0 тогда возврат 1; // Сотрудник, подходящий под условия конецЕсли; // обр и окл, найден конецЦикла; возврат 0; // Нет нужного сотрудника конецФункции // НайтиФлагПодр Таблица 5.7 Сотрудники, имеющие высшее образование и оклад, не меньший 2000 руб. Сотрудник Подразделение 01 Цех Подразделение 01/1 Агальцов Юрий Алексеевич Подразделение 01 / 2 Кузьмина Раиса Николаевна Подразделение 02 Цех Абрамова Лариса Сергеевна Горюнова Ульяна Валерьевна Дата отчета 29.11.01
Дата трудоустройства
Код
Оклад
111
-
2900
122
-
2700
201 2010
20.11.01 -
2000 2700
5.11.3. МОДУЛЬ ОТЧЕТА С ЗАПРОСОМ Длинный код по выбору данных принято заменять запросами к файлам базы данных. В СУБД общего назначения, например FoxPro, запросы не только компактнее по коду, но и эффективнее по быстродействию. В 1С последнее качество запросов не столь очевидно. Результатом выполнения запроса является таблица, содержащая отобранные за просом данные. На основе этой таблицы можно, например, создать отчет, выполнить требуемые вычисления или иные предусмотренные алгоритмом действия. Запрос 1С - это объект, например запС, появляющийся в программе в результате присваивания запС = СоздатьОбъект("Запрос");
Содержание запроса оформляется в 1С, как правило, в виде длинной, расположен ной на нескольких строчках символьной именованной константы, например текстЗапС, которая затем употребляется в качестве параметра метода Выполнить: флаг = запС.Выполнить(текстЗапС); Метод Выполнить вернет 1 при успешном выполнении запроса или 0 -в случае неудачи. Оставим диалог обработки Проба без изменений, но удалим из ее модуля ранее внесенный в него код (кроме процедуры ПриОткрытии) и заменим его на нижеприво димые процедуры и функции. Первая, имеющая имя ЗапрСотр, выполняет запрос к справочнику Сотрудники_2, выбирая в результате данные, удовлетворяющие пере численным в разд. 5.11.1 критериям. Процедура ВывТабСотр формирует, используя макет таблицы Сотрудники_2 (см. рис. 5.57) обработки Проба, отчет. Разумеется, если в диалоге (см. рис. 5.56) для переменной обр задано значение Высшее, а для перемен ной окл - 2000, то итоговый отчет полностью совпадет с результатом, содержащимся в табл. 5.7. Назначение иных программных компонентов модуля обработки Проба будет по ясняться сопровождающим их комментарием. // Установим начальные значения переменных диалога обр и окл процедура ПриОткрытии() перем видОбр; ОчиститьОкноСообщений(); видОбр = СоздатьОбъект("Справочник.Образование_2"); видОбр.ВыбратьЭлементы(); видОбр.ПолучитьЭлемент(); // Используем в качестве начального значения переменной обр // первый элемент справочника Образование_2 обр = видОбр.ТекущийЭлемент(); окл = 2000.0 // руб. конецПроцедуры // ПриОткрытии // Формируем и выполняем запрос по сотрудникам, // имеющим образование обр и оклад, не меньший окл // Функция принимает и возвращает параметр запС типа Запрос функция ЗапрСотр(запС) Перем текстЗапС; // Содержание запроса перем рабДат; рабДат = РабочаяДата(); текстЗапС = " | период с рабДат по рабДат; // Дата для периодического реквизита Оклад // Переменные запроса | род = Справочник.Сотрудники_2.ТекущийЭлемент.Родитель; | имяСотр = Справочник.Сотрудники_2.Наименование; | код = Справочник.Сотрудники_2.Код; | образование = Справочник.Сотрудники_2.Образование; | оклад = Справочник.Сотрудники_2.Оклад; // Задаем порядок выборки данных | группировка род;
| группировка имяСотр; | условие ((образование = обр) и (оклад >= окл));"; // Выполняем запрос и возвращаем 1 в случае удачи, или 0, если есть проблемы возврат запС.Выполнить(текстЗапС); конецФункции // ЗапрСотр функция НайтиДП(сСотр 2, код) далее // Выводит данные в таблицу Сотрудники_2 обработки Проба процедура ВывТабСотр(запС) перем табл; // Создаем объекты сСотр_2 и табл // Объект сСотр_2 создается для функции НайтиДП, осуществляющей поиск // даты приема на работу сотрудника сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); табл = СоздатьОбъект("Таблица"); // Свяжем переменную табл с таблицей Сотрудники_2, содержащей макет отчета табл.ИсходнаяТаблица("Сотрудники_2"); // При выводе применяем заданные по умолчанию параметры таблицы; // для их изменения следует обратиться к методу Опции табл.ВывестиСекцию("заг"); // Выводим секцию заг // Вывод запроса в таблицу Сотрудники_2 пока запС.Группировка("род") = 1 цикл подр = запС.Род; табл.ВывестиСекцию("подр"); пока запС.Группировка("имяСотр") = 1 цикл сотр = запС.ИмяСотр; код = запС.Код; дП = НайтиДП(сСотр_2, код); ставка = запС.Оклад; // Вывод очередной строки в отчет табл.ВывестиСекцию("сотр"); конецЦикла; // пока конецЦикла; // пока табл.ВывестиСекцию("датаОтч"); // Дата отчета // Запрещаем редактирование результирующей таблицы табл.ТолькоПросмотр(1); // Задаем в методе Показать заголовок окна с результирующей таблицей табл.Показать("Выборка из справочника Сотрудники_2"); конецПроцедуры // ВывТабСотр // Находит, если есть ссылка на приказ о приеме на работу, дату трудоустройства сотрудника функция НайтиДП(сСотр 2, код) сСотр_2.НайтиПоКоду(код, 0); // Ищем во всем справочнике // Вернем либо дату приказа о приеме на работу, либо символ -, если // в рассматриваемой записи справочника Сотрудники_2 нет ссылки на приказ возврат ?(ПустоеЗначение(сСотр_2.ПриказПрием) = 0, сСотр_2.ПриказПрием.ДатаПриема,"-"); конецФункции // НайтиДП
процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем запС, тЗнач; // Создаем объекты запС и тЗнач запС = СоздатьОбъект("Запрос"); если ЗапрСотр(запС) = 0 тогда возврат; // Запрос не выполнен конецЕсли; // Если в отчете нет сведений о сотрудниках если запС.Группировка("род") = 0 тогда Предупреждение("Сотрудников, отвечающих заданным условиям, нет."); возврат; иначе запС.ВНачалоВыборки(); конецЕсли; // Создаем объект тЗнач для промежуточной демонстрации выборки запроса тЗнач = СоздатьОбъект("ТаблицаЗначений"); // Выгружаем все переменные запроса в таблицу значений тЗнач // для его предварительного просмотра запС.Выгрузить(тЗнач, 1); // Просмотр таблицы значений (результатов запроса) тЗнач.ВыбратьСтроку(, "Запрос в таблице значений"); // Выводим выборку запроса в таблицу Сотрудники_2 ВывТабСотр(запС); конецПроцедуры // Выполнить Результат приведен в табл. 5.7. Сделаем некоторые пояснения. Результатом запроса является выборка, являющаяся последовательностью записей. Фактически выборка является таблицей, в столбцах которой расположены значения заданных в тексте запроса переменных - в нашем случае переменных родг имяСотр, код, образование и оклад. В общем случае выборка состоит из нескольких групп, зада ваемых в тексте запроса оператором Группировка. Быстрый просмотр выборки запроса можно организовать, выполнив два следую щих метода: // Выгружаем все переменные запроса в таблицу значений тЗнач запС.Выгрузить(тЗнач, 1); // Просмотр таблицы значений тЗнач.ВыбратьСтроку(, "Запрос в таблице значений"); Результат см. на рис. 5.58.
Рис. 5.58. Запрос запС, отображенный в таблице значений тЗнач
Просмотр таблицы показывает, что в ней расположены все нужные нам данные (кроме даты приказа о приеме на работу), причем сгруппированные и упорядоченные надлежащим образом. Объем выбираемых данных, способ группировки и их порядок определяется текстом запроса. Текст запроса текстЗапС содержит, во-первых, дату, задаваемую строкой "период с рабДат по рабДат". Дата в этом запросе нужна только для определения значения пе риодического реквизита Оклад и играет ту же роль, что и метод ИспользоватьДату при простой выборке из справочника. Далее перечисляются реквизиты, значения которых необходимо разместить в запросе (в результирующей таблице), и порядок выборки, устанавливаемый операторами Группировка. Критерий выбора данных задается опе ратором Условие. Последовательность операторов Группировка, например | группировка род; | группировка имяСотр; приводит к созданию вложенных групп данных. Так, в нашем случае употребленные группировки отображают имеющуюся в справочнике Сотрудники_2 иерархию данных. В тексте запроса можно размещать внешние по отношению к запросу переменные, но только те, к которым есть доступ в программном компоненте, содержащем команду выполнения запроса // Должны быть видны имеющиеся в тексте запроса переменные рабДат, обр и окл запС.Выполнить(текстЗапС); Поскольку в тексте запроса нельзя разместить строку "дП = Справочник.Сотрудники_2.ПриказПрием.ДатаПриема;" задающую выборку (эти ограничения накладывает 1С), то для получения даты приема сотрудника на работу в код введена функция НайтиДП, работающая со специально созданным для нее объектом сСотр_1, имеющим разновидность типа Справочники.Сотрудники_2. Так как текст запроса является символьной переменной, то для его формирования можно использовать несколько строк, объединяя их при помощи употребляемой со строками операцией конкатенации (сложения). Например: // Дата для периодического реквизита Оклад датаЗапр = "период с рабДат по рабДат;"; перемЗапр = " // Переменные запроса | род = Справочник.Сотрудники_2.ТекущийЭлемент.Родитель; | имяСотр = Справочник.Сотрудники_2.Наименование; | код = Справочник.Сотрудники_2.Код; | образование = Справочник.Сотрудники_2.Образование; | оклад = Справочник.Сотрудники_2.Оклад;"; групЗапр = "группировка род; // Группы запроса | группировка имяСотр;"; услЗапр = " // Условие выборки | условие ((образование = обр) и (оклад >= окл));"; // Формируем текст запроса текстЗапС = датаЗапр + перемЗапр + групЗапр + услЗапр;
Для получения отчета, отображенного в табл. 5.7, можно, поскольку мы выгрузили запрос в таблицу значений тЗнач, было бы употребить следующий код: // Позиционируемся перед первой строкой таблицы значений тЗнач тЗнач.ВыбратьСтроки(); // Перебор строк таблицы значений начинается с ее первой строки пока тЗнач.ПолучитьСтроку() = 1 цикл подр = тЗнач.Род; если ПустоеЗначение(подр) = 1 тогда // Ничего не вводим продолжить; // Переход на начало цикла конецЕсли; сотр = тЗнач.ИмяСотр; // Вывод очередной строки в отчет если ПустоеЗначение(сотр) = 1 тогда // Строка содержит имя подразделения табл.ВывестиСекцию("подр"); иначе // Строка содержит данные о сотруднике код = тЗнач.Код; дП = НайтиДП(сСотр_2, код); ставка = тЗнач.Оклад; табл.ВывестиСекцию("сотр"); конецЕсли; конецЦикла; // пока Однако для перебора элементов выборки, созданной запросом, в большинстве случаев удобнее пользоваться методом Группировка, принимающим имя группы и по лучающим очередное значение выборки в заданной группе. Поскольку сразу после выполнения запроса он позиционируется перед первой своей записью, то первое упот ребление метода Группировка с именем старшей группы, например запС.Группировка("род"); обеспечит перемещение на первую запись выборки. Перебор записей начинается с первой старшей группы. После ее выбора можно пере брать принадлежащие ей записи, вновь употребив метод Группировка, но передав ему в качестве параметра имя группы, расположенной на следующем уровне иерархии выбор ки. Метод Группировка вернет 0, если либо выбрана запись другого уровня иерархии вы борки, либо выборка исчерпана. В противном случае Группировка возвращает число 1. Понятно, что для перебора более всего подходит управляющая конструкция - цикл Пока, которую мы и применили в вышеприведенной процедуре Выполнить. Поскольку наш запрос имеет две вложенные группы, род и имяСотр, то для перебора их записей пришлось использовать вложенные конструкции Пока. Если нужно вывести только значения старшей группы, то достаточно одного цикла, например такого: пока запС.Группировка("род") = 1 цикл подр = запС.Род; табл.ВывестиСекцию("подр"); конецЦикла; // пока Нам уже понятно, что доступ к отдельному компоненту записи выборки запроса осуществляется по известной схеме - <имя запроса>.<имя переменной запроса>, на пример Сообщить(запС.ИмяСотр);
// Печатаем имя сотрудника
5.12. М Е Т О Д Ы И П Р Е Д О П Р Е Д Е Л Е Н Н Ы Е П Р О Ц Е Д У Р Ы СПРАВОЧНИКОВ Управление справочником осуществляется через его методы, которые можно раз делить на 4 группы: 1. Методы Получить и Установить периодических реквизитов справочника. 2.
Общие методы справочника; они могут быть вызваны как в модулях его форм, так и в модулях, где создан объект типа Справочник, например сСотр_2. В первом случае методы вызываются без префикса, например Записать( ), во втором - им предваряются, например сСотр_2.3аписать( ). Не все методы этой группы будут, однако, работать в форме элемента или списка справочника. Так, метод ВыбратьЭлементы, будучи вставленным в модуль формы элемента справочника и исполь зованным для текущего справочника (записывается без префикса), синтаксический контроль пройдет, но при вызове возникнет ошибка, сопровождаемая сообщением ВыбратьЭлементы(); Объект не может быть перепозиционирован! Но мы уже знаем: для того, чтобы эту ошибку преодолеть, нужно создать объект типа Справочник, например сСотр_2, соответствующей разновидности и употре бить с ним этот метод: сСотр_2.ВыбратьЭлементы();
3.
Методы, используемые только в форме элемента, группы или списка справочника, например ПросмотрИстории. Их действие распространяется или на текущий эле мент, если метод вызван в форме элемента, или на текущий справочник.
4.
Методы, используемые только в форме списка справочника, например Сортиров ка. Их действие распространяется на текущий справочник.
5.12.1. МЕТОДЫ ПЕРИОДИЧЕСКИХ РЕКВИЗИТОВ Метод Получить имеет следующий синтаксис: значение = <спрЭлем>.<пРекв>.Получить([<датаПРекв>]);
Возвращает значение периодического реквизита пРекв для элемента справочника спрЭлем на заданный параметром датаПРекв момент времени. Если параметр датаПРекв опущен, то по умолчанию вместо него используется либо точка актуальности (ТА), если используется компонент Оперативный учет, либо рабочая дата - в против ном случае. Пояснение. Понятие ТА вводится в 1С для объектов типа Регистры. Такие объекты накапливают данные, например остатки или обороты ресурсов. Для первой цели при меняются регистры остатков, для второй - оборотные регистры. Так вот, ТА - это момент времени, на который зафиксированы значения всех регистров остатков. Метод Получить может быть использован как в модулях форм элемента и списка справочника, так и с объектами типа Справочник, возвращаемыми, например, функци ей СоздатьОбъект или определенными в качестве реквизитов других объектов, напри мер документов.
Пример. Методом Получить возвращаются на рабочую дату оклады сотрудников из справочника Сотрудники_2. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, рабДат, окл; ОчиститьОкноСообщений(); // Создаем объект сСотр_1 сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); рабДат = РабочаяДата(); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда продолжить; конецЕсли; окл = сСотр_2.0клад.Получить(рабДат); //или // окл = сСотр_2.ТекущийЭлемент( ).Оклад.Получить(рабДат); Сообщить(СокрП(сСотр_2.Наименование) + Символ Табуляции + окл); конецЦикла; // пока конецПроцедуры // Выполнить Фрагмент возможного результата: Горюнова Ульяна Валерьевна Костина Ольга Владимировна Куприкова Людмила Сергеевна
2700 2900 2100
Замечание. Метод Получить (Установить) нельзя одновременно употреблять с ме тодом ИспользоватьДату для одного и того же объекта типа Справочник. Метод Установить имеет следующий синтаксис: <спрЭлем>.<пРекв>.Установить(<датаПРекв>, <значПРекв>); Устанавливает значение периодического реквизита пРекв элемента справочника спрЭлем, равное величине, возвращаемой выражением значПРекв, на дату, заданную параметром датаПРекв. Используется только с объектами, созданными функцией СоздатьОбъект. Пример. Сотрудникам из справочника Сотрудники_2, ставка которых меньше 2900 руб., начиная с текущей даты повышается оклад на 500 руб. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, текДат, окл, новОкл; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); текДат = ТекущаяДата( ); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл окл = сСотр_2.0клад.Получить(текДат); если (сСотр_2.ЭтоГруппа( ) = 1) или (окл >= 2900) тогда продолжить; конецЕсли;
// //
Пропускаем группы и сотрудников с "большим" окладом
// Повышение оклада сСотр_2.0клад.Установить(текДат, окл + 500); новОкл = сСотр_2.0клад.Получить(текДат); Сообщить(СокрП(сСотр_2. Наименование) + СимволТабуляции + новОкл); конецЦикла; // пока конецПроцедуры // Выполнить Фрагмент
результата:
Горюнова Ульяна Валерьевна Куприкова Людмила Сергеевна
3200 2600
Замечания: 1. Метод Установить меняет значение периодического реквизита справочника без последующего применения метода Записать. 2. Для метода Установить недопустим вызов типа сСотр_2.ТекущийЭлемент( ).Оклад.Установить(текДат, новОкл);
//
Это ошибка
хотя подобный вызов вполне применим с методом Получить: сСотр_2.ТекущийЭлемент().Оклад.Получить(рабДат); 3. Повышение оклада выполнено в обход документа Приказ об изменении оклада. Это зафиксировано и в истории периодического реквизита Оклад, например для Горюновой У. В. (рис. 5.59).
Рис. 5.59. История изменения периодического реквизита Оклад Обратим внимание, что факт отсутствия документа фиксируется в первом столбце таблицы с историей иконкой На практике, однако, такие неформальные мани пуляции с окладами (и многими иными реквизитами) нежелательны и их лучше осуществлять на основе соответствующих документов. При этом взамен метода Установить в модуле документа следует использовать метод УстановитьРеквизитСправочника, записывающий значение периодического реквизита справочника с привязкой к документу.
5.12.2. ОБЩИЕ МЕТОДЫ СПРАВОЧНИКОВ Приведем их в табл. 5.8. После таблицы расположим примеры, эти методы иллю стрирующие. Напомним, что методы-функции могут быть вызваны и как процедуры, то есть в виде отдельных операторов.
Метод видCnp1 = спр.Вид([видСпр2]);
предВида = спр.ПредставлениеВида(); ур = спр.Уровень(); спр.УстановитьАтрибут(рекв, знач);
знач = спр.ПолучитьАтрибут(рекв);
Описание Возвращает вид справочника. Также может быть упот реблен для задания вида видСпр2 объекта типа Спра вочник неопределенного вида, то есть созданного так: спр = СоздатьОбъект("Справочник"); Возвращает представление вида справочника спр, то есть либо синоним справочника (рис. 5.5), либо его идентификатор, если синоним не задан Возвращает номер уровня текущего элемента справочника спр Устанавливает значение непериодического реквизита рекв равным величине знач (напомним, реквизиты являются атрибутами справочника). Для сохранения изменений нужно вызвать метод Записать Возвращает значение реквизита рекв текущей записи справочника
флаг = спр.ЭтоГруппа();
Вернет 1, если текущий элемент является группой, или 0 -в про
флаг = спр.ПринадлежитГруппе (группа); флаг = спр.Выбран();
Вернет 1, если текущий элемент принадлежит к группе группа, или 0 -в противном случае Возвращает 1, если справочник позиционирован на своей записи, или 0 -в противном случае Открывает диалог с заголовком заг, использующий форму списка, заданную параметром формаСписка, для выбора элемента справочника. После закрытия диалога, если элемент выбран, вернет 1 и позиционирует справочник на этом элементе. Если элемент не выбран, метод вернет 0. Если формаСпис ка = "", то употребляется форма списка для выбора. В открытом диалоге курсор позиционируется на текущем элементе справочника. Диалог выбора элемента вызывается и для элементов диалога типа Справочник, расположенных в формах отчетов, справочников, журналов и документов Устанавливает для справочника спр, вид которого не определен, виды для выбора методом Выбрать и возвращает строку, содержащую ранее установленные виды для выбора. Может быть также употреблен и для реквизита типа Справочник неопределенного вида, являющегося компонентом иного объекта. Новые виды для выбора задаются в виде строки, содержащей разделенные запятыми идентификаторы справочников, например видыНое = "Сотрудники_2, Образование_2"; Если видыНое содержит более одного идентификатора, то метод Выбрать активизирует диалог, предлагающий
*флаг - спр.Выбрать(заг, формаСписка);
Метод
Описание выбрать один из справочников (рис. 5.59). Если видыНов = "" или метод ВидыДляВыбора не употреблены вовсе, то метод Выбрать вызовет диа лог, подобный изображенному на рис. 5.60, с полным списком справочников конфигурации
выбТек = спр.ВыборГруппы ([выбНов]);
Устанавливает режим выбора групп для метода Выбрать и для элементов диалога типа Справочник. Группы можно выбирать в диалоге, если выбНов = 1, и нельзя - в противном случае. По умолчанию выбор групп разрешен для элемента диалога типа Справочник в форме обработки (отчета) и запрещен, если диалог выбора активизируется методом Выбрать и если элемент диалога типа Справочник размещен в формах документов, журналов или справочников. Метод возвращает существовавший до его вызова режим выбора групп. Если параметр выбНов опущен, то режим выбора групп не меняется
элем = спр.ТекущийЭлемент();
Возвращает значение текущего элемента справочника. Возвращаемая величина имеет тип Справочник с разновидностью, возвращаемой методом Вид
полнКод = спр.ПолныйКод();
Возвращает строку, содержащую полный код текущего элемента справочника
полнИмя = Возвращает строку, содержащую полное спр.ПолноеНаименование(); наименование текущего элемента справочника *флаг = спр.НайтиЭлемент(спрЭлем); Ищет элемент спрЭлем справочника и позиционирует справочник на элементе и возвращает 1, если поиск удачен, или не меняет позицию справочника и возвращает 0 в противном случае. Элемент спрЭлем должен иметь тип Справочник *флаг = спр.НайтиПоКоду(код), [режим]);
Вернет 1 и переместит позицию справочника на элемент с кодом (или полным кодом) код, если такой элемент найден; в противном случае вернет 0, позиция справочника сохранится. Поиск выполняется, если режим равен: • •
0, во всем справочнике; 1, в пределах родителя, заданного методом ИспользоватьРодителя, либо среди элементов уровня 1, если родитель явно не задан; • 2, по полному коду. Полный код - это строка, в которой коды уровней раз делены символом /, например "1/12/121"; возвращается методом ПолныйКод. Значение параметра режим по умолчанию есть: • 0, для справочника, код в котором уникален в пределах всего справочника; • 2, для справочника, код в котором уникален в пределах группы
Метод
Описание
*флаг = спр.НайтиПоНаименованию (наим, [режим], [соотв]);
Вернет 1 и переместит позицию справочника на эле мент с наименованием наим, если такой элемент най ден; в противном случае вернет 0, позиция справочни ка сохранится. Параметр режим принимает значения 0 или 1 и оказы вает то же действие, что и одноименный параметр ме тода НайтиПоКоду. По умолчанию режим = 1. Если соотв = 0, то поиск считается успешным, если наим и начальные символы реквизита Наименования совпадают, например если наим = "Еп", а фамилия со трудника, хранимая реквизитом Наименование, Епи фанов. Если соотв = 1, то для успешного поиска необ ходимо полное совпадение наим и Наименования. По умолчанию соотв = 0. Регистр задания значения параметра наим несущест венным. Так, сотрудник Епифанов будет найден, если соотв = 0 и наим равен "Еп", или "еп", или "еП", или "ЕП". Если же в справочнике есть также сотрудник Епанов, то для наим = "Еп" будет найден он, поскольку поиск осуществляется среди наименований, располо женных по возрастанию их значений, а "Епанов" < "Епифанов"
*флаг = спр.НайтиПоРеквизиту (рекв, знач, режим);
Ищет реквизит рекв, значение которого равно знач. Если поиск успешен, что бывает, когда знач полностью совпадает со значением реквизита рекв, вернет 1, или 0 -в противн заданной методом ИспользоватьРодителя, если режим = 0, и во всем справочнике, если режим = 1. Метод может быть применен только для реквизитов, имею щих свойство Сортировка (см. разд. 5.6 и 5.7)
*флаг = спр.ВыбратьЭлементы ([режим]);
Открывает выборку элементов из справочника и возвра щает 1, если можно выбрать хотя бы один элемент, или 0 - в противном случае. Элементы выбираются без учета иерархии, если режим = 0, и с учетом, если режим = 1. По умолчанию режим = 1. После вызова справочник по зиционируется на первом элементе выборки. Выбор эле ментов осуществляется методом ПолучитьЭлемент
*флаг - спр.ВыбратьЭлементыПо Реквизиту(рекв, знач, [иерарх], [груп]);
Открывает выборку элементов справочника, реквизит рекв которых равен знач. Собственно выборка осущес твляется методом ПолучитьЭлемент. Такая выборка возможна только в том случае, если для реквизита рекв задано свойство Сортировка (см. рис. 5.29, б). Выборка осуществляется с учетом иерархии, если иерарх = 1, и без учета, если иерарх = 0. Параметр груп принимает те же значения, что и параметр иерарх. Метод вернет 0, если в выборке нет элементов, или 1 - в противном случае. Пример см. в разд. 5.7
Метод *порТек = спр.ОбратныйПорядок ([порНов]);
Описание Если параметр порНов = 1, то выборка элементов, от крываемая методами ВыбратьЭлементы или ВыбратьЭлементыПоРеквизиту и осуществляемая методом ПолучитьЭлемент, выполняется в обратном порядке, и та же выборка выполняется в прямом порядке, ес ли порНов = 0. Возвращает порядок выборки, сущест вовавший до вызова метода. По умолчанию задан пря мой порядок выборки. Если параметр порНов опущен, то метод только вернет текущий порядок выборки, не произведя никаких изменений. Пример см. в разд. 5.7
*флаг = спр.ПолучитьЭлемент ([режим]);
Возвращает 1, если удалось выбрать элемент выборки, открытой методом ВыбратьЭлементы, или 0 в противном случае (в выборке нет элементов или выборка исчерпана). После успешного выбора метод ПолучитьЭлемент перемещает справочник на следующую позицию выборки или за ее пределы, если все элементы выбраны. Выбранный элемент возвращается методом ТекущийЭлемент. Выбор очередной позиции осуществляется с учетом порядка, установленного методами: •
**датаТек = спр.ИспользоватьДату ([датаНов], [сразу]);
ВключатьПодчиненные;
•
ИспользоватьВладельца;
•
ИспользоватьДату;
•
ИспользоватьРодителя;
•
ОбратныйПорядок;
•
ПорядокКодов;
•
ПорядокНаименований;
•
ПорядокРеквизита
Устанавливает параметром датаНов, имеющим тип Дата, дату выборки или записи периодических реквизитов справочника или объекта типа Справочник, являющегося компонентом иного объекта, например Документа. Воз вращает ранее установленную для периодических рекви зитов дату. Если параметр сразу = 1, то заданная методом дата используется в текущей выборке, если сразу = 0, то в последующей. По умолчанию сразу = 0. Не может быть использован вместе с методами Получить и Установить. • Не может иметь двойного префикса (см. пример для ме тода), например такого: док.Сотрудник.ИспользоватьДату(ТекущаяДата());. Если параметр датаНов опущен, то метод только вер нет ранее установленную для периодических реквизи тов дату
Метод
Описание
**владТек = спр.ИспользоватьВладельца ([владелец], [флагИзм]);
Задает параметром владелец, имеющим тип Справоч ник с разновидностью справочника-владельца, эле мент-владелец. После задания владельца методы ВыбратьЭлементы (ВыбратьЭлементыПоРеквизиту) и ПолучитьЭлемент, вызванные для подчиненного спра вочника, смогут выбрать только элементы, подчинен ные элементу-владельцу. Также задает элементвладелец для элемента, добавляемого в подчиненный справочник. Если флагИзм = 1, то элемент-владелец может быть изменен интерактивно либо при выборе значения эле мента диалога типа Справочник, либо при вызове ме тода Выбрать. Элемент-владелец не может быть изме нен при интерактивном выборе элементов, если фла гИзм = 0. По умолчанию флагИзм = 1. Метод возвращает ранее установленный элементвладелец. Причем если параметр владелец опущен, то метод только вернет существующего владельца, не производя иных действий. Пример употребления метода дан в разд. 5.10.3 и 5.10.5
**родТек = спр.ИспользоватьРодителя ([родитель], [флагИзм]);
Задает группу справочника, в которой методами спра вочника будет осуществляться выбор, поиск или до бавление данных. Также употребляется для задания родителя с элементами диалога типа Справочник. Группа задается параметром родитель, имеющим тип Справочник и содержащим значение группы спра вочника. Параметр флагИзм имеет тот же смысл, что и одноименный параметр метода ИспользоватьВладельца. Метод возвращает ранее установленного родителя. Причем, если параметр родитель опущен, то метод только вернет существующего родителя
*вклТек = спр.ВключатьПодчиненные Если параметр включать = 1, то в выборку включают ся подчиненные элементы, если включать = 0, то в вы ([включать]); борку попадут только имена групп первого уровня. По умолчанию подчиненные элементы включаются в выборку. Возвращает текущее значения режима включения подчи ненных. Если параметр включать опущен, то единственное назначение метода - это вернуть сущест вующее значение режима включения подчиненных *спр.ПорядокКодов();
Если метод вызван, то выборка методом Получить Элемент будет осуществляться в порядке возрастания их кодов. Замечание. По умолчанию выборка выпол няется в порядке возрастания основного представления справочника (рис 5.5), то есть Наименования или Кода
Метод *спр.ПорядокНаименований();
Описание Если метод вызван, то выборка методом Получить Элемент будет осуществляться в порядке возрастания их наименований (см. также замечание к методу ПорядокКодов)
*спр.ПорядокРеквизита(рекв);
Если метод вызван, то выборка методом Получить Элемент будет осуществляться в порядке возрастания значения реквизита рекв. Метод может быть применен только с реквизитом, для которого задано свойство Сортировка (см. рис. 5.29, б). Если это условие не вы полнено или реквизит задан неверно, то возникнет за вершающая ошибка исполнения
*спр.Новый( );
Создает пустую запись. Для ее добавления в справоч ник определяются соответствующие реквизиты и вы зывается метод Записать. Пример см. в разд. 5.5.3.1
*спр.НоваяГруппа();
Создает пустую запись новой группы. Для ее добавле ния в справочник определяются реквизиты группы, например Код и Наименование, и вызывается метод Записать. Пример см. в разд. 5.5.3.1
префТек = спр.ПрефиксКода ([префНов]);
Устанавливает префикс, употребляемый при автомати ческом вычислении значения атрибута Код. Параметр префНов, содержащий значение префикса, имеет сим вольный тип. Возвращает префикс, заданный преды дущим вызовом метода. Если код имеет числовой тип, то префикс игнорируется
спр.УстановитьНовыйКод(преф);
Добавляет к коду элемента справочника префикс, заданный параметром преф
спр.НазначитьТип (рекв, тип, [длина], [точность]);
Назначает тип или разновидность типа, заданную па раметром тип, реквизиту рекв неопределенного типа. Символьный параметр тип может принимать значение базового типа ("Число", "Строка", "Дата"), любой оп ределенной в конфигурации разновидности типа, на пример "Справочник.Дети" или "Документ.ИзменениеОклада", или значение вида субконто
спр.3аписать();
Обновляет текущий элемент справочника. Метод, в от личие от команды диалога #3аписать, не вызывает предопределенную процедуру ПриЗаписи
*спр.Удалить(способ);
Если способ = 1, то проставляет DBF-пометку удале ния текущей записи. Такую запись средствами 1С воестановить нельзя. Если способ = 0, то проставляет 1Спометку удаления текущей записи (см. разд. 5.5.2). По умолчанию способ = 1. Если удаляется запись справочника-владельца, то будут удалены (помечены для удаления) и все подчиненные ей записи
флаг = спр.ПометкаУдаления();
Возвращает 1, если текущая запись имеет 1 С-пометку удаления, или 0 -в противном случае
Метод *спр.СнятьПометкуУдаления();
флаг = спр.Блокировка([блк]);
Описание Снимает 1С-пометку удаления текущего элемента справочника. Если запись, с которой снимается 1Спометка удаления, имеет подчиненные записи в дру гом справочнике и они также имеют 1С-пометку уда ления, то 1С-пометка удаления будет снята только с записи-владельца . Возвращает/устанавливает режим блокировки записи. Если блк = 1, то блокировка устанавливается, и блокировка отключается, если блк = 0. Возвращает 1 если есть блокировка, или 0 -в противном случае
Замечания: 1. Имя спр переменной типа Справочник, употребленное в табл. 5.8 перед названи« ми методов, может быть произвольным. 2. В форме элемента, группы или списка справочника методы, если они применяютс для текущего справочника, вызываются без префикса. 3. Методы, отмеченные в табл. 5.8 звездочкой, можно использовать только с объек тами, созданными функцией СоздатОбъект. То есть их нельзя употреблять для текущего справочника в форме его элемента, группы или списка. 4. Методы, отмеченные в табл. 5.8 двумя звездочками, можно использовать как с объектами, созданными функцией СоздатОбъект, так и для реквизитов тип Справочник, которые являются компонентами иных объектов, например докумен тов или справочников другого вида. 5. Имя реквизита рекв, используемое в методах, является символьным выражением возвращающим строку с именем реквизита, например строку "Оклад". 6. В методах УстановитьАтрибут и ПолучитьАтрибут при неверном значении пар з метра рекв возникнет завершающая ошибка, сопровождаемая сообщением "Неверное имя атрибута". 7. Параметры методов, если это не оговаривается особо, являются входными. 8. При изменении значений реквизитов методами Установить, УстановитьАтрибута УстановитьНовыйКод, Записать предопределенная процедура ПриЗаписи модуля формы элемента (группы) или списка не вызывается. Примеры. Расположим их в процедуре Выполнить модуля обработки Проба Смысл фрагментов поясняется пользователю в окне, вызываемом встроенной процедурой Предупреждение. процедура Выполните( ) // Связана перем сСотр_2, сОбр_2, спр, флаг, гр, обр; перем видыТек, видыНов, род, дети;
с
кнопкой
ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2");
Пуск
обработки
Проба
// Пример для метода Вид Предупреждение(''Создаем справочник неопределенного вида."); // спр - объект типа Справочник неопределенного вида спр = СоздатьОбъект("Справочник"); спр.Вид("Образование_2"); // Задаем разновидность типа объекта спр Сообщить(спр.Вид( )); // Напечатает Образование_2 // Пример для методов НайтиПоНаименованию, НайтиПоКоду, // УстановитьАтрибут, Записать и ПолучитьАтрибут Предупреждение("Меняем реквизит Образование у сотрудника с кодом 302."); // Ищем по части наименования флаг = спр.НайтиПоНаименованию("Нач", 0); если флаг = 1 тогда // Ищем во всем справочнике флаг = сСотр_2.НайтиПоКоду(302, 0); если флаг = 1 тогда // Сотрудник найден сСотр_2.УстановитьАтрибут("Образование", спр.ТекущийЭлемент()); сСотр_2.3аписать(); // Те же изменения вызовут два следующих оператора: // сСотр_2.0бразование = спр.ТекущийЭлемент(); // сСотр_2.3аписать(); Сообщить(сСотр_2.ПолучитьАтрибут("Образование")); // То же сообщение выведет оператор // Сообщить(сСотр_2.Образование)); // Просмотр результата сСотр_2.Выбрать("Просмотр результата", "ФормаСписка"); иначе Предупреждение("Сотрудник с кодом 302 не найден."); конецЕсли; иначе Предупреждение("В справочнике Образование_2 нет |элемента с наименованием Начальное."); конецЕсли; // Пример для методов ТекущийЭлемент. ЭтоГруппа. ПринадлежитГруппе. // ВыбратьЭлементы, ПолучитьЭлемент и Выбран Предупреждение("Выводится список сотрудников цеха 1." + РазделительСтрок + "Имена подразделений цеха не печатаются."); флаг = сСотр_2.НайтиПоНаименованию("01 Цех"); если флаг = 1 тогда если сСотр_2.ЭтоГруппа() = 1 тогда // Запоминаем найденную группу гр = сСотр_2.ТекущийЭлемент(); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл // Не выводим имена подразделений первого цеха если (сСотр_2.ПринадлежитГруппе(гр) = 1) и (сСотр_2.ЭтоГруппа() = 0) тогда Сообщить(сСотр_2.Наименование); конецЕсли; конецЦикла; // пока // После такого цикла метод Выбран вернет 0
если сСотр_2.Выбран() = 0 тогда Сообщить("После цикла Пока элемент справочника не выбран."); конецЕсли; иначе Предупреждение("Найденный элемент не группа."); конецЕсли; иначе Предупреждение("Имя 01 Цех не найдено."); конецЕсли; // Пример для методов ВидыДляВыбора, ВыборГруппы и Выбран. // а также для методов ПолныйКод и ПолноеНаименование спр = 0; // спр - справочник неопределенного вида спр = Создать0бъект("Справочник"); видыНов = "Сотрудники_2, Образование_2"; Предупреждение("Если выберете вид Сотрудники, то получите полные код | и наименование выбранного вслед элемента справочника."); видыТек = спр.ВидыДляВыбора(видыНов); // Разрешаем выбор групп в диалоге выбора. По умолчанию выбор групп запрещен спр.ВыборГруппы(0); флаг = спр.Выбрать(, "ФормаСписка"); если флаг = 1 тогда если спр.Вид( ) = "Сотрудники_2" тогда Сообщить("Выбран сотрудник: " + спр.Наименование); // Следующий вызов напечатает, например, 1/12/121 Сообщить(спр.ПолныйКод()); // Напечатает, например, 01 Цех / 01/2 / Волосков Михаил Андреевич Сообщить(спр.ПолноеНаименование()); иначе Сообщить("Выбрано образование:" + спр.Наименование); конецЕсли; иначе Предупреждение("Сотрудник не выбран."); конецЕсли; // Пример для метопа НайтиЭлемент: справочник Сотрудники_2 // позиционируется на родителе выбранного элемента Предупреждение("После выбора сотрудника откроется форма, | в которой выбран родитель сотрудника."); сСотр_2.ВыборГруппы(0); // Запрещаем выбор групп // Открываем диалог формы для выбора флаг = сСотр_2.Выбрать("Выберите сотрудника", "ФормаСписка"); если флаг = 1 тогда сСотр_2.НайтиЭлемент(сСотр_2.Родитель); сСотр_2.ВыборГруппы(1); // Разрешаем выбор групп сСотр_2.Выбрать("Курсор размещен на родителе ранее выбранного элемента", "ФормаСписка"); иначе Предупреждение("Элемент не выбран."); конецЕсли;
// Пример для методов НайтиПоРеквизиту и ИспользоватьРодителя Предупреждение("После выбора вида образования и подразделения | откроется форма справочника Сотрудники_2, в которой | выбран сотрудник, имеющий выбранный вид образования."); // Предварительно выбираются вид образования обр и подразделение (группа) // Затем выполняется поиск сотрудника, имеющего образование обр // и работающего в выбранном подразделении сСотр_2 = 0; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сОбр_2 = СоздатьОбъект("Справочник.Образование_2"); // Выбираем вид образования обр флаг = сОбр_2.Выбрать("Выберите вид образования",""); если флаг = 1 тогда обр = сОбр_2.ТекущийЭлемент(); // Выбираем подразделение подр // Разрешаем выбор групп в диалоге выбора. По умолчанию выбор групп запрещен сСотр_2.ВыборГруппы(1); флаг = сСотр_2.Выбрать("Выберите цех", ""); если сСотр_2.ЭтоГруппа() = 1 тогда // Поиск обр в группе сСотр_2 род = сСотр_2.ТекущийЭлемент(); сСотр_2.ИспользоватьРодителя(род); флаг = сСотр_2.НайтиПоРеквизиту("Образование", обр, 0); если флаг = 1 тогда // Покажем результат в форме списка для выбора сСотр_2.Выбрать("Курсор размещен на найденном сотруднике", ""); иначе Предупреждение("В подразделении " + род.Наименование + " нет сотрудника |с образованием " + обр.Наименование); конецЕсли; иначе Предупреждение("Цех не выбран."); конецЕсли; иначе Предупреждение("Вид образования не выбран."); конецЕсли; // Пример для методов ИспользоватьРодителя и Выбрать // В качестве родителя задается второй цех. Затем используется метод Выбрать // Первый раз родитель может быть сменен при интерактивном выборе, второй раз - нет флаг = сСотр_2.НайтиПоКоду(2,0); // 2 - код второго цеха если флаг = 1 тогда Предупреждение("В первой форме родителя менять можно, а во второй - нельзя"); род = сСотр_2.ТекущийЭлемент(); сСотр_2.ИспользоватьРодителя(род, 1); // Можно выбирать сотрудников из любой группы сСотр_2.Выбрать("Можно менять родителя", "ФормаСписка"); // Сотрудников можно выбрать только из второго цеха сСотр_2.ИспользоватьРодителя(род, 0); сСотр_2.Выбрать("Родителя сменить не удается", "ФормаСписка"); иначе Предупреждение("Цех с кодом 2 не найден."); конецЕсли;
// Пример для метода ИспользоватьДату // Создается объект типа Документ с разновидностью ПриказОПриеме, // имеющий, как известно, реквизит Сотрудник, устанавливающий связь документа // с порожденной им записью в справочнике Сотрудники_2. Используя этот реквизит, // определим нынешний оклад сотрудника Горюновой У. В., // принятой на работу по приказу № 1 док = СоздатьОбъект("Документ.ПриказОПриеме"); флаг = док.НайтиПоНомеру(1, Дата(0)); если флаг = 1 тогда Предупреждение("В окне сообщений выведем оклады Горюнова Ульяны | по приказу и на рабочую дату."); // Нельзя устанавливать дату периодического реквизита следующим образом: // док.Сотрудник.ИспользоватьДату(ТекущаяДата()); // Это нужно делать так: // Определяем объект с разновидностью типа Справочник.Сотрудники_2 сотр = док.Сотрудник; // Устанавливаем для этого объекта дату периодических реквизитов сотр.ИспользоватьДату(док.ДатаПриема); // Оклад на дату зачисления на работу; должен быть равен док.Оклад; оклСтар = сотр.Оклад; // Меняем дату чтения периодического реквизита Оклад сотр.ИспользоватьДату(ТекущаяДата()); оклСегодня = сотр.Оклад; Сообщить("Оклад по приказу: " + оклСтар); // Оклад по приказу: 2500 Сообщить("Оклад сегодня: " + оклСегодня); // Оклад сегодня: 3200 иначе Предупреждение("Приказ № 1 не найден."); конецЕсли; // Пример для методов Удалить, ПометкаУдаления и СнятьПометкуУдаления // Ставится пометка удаления на запись с кодом 302. Это вызывает простановку // пометок удаления и на записи в справочнике Дети, подчиненные записи-владельцу сСотр_2 - 0; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); флаг = сСотр_2.НайтиПоКоду(302, 0); если флаг = 1 тогда если Вопрос("Удалить запись с кодом 302?", "Да+Нет") = "Да" тогда Предупреждение("Ставим пометку удаления сотруднику с кодом 302 | в справочнике Сотрудники_2 и в подчиненном справочнике Дети."); // Проставляем 1С-пометку удаления (Рис. 5.61.) // Если дети сотрудника занесены в справочник Дети, то соответствующие // им записи также получат пометку удаления сСотр_2.Удалить(0); Предупреждение("Просмотр результата."); сСотр_2.Выбрать("Запись о сотруднике с кодом 302 помечена для удаления", ""); дети = СоздатьОбъект("Справочник.Дети"); дети.ИспользоватьВладельца(сСотр_2); дети.Выбрать("Удалены записи и в подчиненном справочнике", ""); дети = 0; конецЕсли;
иначе Предупреждение("Сотрудник не найден."); конецЕсли; // Снимаем пометку удаления с текущей записи // Однако проставленная ранее пометка удаления в подчиненном // справочнике сохранится если сСотр_2.ПометкаУдаления( ) = 1 тогда если Вопрос("Снять пометку удаления?", "Да+Нет") = "Да" тогда Предупреждение("Пометка удаления снимается только" + РазделительСтрок + "в справочнике Сотрудники_2."); сСотр_2.СнятьПометкуУдаления(); Предупреждение("Просмотр результата."); сСотр_2.Выбрать("С записи о сотруднике с кодом 302 снята пометка удаления", ""); дети = СоздатьОбъект("Справочник.Дети"); дети.ИспользоватьВладельца(сСотр_2); дети.Выбрать("Дети по-прежнему имеют пометку удаления",""); конецЕсли; конецЕсли; // Пример для метода ВключатьПодчиненные Предупреждение("В окно сообщений благодаря методу ВключатьПодчиненные" "выводятся только имена групп справочника Сотрудники_2."); сСотр_2.ВключатьПодчиненные(0); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл Сообщить(сСотр_2.Наименование) конецЦикла; // пока // Результат: //01 Цех // 02 Цех // 03 Цех конецПроцедуры // Выполнить
Рис. 5.61. Удаление записи-владельца влечет удаление и подчиненных записей Замечание. Сразу после выбора нового вида справочника в результате вызова видыТек = спр.ВидыДляВыбора(видыНов); метод спр.Вид() вернет пустое значение.
5.12.3. МЕТОДЫ ФОРМЫ ЭЛЕМЕНТА СПРАВОЧНИКА Могут быть использованы только в модуле элемента справочника. Вызываются без префикса. Приводятся в табл. 5.9.
Таблица 5.9 Методы формы элемента справочника Метод
Описание
флаг = Модифицированность();
Вернет 1, если изменен хотя бы один элемент диалога формы элемента справочника, или 0, если элементы диалога не модифицировались. Если изменения были, то в заголовке диалога появится символ *
датаТек = ИспользоватьДату ([датаНов], [обновить]);
Имеет то же назначение, что и одноименный метод табл. 5.8
списТек = СохранениеПериодических Реквизитов(вариант, [список]);
Задает режим отображения диалога Изменения периодических реквизитов (см. рис. 5.16). Если вариант равен 0 или 2, то диалог не отображается; и отображается, если вариант равен 1, 3, 4 или 5. Символьный параметр список содержит список анализируемых на предмет изменения периодических реквизитов, например "Оклад, Должность". Если список = "*", то в него включаются все периодические реквизиты справочника. Периодический реквизит считается измененным, если изменилась его дата, или значение, или и то и другое. Возвращает ранее установленный список периодических реквизитов
списТек = ПросмотрИстории([список]);
Задает параметром список периодические реквизиты, для которых допускается просмотр истории
Замечание. Для работы с периодическими реквизитами в 1С, помимо приве денных в табл. 5.9, имеется широкий ассортимент возможностей: методы справоч ника Получить и Установить, метод модуля документа УстановитьРеквизитСпр авочника, а также методы объекта Периодический, рассматриваемые в гл. 6.
5.12.4. ПРЕДОПРЕДЕЛЕННЫЕ ПРОЦЕДУРЫ МОДУЛЯ ФОРМЫ ЭЛЕМЕНТА И ГРУППЫ СПРАВОЧНИКА В модуле любой формы можно задать ее предопределенные процедуры, такие, как ПриОткрытии, ПриПовторномОткрытии, ПриЗакрытии, ПриВыбореЗакладки, ПриНачалеВыбораЗначения, ОбработкаПодбора, ОбработкаВыбораЗначения, ПриВыбореСтроки. Некоторые из них нами уже употреблялись, иные будут рассмот рены позже. Вдобавок к этим процедурам в форме элемента (группы) справочника можно на писать код еще двух перечисленных в табл. 5.10 предопределенных процедур.
Таблица5.10 Предопределенные процедуры модуля формы элемента (группы) справочника Процедура ВводНового ([копир], [объект])
ПриЗаписи ([перРекв])
Описание Вызывается, если ее код присутствует в модуле формы элемента (груп пы), при интерактивном вводе нового элемента справочника, например в результате выбора пункта Новый в колонке меню Действия. Необяза тельный формальный параметр копир, если он есть и равен единице, означает, что новая запись введена копированием (Действия - Копиро вать или клавиша F9) и, следовательно, значения ее реквизитов извест ны. Если параметр равен нулю, то значения реквизитов не определены и тогда имеет смысл задать их в теле рассматриваемой процедуры. Па раметр объект, если присутствует, передает скопированный объект Вызывается, если ее код присутствует в модуле формы элемента (груп пы), при выполнении команды #3аписать. Необязательный формаль ный параметр перРекв, если он есть, передает список периодических реквизитов, выбранных для обновления в окне диалога изменения пе риодических реквизитов (см. рис. 5.16)
Пример 1. В предопределенной процедуре ВводНового модуля формы элемента справочника Сотрудники_2 (разд. 5.3.4.2) выводятся переданные параметром объект реквизиты формы. // Для ввода копированием используем F9 или нажмем на иконку на панели инструментов // формы списка справочника, или выберем соответствующий пункт меню конки Действия процедура ВводНового(копир, объект) если копир = 1 тогда // Если новый элемент вводится копированием Сообщить(объект.Наименование); Сообщить(объект.Оклад); Сообщить(объект.Образование); конецЕсли; конецПроцедуры // ВводНового Замечания: 1. Предопределенная процедура ВводНового вызывается раньше предопределенной процедуры ПриОткрытии. 2.
Параметром объект не передаются периодические реквизиты. Так, при вызове Сообщить(объект.Оклад); 1С напечатает НеизвестныйОбъект
Пример 2. В предопределенной процедуре ПриЗаписи модуля формы элемента справочника Сотрудники_2 (разд. 5.3.4.2) анализируется параметр процедуры перРекв, и если он содержит реквизит Оклад, то процедура завершается со статусом возврата О, что означает запрет записи данных.
// Предопределенная процедура ПриЗаписи вызывается после появления диалога // Изменения периодических реквизитов (рис. 5.16) процедура ПриЗаписи(перРекв) // Если планируется обновить периодический реквизит Оклад если Найти(перРекв, "Оклад") = 1 тогда Предупреждение("Оклад можно изменить только на основании приказа."); СтатусВозврата(0); // Запрещаем запись элемента возврат; конецЕсли; // Последующий код конецПроцедуры // ПриЗаписи
5.12.5. МЕТОДЫ ФОРМЫ СПИСКА СПРАВОЧНИКА Употребляются в модулях формы списка справочника без префикса. Приводятся в табл. 5.11. Таблица 5.11 Методы формы элемента справочника Метод
Описание
датаТек = ИспользоватьДату ([датаНов]);
Имеет то же назначение, что и одноименный метод табл. 5.8
владТек = ИспользоватьВладельца ([владелец], [флагИзм]);
Задает владельца для формы списка подчиненного справочника. Имеет то же описание, что и одноименный метод табл. 5.8. Пример см. в разд. 5.10.3.
родТек = ИспользоватьРодителя ([родитель], [флагИзм]);
Задает родителя для формы списка подчиненного справочника. Имеет то же описание, что и одноименный метод табл. 5.8
иерархТек = ИерархическийСписок ([иерарх], [флагИзм]);
Если иерарх =1, то многоуровневый справочник будет отображаться по группам в виде иерархиче ского списка (см., например, рис. 5.1); если ие рарх = 0, то элементы в окне вывода следуют без учета иерархии, отсортированные, например, по коду (см. рис. 5.2). Если флагИзм = 1, то пользова тель может интерактивно включать/отключать ре жим ввода списка справочника по группам, и не может, если флагИзм = 0. По умолчанию флагИзм = 1. Возвращает 1, если показ списка элементов осуществляется по группам, или 0 -в противном случае
выбТек = ВыборГруппы([выбНов]);
Имеет то же назначение, что и одноименный метод табл. 5.8
Метод редактТек = РедактироватьВДиалоге ([редакт], [флагИзм]);
списТек = СохранениеПериодических Реквизитов(вариант, [список]); сортТек = Сортировка {[сорт], [флагИзм]);
Описание Если редакт = 1, то редактирование элемента (груп пы) справочника будет осуществляться с использова нием формы этого элемента (группы). Такие формы, конечно же, должны быть заготовлены заранее. Если редакт = 0, то элемент (группа) редактируется в строке формы списка справочника. Если флагИзм = 1, то пользователь может интерактивно менять режим редактирования справочника, и не может, если фла гИзм = 0. По умолчанию флагИзм = 1. Возвращает 1, если редактирование на момент вызова метода осу ществляется в диалоге, или 0 -в противном случае Имеет то же назначение, что и одноименный метод табл. 5.8 Задает символьным параметром сорт способ сорти ровки данных в диалоге формы списка справочника. Всегда параметр сорт можно задать равным "Код" или "Наименование". Также в качестве сорт можно передать строку с именем реквизита, имеющего свой ство Сортировка. Если флагИзм = 1, то пользователь может интерактивно менять способ упорядочения данных, и не может, если флагИзм = 0. По умолча нию флагИзм = 1. Возвращает 1, если редактирование на момент вызова метода осуществляется в диалоге, и л и 0 - в противном случае
УстановитьОтбор(рекв, знач);
Ограничивает объем выводимых в диалоге формы списка справочника данных: отображаются только те элементы, реквизит рекв которых имеет значение знач. Параметр рекв - это строка со значением, совпа дающим с именем реквизита. Для этого реквизита должно быть задано свойство Отбор по реквизиту (см. рис. 5.29, б). Если рекв - пустая строка, то отбор отключается. Тип (разновидность типа) параметра знач определяется типом реквизита, по которому ус танавливается отбор
флаг = ПолучитьОтбор(рекв, знач);
Возвращает в параметры рекв и знач текущий отбор элементов справочника. Параметры имеют тот же смысл, что и одноименные параметры метода УстановитьОтбор. Возвращает 1, если отбор задан, или 0 в противном случае Задает параметром списокРекв список реквизитов, по которым в интерактивном режиме можно устанав ливать отбор. Параметр списокРекв - это строка, со держащая одно или несколько разделенных запятыми имен отбора, например "Образование" или "Имя,, ГодРождения"
видыТек = ВидыОтбора (списокРекв);
Метод
Описание
флаг = ЗакладкиОтбора(рекв, [знач]);
Устанавливает закладки отбора по реквизиту рекв в диалоге формы списка справочника. Параметр знач, если он задан, определяет первоначально отображаемый отбор. Описание параметров то же, что и для метода УстановитьОтбор. Возвращает пустое значение. Пример использования см. в разд. 5.9.1.2
ИспользоватьСписокЭлементов (сЗнач);
Устанавливает фильтр, позволяющий отображать только те элементы справочника, значения которых содержатся в списке значений сЗнач. Если список пуст, то фильтр отключается. После вызова метода с непустым списком нельзя вводить, копировать и перемещать элементы справочника. Пример употребления см. в разд. 5.9.2
списТек = ПросмотрИстории([список]); Имеет то же назначение, что и одноименный метод табл. 5.8 Пример. В предопределенной процедуре ПриОткрытии модуля ф о р м ы списка справочника Сотрудники_2 задаются способы представления данных. процедура ПриОткрытии() перем обр; // Вывод данных осуществляется в диалоге формы без учета иерархии; // способ вывода изменению не подлежит // Сортировка производится по наименованию; // способ сортировки может меняться интерактивно // Редактирование записи выполняется в диалоге или в документе, // если реквизит ПриказПрием содержит ссылку на документ Приказ о приеме на работу // Кроме того, устанавливаем отбор по реквизиту Образование и выводим после // открытия список сотрудников с высшим образованием ИерархическийСписок(0, 0); Сортировка("Наименование", 1); // Режим редактирования можно менять интерактивно РедактироватьВДиалоге(1, 1); обр = СоздатьОбъект("Справочник.Образование_2"); // Ищем высшее образование если обр.НайтиПоНаименованию("В", 0) = 1 тогда // Второй параметр метода УстановитьОтбор должен иметь разновидность типа // Справочник.Образование_2 УстановитьОтбор("Образование", обр.ТекущийЭлемент()); иначе Предупреждение ("В справочнике Образование_2 нет записи, начинающейся с буквы В."); конецЕсли; конецПроцедуры // ПриОткрытии
5.12.6. ПРЕДОПРЕДЕЛЕННЫЕ ПРОЦЕДУРЫ МОДУЛЯ ФОРМЫ СПИСКА СПРАВОЧНИКА Включаются программистом в модуль формы списка справочника по мере необ ходимости. Приводятся в табл. 5.12. Таблица5.12 Предопределенные процедуры модуля формы списка справочника Процедура
Описание
ПриВводеСтроки()
Вызывается до начала ввода новой строки в форме списка справочника
ПриРедактированииНовой Строки()
Вызывается в момент начала ввода данных в новую строку формы списка справочника
ПриНачалеРедактирования Строки()
Вызывается в момент начала редактирования данных в существующей строке формы списка справочника
ПриЗаписи([пер.Рекв])
Имеет то же назначение, что и одноименная предопределенная процедура модуля формы элемента (группы) справочника
ПриПереносеЭлемента ВДругуюГруппу ([элемент], [группа])
Вызывается при интерактивном переносе элемента в другую группу справочника. Формальный параметр элемент содержит переносимый элемент, а группа - это группа, в которую элемент переносится
ПриВыбореРодителя ([родитель])
Вызывается при выборе (двойном ударе мышью или нажатии на Enter) родителя (группы). Параметр родитель, если присутствует, имеет тип Справочник и содержит выбранный элемент-родитель
ПриВыбореВладельца ([владелец])
Вызывается при интерактивном выборе владельца. Здесь выбор - это перемещение с одного элемента справочника-хозяина на другой. Параметр владелец, если присутствует, имеет тип Справочник и содержит выбранный элемент-владелец. Пример см. в разд. 5.10.3
ПриСменеИерархии ([иерарх])
Вызывается при интерактивной смене способа представления списка элементов справочника (иерархический неиерархический). Числовой параметр иерарх, если он присутствует, равен единице, если устанавливается иерархический способ, или нулю - в противном случае
ПриУстановкеОтбора ([рекв], [знач])
Вызывается при интерактивной установке отбора (см. разд. 5.9). Параметр рекв - это строка, содержащая имя реквизита, по которому отбор установлен, например "Образование", знач значение(критерий)отбора
Пример. При переносе элемента в другую группу справочника Сотрудники_2 не обходимо изменить его код, приведя в соответствие с правилами его автоматического назначения. Правила таковы: в качестве первых цифр кода элемента берется код роди теля, если родитель расположен на втором уровне справочника Сотрудники_2, или код родителя, увеличенный в 10 раз, если родитель лежит на первом уровне (всего в спра вочнике Сотрудники_2 три уровня). Последующие цифры кода сотрудника образуют
возрастающую последовательность целых положительность чисел. Эту схему реализ ует функция СоздатьКод. Ее алгоритм приведен в разд. 5.8.2.2. Правда, в нем вместо функции СоздатьКод использована одноименная процедура. функция СоздатьКод(группа) далее процедура ПриПереносеЭлементаВДругуюГруппу(элемент, группа) новКод = СоздатьКод(группа); // Новый код для переносимого в другую группу элемента УстановитьАтрибут("Код", новКод); конецПроцедуры // ПриПереносеЭлементаВДругуюГруппу функция СоздатьКод(группа) перем сСотр_2, максКод, двеЦифр, послЦифр; перем двеЦифрУмнНаК, к; двеЦифр = ?(группа.Уровень() = 1, 10 * группа.Код, 1 * группа.Код); // Найдем максКод - максимальный код сотрудника в пределах подразделения сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ИспользоватьРодителя(группа); // Осуществим перебор в порядке возрастания кодов элементов сСотр_2.ПорядокКодов(); сСотр_2.ВыбратьЭлементы(); максКод = 0; пока сСотр_2.ПолучитьЭлемент() > 0 цикл максКод = 1 * сСотр_2.Код; конецЦикла; // пока // Ищем последующие цифры максКод и затем последующие цифры кода // нового сотрудника. Будем умножать двеЦифр на 10, пока не превысим максКод к=1; двеЦифрУмнНаК = двеЦифр; пока двеЦифрУмнНаК < максКод цикл двеЦифрУмнНаК = двеЦифрУмнНаК * 10; конецЦикла; // пока // Последующие цифры кода нового сотрудника послЦифр = ?(максКод = 0, 1, максКод - двеЦифрУмнНаК / 10 + 1); // Итак, вернем код нового сотрудника возврат Число(Строка(двеЦифр) + Строка(послЦифр)); конецФункции // СоздатьКод
5.13. ЧТЕНИЕ ЗНАЧЕНИЯ РЕКВИЗИТА СПРАВОЧНИКА Все рассматриваемые в текущем разделе способы доступа к реквизитам справоч ника уже нами употреблялись. Здесь же мы приведем некоторые обобщения. Значение реквизита справочника можно получить, создав объект типа Справочник и применив вслед известные методы доступа к реквизитам объекта (см. табл. 5.8). Причем значение кода (или иного реквизита) атрибута Родитель (Владелец) можно п олучить следующим образом:
сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.НайтиПоКоду(301, 0); // Ищем во всем справочнике Сообщить(сСотр_2.Родитель.Код); // Код атрибута Родитель
В формах элемента, группы и списка справочника его реквизиты доступны непо средственно и могут быть использованы в качестве элементов диалога или перемен ных модулей этих форм. Функция ОткрытьФорму и метод модуля формы ОткрытьПодбор возвращают ко нтекст открытой формы. Поэтому, если открыт справочник, можно обратиться к его р еквизиту через полученный контекст (см. разд. 5.4). Если объект, например Сотрудник, имеет тип Справочник и является компонентом другого объекта, например документа ПриказОПриеме, то к реквизиту объектакомпонента можно обратиться, использовав, например, такой путь: <Создаем объект док с разновидностью типа Документ.ПриказОПриеме> <Находим нужный документ (приказ)> // Читаем значение реквизита Образование в справочнике, на который указывает // реквизит Сотрудник найденного документа ПриказОПриеме обр = док.Сотрудник.Образование;
5.14. ПРОГРАММНАЯ ОБРАБОТКА УДАЛЯЕМЫХ ЗАПИСЕЙ Все записи информационной базы, обладающие 1С-пометкой удаления, можно разместить в списке значений, вызвав процедуру НайтиПомеченныеНаУдаление. Спи сок найденных ей объектов можно просмотреть и передать его (весь или сокращен ный) затем либо процедуре НайтиСсылки, либо процедуре УдалитьОбъекты. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба // сЗнач - список значений, куда будут размещены записи об объектах, // помеченных для удаления // сЗнач2 - список объектов, передаваемых процедуре НайтиСсылки перем сЗнач, сЗнач2, тЗнач, ин, значен, пред; ОчиститьОкноСообщений(); сЗнач = СоздатьОбъект("СписокЗначений"); НайтиПомеченныеНаУдаление(сЗнач); если сЗнач.РазмерСписка() > 0 тогда // Диалог для проставления пометок в списке см. на рис. 5.62 сЗнач.ОтметитьЗначения(, "Выберите объекты для поиска ссылок"); // После проставления пометок в списке сЗнач перенесем // выбранные элементы в список сЗнач2 (рис. 5.63) сЗнач2 = СоздатьОбъект("СписокЗначений"); для ин = 1 по сЗнач.РазмерСписка() цикл // Если элемент списка не имеет пометки если сЗнач.Пометка(ин) = 1 тогда значен = сЗнач.ПолучитьЗначение(ин, пред); сЗнач2.ДобавитьЗначение(значен, пред); конецЕсли; конецЦикла; // для
если сЗнач.РазмерСписка() > 0 тогда // Просмотр результата сЗнач2.ВыбратьЗначение(, "Список для процедуры НайтиСсылки"); // тЗнач - таблица, в которую разместим ссылки на удаленные объекты тЗнач = СоздатьОбъект("ТаблицаЗначений"); НайтиСсылки(сЗнач2, тЗнач); // Просмотр результата. Возможный вариант приведен на рис. 5.64 тЗнач.ВыбратьСтроку(, "Таблица ссылок"); иначе Сообщить("Список для процедуры НайтиСсылки пуст."); конецЕсли иначе Сообщить("Нет записей с 1С-пометкой удаления."); конецЕсли; конецПроцедуры // Выполнить
Рис. 5.62. Объекты, отобранные в списке сЗнач для поиска ссылок
Рис. 5.63. Список сЗнач2 объектов для поиска ссылок
Рис. 5.64. Обнаружена ссылка на документ № 4 из справочника Сотрудники_2 Замечание. В процедуре НайтиСсылки в качестве первого параметра можно упот ребить не список значений, а значение одного объекта, ссылки на который нужно о бнаружить, например, так: тЗнач = СоздатьОбъект("ТаблицаЗначений"); док = СоздатьОбъект("Документ.ИзменениеОклада"); док.НайтиПоНомеру(4); // Ищем документ НайтиСсылки(док.ТекущийДокумент( ), тЗнач); // Просмотр таблицы ссылок тЗнач.ВыбратьСтроку(, "Таблица ссылок");
Передаваемый процедуре объект может не иметь пометки удаления. Процедура УдалитьОбъекты проставляет DBF-пометки удаления. Процедура, так же как и процедура НайтиСылки, может принимать в качестве первого пара метра либо отдельный удаляемый объект, либо список объектов. Причем объекты могут в общем-то и не иметь 1С-пометок удаления. Процедура имеет следующий синтаксис: УдалитьОбъекты(объект | список_объектов, [проверка], [таблица_ссылок]); где первый параметр - это либо удаляемый объект, либо список объектов, возвра щаемый, например, процедурой НайтиПомеченныеНаУдаление. Если параметр проверка = 1, то осуществляется поиск ссылок на объекты, заданные первым пара метром процедуры. Найденные ссылки возвращаются выходным параметром таб лица_ссылок, имеющим тип ТаблицаЗначений. Причем объекты, ссылки на кото рые обнаружены, не удаляются. Если же проверка = 0, то ссылки и все объекты, заданные параметром 1, удаляются (разумеется, в том случае, если первый пара метр задан без ошибок). Удаленные объекты получают DBF-пометку удаления. Их физическое удаление выполняется в конфигураторе, открывающем подлежащие упаковке файлы в моно польном режиме (разд. 5.5.2). Пример. Удаляется документ № 4 об изменении оклада. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем тЗнач, док; тЗнач = СоздатьОбъект("ТаблицаЗначений"); док = СоздатьОбъект("Документ.ИзменениеОклада"); // Ищем документ если док.НайтиПоНомеру(4) = 0 тогда Предупреждение("Документ под номером 4 не найден."); конецЕсли; // Перед удалением проводим поиск ссылок на документ № 4 УдалитьОбъекты(док.ТекущийДокумент(), 1,тЗнач); // Если есть ссылки, то прежде их просмотрим, а затем примем решение, // удалять документ или нет если тЗнач.КоличествоСтрок() > 0 тогда // Просмотр таблицы ссылок тЗнач.ВыбратьСтроку(, "Таблица ссылок"); если Вопрос("Удалить документ, на который есть ссылки?", "Да+Нет") = "Да" тогда // Удаляем документ без поиска ссылок (проставляем DBF-пометку удаления) УдалитьОбъекты(док.ТекущийДокумент()); иначе' Предупреждение("Документ не удален."); конецЕсли; иначе Предупреждение("Документ № 4 удален."); конецЕсли; конецПроцедуры // Выполнить
После удаления в окне сообщений возникнет следующий текст: Удаленные записи: Документ: ИзменениеОклада Номер: 4 Дата: 29.11.2001 Документ.ИзменениеОклада: удалено объектов 1 Замечание. Удаление объектов, на которые есть ссылки, не приветствуется, по скольку приводит к нарушению целостности информационной базы данных системы.
5.15. ВЫВОДЫ 1. Справочник 1С - это в общем случае совокупность нескольких взаимосвязанных DBF-файлов. 2. Главная таблица справочника в общем случае, кроме элементов, включает и груп пы, к которым эти элементы принадлежат. Такая организация таблицы позволяет представлять справочник в виде иерархического списка. 3. Периодические реквизиты справочника хранятся в файле 1 SCONST.DBF. 4. Для добавления, редактирования и просмотра данных справочника можно использовать формы элемента, группы, списка и документы, а также соответст вующие методы справочника. 5. Реквизиты, автоматически формируемые программой (например код) следует де лать доступными только для чтения. 6. Атрибуты Родитель и Владелец имеют тип Справочник. 7. Удаление записей справочника выполняется в 3 этапа: на первом записи поме чаются для удаления символом * в поле Ismark таблицы справочника, на втором в специально выделенном поле DBF-файла таблицы, на третьем - выполняется упаковка помеченных для удаления данных; записи, удаление которых приведет к нарушению целостности базы данных, нельзя удалить в интерактивном режиме, зато это можно выполнить программно, употребив встроенную процедуру УдалитьОбъекты. 8. При простановке пометки удаления на запись-владелец автоматически такие по метки проставляются и на подчиненные ей записи. Для интерактивного режима справедливо и обратное: снятие пометки удаления с записи-владельца приводит, если пользователь дал согласие, к снятию таких пометок и с подчиненных записей. 9. Предопределенную процедуру ПриЗаписи следует применять для контроля ввод имых данных. 10. Предопределенная процедура ВводНового употребляется для задания начальных значений вводимых реквизитов. 11. Отбор значений справочника возможен только по реквизитам, имеющим свойства Сортировка и Отбор по реквизиту. 12. В подчиненном справочнике отображаются только строки, связанные с записью, выбранной в справочнике-владельце.
13. Связь между подчиненным справочником и его владельцем осуществляется через атрибут Владелец подчиненного справочника. Связь между их элементами - через поля Parentext и Id DBF-таблиц этих файлов. 14. Длинный код по выбору данных следует заменять запросами. 15. Для быстрого отображения выборки, созданной в результате выполнения запроса, ее можно выгрузить в таблицу значений, а последнюю вывести на экран, употре бив метод ВыбратьСтроку. 16. Перебор записей выборки запроса выполняется в цикле Пока, использующем ме тод Группировка. Причем для доступа к вложенной группе выборки прежде нужно выбрать запись группы более высокого уровня. 17. Методы ИспользоватьДату, ИспользоватьВладельца и ИспользоватьРодителя употребляются для объектов, имеющих тип Справочник, которые либо возвраща ются функцией СоздатьОбъект, либо являются компонентами других объектов, например документов или других справочников, либо возвращаются параметром контекст функции ОткрытьФорму или метода ОткрытьПодбор.
6. ОБЪЕКТЫ ТИПА ПЕРИОДИЧЕСКИЙ 6.1. ПОРЯДОК ИСПОЛЬЗОВАНИЯ ОБЪЕКТА ПЕРИОДИЧЕСКИЙ Объекты типа Периодический (далее - ОП) предоставляют дополнительные воз можности по управлению периодическими реквизитами справочников и периодичес кими константами. ОП. подобно, например, спискам или таблицам значений, не вхо дят в состав метаданных, а создаются на время исполнения программы функций СоздатьОбъект, Например: оп = СоздатьОбъект("Периодический"); После создания ОП прикрепляется к определенному периодическому объекту - ре квизиту справочника или периодической константе. Это действие выполняется мето дом ИспользоватьОбъект. Метод не применяется, если выборка периодических объе ктов осуществляется по документу. Далее употребляются методы или атрибуты ОП, п озиционирующие периодической объект и управляющие его значениями. С каждым периодическим объектом связано в общем случае несколько записей, содержащих каждая значение периодического объекта, дату значения и, возможно, иные компоненты, например ссылку на документ, создавший запись. Пример 1. Выводится список периодических констант, содержащий их идентифи каторы, синонимы и значения на текущую дату. // Процедура вывода списка определенных в конфигурации периодических констант процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем всегоКонстант, дат; // Число констант в конфигурации перем идеи, син, значен, пКонст; ОчиститьОкноСообщений(); дат = ТекущаяДата(); // Создаем ОП пКонст = СоздатьОбъект("Периодический"); всегоКонстант = Метаданные.Константа(); для ин = 1 по всегоКонстант цикл если Метаданные.Константа(ин).Периодический = 0 тогда продолжить; // Значения непериодических констант не выводятся конецЕсли; син = Метаданные.Константа(ин).Синоним; // Выводим сообщения о константах, для которых задан синоним если ПустоеЗначение(син) = 0 тогда идеи = Метаданные.Константа(ин). Идентификатор; // Прикрепляем ОП к периодической константе, имеющей идентификатор иден пКонст.ИспользоватьОбъект(иден); // Получаем значение периодической константы на дату дат значен = пКонст.ЗначениеНаДату(дат); Сообщить(иден + " - " + син + " - " + значен); конецЕсли; конецЦикла; // для конецПроцедуры // Выполнить
Пример 2. Выводится история изменения константы МинимальнаяЗарплата. процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем значен, дат, пКонст; ОчиститьОкноСообшений(); // Создаем ОП пКонст = СоздатьОбъект("Периодический"); попытка // Прикрепляем ОП к периодической константе, // имеющей идентификатор МинимальнаяЗарплата пКонст.ИспользоватьОбъект("МинимальнаяЗарплата"); исключение Предупреждение(ОписаниеОшибки()); возврат; конецПопытки; // Позиционируемся перед первой записью истории константы пКонст.ВыбратьЗначения(); Сообщить("История константы МинимальнаяЗарплата"); // Метод ПолучитьЗначение позиционирует ОП на следующей записи о константе пока пКонст.ПолучитьЗначение() = 1 цикл значен = пКонст.Значение; // Значение и ДатаЗнач - атрибуты ОП дат = пКонст.ДатаЗнач; Сообщить("Минимальная заработная плата " + значен + " руб. введена с " + дат); конецЦикла // пока конецПроцедуры // Выполнить Результат: История константы МинимальнаяЗарплата. Минимальная заработная плата 83.49 руб. введена с 01.01.98 Минимальная заработная плата 132 руб. введена с 01.07.00 Минимальная заработная плата 200 руб. введена с 01.01.01 Минимальная заработная плата 300 руб. введена с 01.07.01 Пример 3. Выводится история изменений оклада Горюновой У. В. с указанием до кументов, вызвавших эти изменения. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, оп, окл, дат, док, сооб; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Ищем сотрудника во всем справочнике Сотрудники_2 если сСотр_2.НайтиПоНаименованию("Горюнова", 0) = 1 тогда // Создаем ОП оп = СоздатьОбъект("Периодический"); // Прикрепляем ОП к периодическому реквизиту Оклад найденного сотрудника оп.ИспользоватьОбъект("Оклад", сСотр_2.ТекущийЭлемент()); // Или проще: оп.ИспользоватьОбъект("Оклад", сСотр_2); // Позиционируемся перед первой записью истории окладов Горюновой У. В. оп.ВыбратьЗначения(); Сообщить("История окладов Горюновой Ульяны Валерьевны.");
// Метод ПолучитьЗначение позиционирует ОП на следующей записи // периодического реквизита Оклад пока оп.ПолучитьЗначение() = 1 цикл окл = оп.Значение; // Значение и ДатаЗнач - атрибуты ОП дат = оп.ДатаЗнач; // Документ, вызвавший изменение оклада док = оп.ТекущийДокумент(); // Текст для сообщения о документе сооб = ?(док.Выбран() = 1, " согласно документу " + оп.ТекущийДокумент(), " без оформления документа"); Сообщить("Оклад " + окл + " назначен с " + дат + сооб); конецЦикла; // пока иначе Предупреждение("Найти госпожу Горюнову У. В. не удалось."); конецЕсли; конецПроцедуры // Выполнить Результат. История окладов Горюновой Ульяны Валерьевны Оклад 2500 назначен с 20.11.01 согласно документу Приказ о приеме на работу 2 Оклад 2700 назначен с 22.11.01 согласно документу ИзменениеОклада 2 Оклад 3200 назначен с 30.11.01 без оформления документа
6.2. АТРИБУТЫ ОБЪЕКТА ПЕРИОДИЧЕСКИЙ Имеет два атрибута: Значение и ДатаЗнач, которые после создания ОП имеют пус тые значения соответственно неопределенного типа и типа Дата. После прикрепления к периодическому реквизиту или периодической константе сохраняют пустые значе ния, но атрибут Значение приобретает тип, совпадающий с типом объекта, к которому ОП прикреплен. Атрибут ДатаЗнач тип сохраняет. И наконец, получают значения по сле позиционирования на записи, отвечающей объекту, к которому прикреплен ОП. Напомним, что данные о константах и периодических реквизитах справочников хра нятся в файле 1SCONST.DBF. Примеры чтения значений атрибутов ОП приведены в предшествующем разделе. Эти же атрибуты совместно с методом Записать используются и для изменения значений периодических реквизитов и констант. Пример. Изменяется размер минимальной заработной платы, введеной с 01.07.01, с 300 на 350 руб. процедура Выполнить( ) // перем значен, дат, пКонст; ОчиститьОкноСообщений(); дат = '01.07.2001'; // Создаем ОП пКонст = СоздатьОбъект("Периодический"); попытка
Связана с кнопкой Пуск обработки Проба
// Прикрепляем ОП к периодической константе, // имеющей идентификатор МинимальнаяЗарплата пКонст.ИспользоватьОбъект("МинимальнаяЗарплата"); исключение Предупреждение(ОписаниеОшибки()); возврат; конецПопытки; если пКонст.НайтиЗначение(дат, 0) = 1 тогда пКонст.Значение = 350; // Новое значение константы на дату дат пКонст.Записать(); Сообщить("Теперь минимальная зарплата равна " + Константа. МинимальнаяЗарплата.Получить(дат) + " руб."); // Восстанавливаем значение нужной для работы константы пКонст.Значение = 300; // Новое значение константы на дату дат пКонст.Записать(); иначе Предупреждение("Константы МинимальнаяЗарплата на дату " + дат + " нет."); конецЕсли; конецПроцедуры // Выполнить
6.3. МЕТОДЫ ОБЪЕКТА ПЕРИОДИЧЕСКИЙ Позволяют читать, перебирать, искать, изменять и удалять периодические рекви зиты справочников и периодические константы. При удалении записи сразу простав ляется DBF-пометка удаления. Удаленные записи восстановлению не подлежат. Нель зя методами ОП добавлять новые значения периодических констант или реквизитов справочников. Кроме значения и даты периодического объекта, методы ОП устанавливают связь с документами, создавшими и впоследствии обновлявшими записи периодических объектов. Методы перечислены в табл. 6.1. Таблица 6.1 Методы объекта Периодический Метод флаг = оп.Использовать Объект(перОб, [элемент]);
Описание Прикрепляет ОП к периодическому реквизиту справочника (периодической константе), идентификатор которого (которой) содержится в строке перОб. Параметр элемент имеет тип Справочник. Параметр задается, если перОб является периодическим реквизитом справочника, и содержит значение текущего элемента справочника. Если перОб - это пустая строка и задан параметр элемент, то ОП доступны значения всех периодических реквизитов текущего элемента справочника, заданного параметром элемент. Метод возвращает 1, если действие завершилось успешно, или 0 -в противном случае. Пример употребления см. в разд. 6.1, 6.2
Метод
Описание
оп.НазначитьТип (тип, длина, точность);
Назначает тип периодическому объекту неопределенного типа. Параметр тип - это строка с именем базового типа, например "Число", или разновидностью агрегатного типа, например "Справочник.Образование_2" или "Документ.ИзменениеОклада". Параметр длина задается для чи слового или символьного типа и означает размер отводи мого под значение поля. Параметр точность применяется только с числовым типом и задает число знаков после де сятичной точки. Понятно, что точность < длина
значен = оп.ЗначениеНаДату (дата);
Возвращает значение периодического объекта на дату, заданную параметром дата. При исполнении метода позиция периодического объекта сохраняется. Пример употребления см. в разд. 6.1
флаг = оп.НайтиЗначение (дата, [режим]);
Осуществляет поиск значения периодического объекта на заданную параметром дата дату. Если значение не найдено и параметр режим = -1, то ищется значение на ближайшую меньшую дату, если режим = 1, то ищется значение на ближайшую большую дату, и если режим = 0, то возвращает 0. По умолчанию режим = -1. Возвращает 1 в случае успеха. Пример употребления см. в разд. 6.2
флаг = оп.ВыбратьЗначения ([датаНачала], [датаКонца]);
Открывает выборку значений периодического объекта. Параметры датаНачала и датаКонца задают соответственно даты начала и конца периода выборки. Собственно выборка осуществляется методом ПолучитьЗначение. Если параметр датаНачала не задан, то значения периодического объекта выбираются начиная с меньшей даты. Если не задан параметр датаКонца, то периодические значения выбираются вплоть до самой большой даты. Возвращает 1, если в выборке есть хотя бы одно значение, или 0 -в противном случае. Пример использования см. в разд. 6.1
флаг - оп.ВыбратьПо Документу(док);
Открывает выборку значений периодических объектов, созданных документом док. Дальнейшая выборка осуще ствляется методом ПолучитьЗначение. Возвращает 1, если в выборке есть хотя бы одно значение, или 0 - в против ном случае
флаг = оп.ПолучитьЗначение();
Перемещает позицию выборки значений периодического объекта на одну запись. По умолчанию перемещение вы полняется вниз по выборке (прямой порядок), но может быть изменено методом Обратный порядок. Возвращает 1, если следующая запись выбрана, или 0, если выборка ис черпана. Ели запись выбрана, то ее значение и дата воз вращаются атрибутами ОП Значение и ДатаЗнач. Доку мент, установивший выбранное значение, возвращается методом ТекущийДокумент; элемент справочника, кото рому принадлежит выбранный периодический реквизит, вернет метод ТекущийОбъект, а идентификатор текущего периодического реквизита или периодической константы вернет метод ТекущийРеквизит
Метод флаг = оп.ОбратныйПорядок ([порядок]);
Описание Задает порядок получения записей выборки. Если пара метр порядок отличен от нуля или не задан вовсе, то вы бор записей осуществляется в обратном порядке. Если па раметр порядок равен нулю, выборка производится в пря мом порядке. Вызывается до применения метода ВыбратьЗначения. Вернет 1 при успешном вызове или 0 в противном случае
док = оп.ТекущийДокумент();
Возвращает значение документа, установившего значение выбранной записи периодического объекта. Вернет пустое значение типа Документ, если ссылки на документ нет. Пример для метода приведен в разд. 6.1
элем = оп.ТекущийОбъект( );
Возвращает, если выбираются значения периодического реквизита, значение элемента справочника, которому пе риодический реквизит принадлежит. Вернет пустое зна чение неопределенного типа, если выбираются значения периодической константы
рекв = оп.ТекущийРеквизит();
Возвращает идентификатор выбранного периодического реквизита или периодической константы. При выборке по документу в возвращаемом значении в качестве префикса присутствует имя справочника, в котором в результате проведения документа добавляется (изменяется) значение периодического реквизита, например Сотрудники 2.0клад
номСтроки = оп.НомерСтроки();
Должен вернуть номер строки документа, получаемого методом ТекущийДокумент
флаг = оп.Записать();
Обновляет периодическое значение текущей записи. Воз вращает 1, если обновление выполнено успешно, или 0 в случае неудачи. Пример употребления см. в разд. 6.2
флаг = оп.Удалить();
Удаляет (проставляет DBF-пометку удаления) запись периодического объекта. Возвращает 1, если обновление выполнено успешно, или О-в случае неудачи
Замечание. Имя on переменной типа Периодический, употребленное в табл. 6.1 перед названиями методов, может быть произвольным. Пример. Выбираются значения периодического реквизита Оклад справочника С отрудники_2, созданные документом ИзменениеОклада № 3. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем жд, док; ОчиститьОкноСообщений(); док = СоздатьОбъект("Документ.ИзменениеОклада"); флаг = док.НайтиПоНомеру(3, Дата(0)); если флаг = 1 тогда оп = СоздатьОбъект("Периодический"); оп.ВыбратьПоДокументу(док.ТекущийДокумент()); пока оп.ПолучитьЗначение() = 1 цикл
окл = оп.Значение; // Значение и ДатаЗнач- атрибуты ОП дат = оп.ДатаЗнач; Сообщить("Оклад " + окл + " сотрудника " + оп.ТекущийОбъект( ).Наименование + " назначен с " + дат); конецЦикла; // пока иначе Предупреждение("Приказ № 3 об изменении оклада не найден"); конецЕсли; конецПроцедуры // Выполнить Результат: Оклад 3000 сотрудника Волосков Михаил Андреевич назначен с 22.11.01 Оклад 2700 сотрудника Горюнова Ульяна Валерьевна назначен с 22.11.01
6.4. ПРОСМОТР ИСТОРИИ ПЕРИОДИЧЕСКОГО ОБЪЕКТА Можно выполнить интерактивно, выбирая, например, на панели инструментов или нажимая F5. Программно осуществляется встроенной функцией Отиконку крытьФорму. Пример 1. Выводится история периодической константы МинимальнаяЗарплата. ОткрытьФорму("История.Константа.МинимальнаяЗарплата"); Результат см. на рис. 6.1.
Рис. 6.1. Результат вызова функции ОткрытьФорму Пример 2. Выводится история окладов Горюновой У. В. процедура Выполнить( ) перем сСотр_2; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); если сСотр_2.НайтиПоНаименованию("Горюнова", 0) = 1 тогда ОткрытьФорму("История.Справочник.Сотрудники_2.0клад",, сСотр_2.ТекущийЭлемент()); иначе Предупреждение("Найти госпожу Горюнову У. В. не удалось."); конецЕсли; конецПроцедуры // Выполнить Результат см. на рис. 6.2.
Рис. 6.2. История окладов Горюновой Ульяны Валерьевны
6.5. ВЫВОДЫ 1. Агрегатный тип данных Периодический применяется для периодических реквизи тов справочников и периодических констант. 2. Метод ИспользоватьОбъект, связывающий ОП с периодическим объектом, не упот ребляется, если выборка периодических объектов осуществляется по документу. 3. Вызов оп.ИспользоватьОбъект("", элемент);
4.
5. 6. 7.
8.
9.
обеспечит доступ к значениям всех периодических реквизитов текущего элемента справочника, заданного параметром элемент. Позиция периодического объекта изменяется методами НайтиЗначение, Выбрать Значения, ВыбратьПоДокументу и ПолучитьЗначение. Метод ЗначениеНаДату по зицию периодического объекта не изменяет. Записи периодического объекта, удаляемые методом Удалить, получают DBFпометку удаления и поэтому восстановлению не подлежат. Программно просмотр истории периодического объекта можно выполнить, вызы вая встроенную функцию ОткрытьФорму. Каждый вид документа порождает в информационной базе в общем случае два DBF-файла и два CDX-файла - одна пара файлов для шапки документов заданного вида, вторая для их табличной части. 1С открывает все созданные для документов и справочников DBF- и CDX-файлы. Поэтому рост числа таких файлов ведет к снижению быстродействия программы и, следовательно, нужно стремиться к снижению числа видов документов и справочников в конфигурации системы (впрочем, как и иных ее объектов). Все созданные документы перечисляются в одном, общем журнале документов, который можно просматривать по частям, давая каждой части свое имя, например ПриказыКадровые.
7. ЖУРНАЛ И ВИДЫ РАСЧЕТОВ. КАЛЕНДАРИ И ТАБЕЛЬ 7.1. ЖУРНАЛ РАСЧЕТОВ В ЗАДАЧЕ НАЧИСЛЕНИЯ ЗАРАБОТНОЙ ПЛАТЫ 7.1.1. ПОНЯТИЕ ЖУРНАЛА РАСЧЕТОВ Разного рода расчеты в 1С заносятся в журналы расчетов. Иллюстрацию методов работы с подобными журналами мы выполним на примере расчета заработной платы сотрудников из справочника Сотрудники_2, размещая расчеты и их результаты в жур нале Зарплата_2. Попутно мы рассмотрим задачу учета отработанного времени, для решения которой необходимо освоить методы работы с такими объектами 1С, как Ка лендари и Праздники. Журнал расчетов является агрегатным типом данных и применяется в 1С:Пред приятии, например в конфигурации Заработная плата и кадры, для начисления за работной платы сотрудникам, поэтому его второе название в этой задаче - журнал зарплаты. Также журнал расчетов можно приспособить и для иных целей, напри мер для расчета дивидендов акционеров АО. В общем случае в конфигурации можно задать и использовать произвольное число журналов расчетов, дав им под ходящие имена. Замечание. Далее, где это возможно, вместо общего названия "журнал расчетов" (ЖР) будем употреблять частное имя "журнал зарплаты" (ЖЗ). Каждая запись ЖЗ называется расчетом и отображает некоторое начисление, например премию, или удержание, например налог на доходы физических лиц. Расчет связан с конкретным элементом справочника сотрудников, называемым объектом расчета или просто объектом. Совокупность всех начислений и удержаний объекта за расчетный период, продолжительность которого составляет, как правило, месяц, по зволяет определить размер получаемой сотрудником заработной платы. Справочник, элементы которого являются объектами расчетов, называется вла дельцем ЖЗ. В свою очередь, элемент такого справочника, называется владельцем рас четов, объектом которых он является. Расчет характеризуется результатом, который возвращается специальными объ ектами 1С, имеющими тип ВидРасчета. Более точно: результат вычисляется в проце дуре ПровестиРасчет, принадлежащей модулю вида расчета - объекту типа ВидРасчета. Далее, упоминая объекты типа ВидРасчета, мы будем использовать сокра щение ВР.
7.1.2. ПРИМЕР ПРОСТОГО РАСЧЕТА Возьмем простой расчет - начисление премии, зависящей от проработанных часов, и на его примере рассмотрим основные свойства расчета.
Алгоритм расчета премии состоит из одной конструкции "если-то-иначе": 1. 2. 3.
Начало. Выбрать сотрудника. Если оплата сотрудника производится по окладу, то премия = (оклад * всегоЧасов / всегоЧасовПоКалендарю) * коэффициент иначе // Часовой тариф премия = тариф * всегоЧасов * коэффициент конец если 3. 4. Конец. Оклад мы возьмем из справочника Сотрудники 2 (согласно ему все наши люди "сидят" на окладе). Значение переменной всегоЧасов - число отработанных сотрудни ком часов в расчетном периоде - придется вводить (или подсчитывать по табелю) ежемесячно. Значение переменной всегоЧасовПоКалендарю - число рабочих часов в расчетном периоде - в 1С определяется при помощи объектов типа Календарь. С переменной коэффициент поступим так: установим индивидуальный для каждого сотрудника коэффициент, причем его значения в разных расчетных периодах могут не совпадать. Если коэффициент постоянен и одинаков для всех работников, то его можно добавить в список констант, причем периодических. Из приведенного обзора входных данных алгоритма следует, что справочник Сотрудники_2 нужно дополнить еще одним реквизитом - Календарь. Тогда, зная кален дарь, то есть зная продолжительность рабочей недели и рабочего дня сотрудника, а также даты выходных и праздников в расчетном периоде, легко вычислить значение переменной всегоЧасовПоКалендарю. В 1С его вернет следующий код: // Возвращает число рабочих часов в расчетном периоде по календарю // кален - параметр типа Календарь; жз - переменная типа ЖурналРасчетов.Зарплата // датаП - дата, принадлежащая текущему расчетному периоду ЖЗ функция НайтиВсегоЧасовПоКалендарю(кален, жз, датаП) возврат кален.Часов(жз.НачалоПериодаПоДате(датаП), жз.КонецПериодаПоДате(датаП)); конецфункции // НайтиВсегоЧасовПоКалендарю // Запустим функцию НайтиВсегоЧасовПоКалендарю из обработки Проба процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем кален, жз, датаП, всегоЧасовПоКалендарю; кален = СоздатьОбъект("Календарь.Рабочие_2"); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // 3 марта 2001 г. - дата, принадлежащая текущему расчетному периоду ЖЗ датаП = '03.03.2001'; всегоЧасовПоКалендарю = НайтиВсегоЧасовПоКалендарю(кален, жз, датаП); Сообщить("Число рабочих часов в марте 2001 г. равно " + всегоЧасовПоКалендарю); конецПроцедуры // Выполнить Результат; Число рабочих часов в марте 2001 г. равно 167 Такой результат получен, потому что рабочие работают на пятидневке; продолжи тельность рабочего дня - 8 ч; в марте один праздник - 8 Марта; продолжительность пред праздничного дня -7 ч. Тогда имеем в марте 2001 г. 21 рабочий день и всегоЧасовПоКа лендарю = 20 * 8 + 7 = 167. Более подробно об управлении календарями см. в разд. 7.5.
Вернемся, однако, к премии. В ЖЗ ее расчет может отобразиться в виде результа та, представленного на рис. 7.1. Но чтобы его получить, придется выполнить некото рую цепочку действий. Эта цепочка должна отражать реальные процессы начисления зарплаты в целом и премии в частности.
Рис. 7.1. Премия Добрецова Бориса Юрьевича.
Как правило, вопросы премирования отражаются в приказе о премии по отдельно му подразделению или по предприятию в целом. Чтобы ускорить создание этого доку мента, в нем следует предусмотреть реквизиты Сотрудник, всего Часов, коэффициент, премия, а также даты, задающие период, за который премия начисляется, называемые датами начала и окончания расчета. Период, в которой эти даты входят, называется периодом действия расчета. В 1С период действия расчета всегда принадлежит одно му расчетному периоду. Кроме того, документ должен ссылаться на процедуру, выполняющую по введен ным данным расчет премии. В самой же процедуре по известному календарю должен осуществляться расчет значения переменной всегоЧасовПоКалендарю. Таким образом, табличная часть документа имеет вид, представленный в табл. 7.1. Таблица 7.1 Табличная часть документа ПриказОПремии Сотрудник Добрецов Борис Юрьевич
Отработанные часы 167
Коэффициент
10
Премия 1,670.00
Графа Премия заполняется автоматически и для редактирования недоступна. После формирования документа, в котором скорее всего будут фигурировать фа милии нескольких сотрудников, выполняется его проведение и, возможно, приказ пе чатается. Сам же документ разместим в журнале Расчеты, который является частью общего журнала документов. Результатом проведения документа является добавление расчета в ЖЗ (см. рис. 7.1). Список приведенных выше реквизитов документа не является исчерпывающим: для бухгалтерского учета начисления и удержания разных видов нужно отнести на со ответствующие счета (дебет и кредит). То есть в документе, в его шапке, нужно доба вить реквизит ХозОп (код хозяйственной операции ). Этот код можно выбирать из справочника ХозяйственнаяОперация. Однако известно, что один и тот же ВР свя зан с определенной хозяйственной операцией. Поэтому правильнее создать дополни тельный справочник, например ХозОпДляВР, хранящий для каждого вида расчета его хозяйственную операцию. * Хозяйственные операции - это отдельные хозяйственные действия, вызывающие изменения в объеме, составе, размещении и использовании средств, а также в составе и назначении ис точников этих средств. Так, при покупке материалов происходит увеличение их запасов и уменьшение денежных средств.
7.1.3. НЕКОТОРЫЕ СВОЙСТВА ЖУРНАЛА ЗАРПЛАТЫ И ЕГО РАСЧЕТОВ • • •
• • • • • •
Журнал зарплаты: характеризуется расчетным периодом; состоит из записей - расчетов; содержит для каждого объекта (сотрудника) все причитающиеся ему в расчетном пе риоде начисления и полагающиеся удержания. Каждый расчет ЖЗ: регистрируется в некотором периоде расчета ЖЗ; имеет дату начала и дату окончания, которые в обязательном порядке должны нахо диться в пределах одного расчетного периода; имеет период действия; связан с документом, порождающим расчет; связан с объектом расчета; связан с ВР, в который в том числе входит и процедура, выполняющая расчет и воз вращающая его результат.
Последние 3 ссылки попадают в ЖЗ из документа, порождающего расчет. Период действия определяется по датам начала и окончания расчета. Сами же даты либо за даются в родительском документе, либо устанавливаются равными соответственно началу и концу текущего расчетного периода, продолжительность которого мы уста новим равной одному месяцу. Замечание. Период действия расчета может лежать в ином, отличном от текущего расчетном периоде. Так, расчет отпуска с 1 июня продолжительностью 40 календар ных дней осуществляется в мае. Поэтому, начисляя отпускные, система должна, вопервых, разбить исходный расчет на два, задав для первого в качестве периода дейст вия июнь, а для второго - июль (за это отвечает атрибут ЖЗ ПериодДействия), опреде лив соответствующим образом атрибуты ЖЗ ДатаНачала и ДатаОкончания. Вовторых, разместить обе части в майском расчетном периоде ЖЗ (за это отвечает атри бут ЖР ПериодРегистрации). Тогда процедура расчета выберет верные календари, расходы бухгалтерского учета будут отнесены на соответствующие периоды, а отпу скник увидит в расчетном листке корректные даты.
7.1.4. МЕРОПРИЯТИЯ ПО СОЗДАНИЮ ЖУРНАЛА ЗАРПЛАТЫ Общий порядок начисления заработной платы, а также рассмотренный выше алго ритм отдельного расчета позволяют определить и последовательность действий, кото рые необходимо предпринять для автоматизации ведения ЖЗ. Очевидно, что для этого потребуется: 1) выделить все имеющиеся на предприятии виды начислений и удержаний; 2) оформить каждый вид начисления (удержания) как объект 1С типа ВидРасчета; 3) разработать документы 1С, автоматизирующие учет отработанного времени;
4) дополнить конфигурацию вспомогательным справочником хозяйственных опера ций для ВР предприятия; 5) создать ЖЗ; 6) сконструировать отчеты по выполняемым расчетам, в том числе расчетный листок сотрудника.
7.2. ЗАДАНИЕ РАСЧЕТНОГО ПЕРИОДА ЖУРНАЛА ЗАРПЛАТЫ Завершив очередной расчетный период (РП) или открыв ЖЗ в первый раз, мы, чтобы продолжить расчеты, должны задать новый РП. Для этого при открытом ЖЗ мы можем воспользоваться пунктом Сменить период расчета, принадлежащим колонке Действия меню системы, или иконкой 03, находящейся на панели инструментов диа лога формы списка ЖЗ. Появившийся диалог (рис. 7.2) позволит перейти к следующе му периоду.
Рис. 7.2. Задание расчетного периода
Смена периода является ответственной процедурой, поскольку приводит к закры тию текущего РП, в результате чего его записи переносятся в разряд архивных и ока зываются доступными только для просмотра (если пользоваться стандартными инте рактивными средствами). Программно новый РП задается методом журналов расчетов УстановитьТекущийПериод. Замечание. Иконки, сопровождающие расчеты архивов, отображаются в ЖЗ на си нем фоне. Исключение составляют иконки фиксированных расчетов, например можно зафиксировать результаты выплат через кассу. Фон таких иконок - желтый. Если же произошла ошибка и новый РП установлен, когда еще не завершенное расчеты в старом, то возврат к прежнему РП при помощи формы рис. 7.2 приведет к обнулению результатов его расчетов (кроме фиксированных). Этого можно избежать, если вернуться назад программно, вызвав метод УстановитьТекущийПериод и задав в нем второй параметр равным нулю. Например: процедура Выполнить( ) перем жз, пер, флаг; // Откат назад без отработки системных действий жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); пер = жз.ТекущийПериод(); пер = пер.ПрибавитьПериод(-1); // Получаем предыдущий период // Возврат к прежнему РП флаг = жз.УстановитьТекущийПериод(пер, 0); конецПроцедуры // Выполнить
Организовать просмотр прошлых периодов можно, задав соответствующую глу бину просмотра архива (рис. 7.3).
Для этого при открытом ЖЗ воспользуйтесь пунктом Задать глубину просмотра архива, входящим в колонку Действия меню системы, или иконкой
7.3. ВИДЫ РАСЧЕТОВ 7.3.1. СВОЙСТВА ВИДОВ РАСЧЕТОВ 7.3.1.1. НЕСОВМЕСТИМЫЕ ВИДЫ РАСЧЕТОВ. ВЫТЕСНЕНИЕ РАСЧЕТА Введем еще несколько понятий. Назовем интервалом действия расчета промежуток времени между датой начала и датой окончания расчета, включающий и сами даты. Назовем виды расчетов (ВР) несовместимыми, если соответствующие им расчеты ЖЗ не могут иметь пересекающихся интервалов действия. Расчеты с такими ВР также будем называть несовместимыми. Несовместимость расчетов преодолевается при их записи в ЖЗ за счет эффекта вытеснения. Рассмотрим ситуацию, когда расчеты А и Б являются несовместимыми, расчет А с приведенным на рис. 7.4, а интервалом действия уже введен и вводится расчет Б. Возможны случаи: 1. Интервалы действия расчетов различны. Тогда расчет Б будет благополучно вве ден. 2. Интервалы действия расчетов совпадают, тогда в ЖЗ останется один расчет: либо А, либо Б. Это зависит от направления вытеснения, которое устанавливается в конфигурации для несовместимых ВР при их создании или редактировании. На пример, если Б вытесняет А, то А удаляется из ЖЗ, а Б вводится. При обратном направлении вытеснения Б введен не будет. 3. Интервал действия расчета Б полностью лежит в интервале действия расчета А, но А вытесняет Б. Расчет Б не вводится. 4. Интервал действия расчета Б полностью лежит в интервале действия расчета А, но теперь уже Б вытесняет А. Расчет Б вводится, расчет А разбивается на два рас чета - А1 и А2, такие, что интервал действия расчета А1 лежит на временной оси слева от интервала расчета Б, а интервал А2 - справа (рис. 7.4, б).
5. Интервалы действия расчетов А и Б частично пересекаются, причем А вытесняет Б. Тогда А остается полностью и вводится часть Б1 расчета Б, такая, что интервал этой части соприкасается на временной оси с интервалом А либо слева, либо спра ва (в зависимости от характера пересечения) (рис. 7.4, в). 6. Интервалы действия расчетов А и Б частично пересекаются, но теперь уже Б вытесняет А. Тогда Б вводится полностью и остается часть А1 расчета А, такая, что интервал этой части соприкасается на временной оси с интервалом Б либо слева, либо справа (рис. 7.4, г).
Рис. 7.4. Вытеснение расчета: а - интервал действия расчета А до ввода Б; б - Б полностью лежит в А и вытесняет его; в -А и Б частично пересекаются, А вытесняет г - А и Б частично пересекаются, Б вытесняет А В качестве расчета Б может выступать как расчет, имеющий с А тот же ВР, так и расчет с иным ВР. ВР, который может вытеснять сам себя, называется самовытесняющимся. Настройка вытеснения ВР (направление вытеснения), так же как и приоритет, задаютcat в конфигурации при создании или редактировании ВР. В программе можно, применив методы видов расчетов ВытесняетВидРасчета и ВытесняетсяВидомРасчета, узнать, каким образом взаимодействуют два расчета, употребленные с этими методами. 7.3.1.2. ДЛИННЫЕ РАСЧЕТЫ Расчет называется длинным, если он начинается в одном расчетном периоде, а заканчивается в другом. Длинные расчеты могут существовать только в документах. В 1С длинный расчет при вводе разбивается на обычные, такие, что каждый из них принадлежит к одному расчетному периоду (рис. 7.5).
Рис. 7.5. Длинный расчет А разбивается в ЖЗ на два обычных - А1 и А2, лежащих соответственно в расчетных периодах РП1 и РП2
7.3.2. ПЕРЕЧЕНЬ ВИДОВ РАСЧЕТОВ ПРЕДПРИЯТИЯ Для определенности условимся, что на нашем предприятии используются приве денные в табл. 7.2 ВР, в которой сокращение ХО, употребленное в заголовке 5-го столбца, расшифровывается как хозяйственная операция. Таблица 7.2 ВР предприятия ВР
Идентификатор / Синоним
Результат
Дебет Начальное сальдо прежнего перио ВР0 НачСальдо_2 / Начальное сальдо да + Дебет - Кредит + Сторно ВР1 Оклад_2 / Оклад Оклад * всегоЧасов / всегоЧасовПоКалендарю ВР2 Тариф_2 / Тариф Тариф * всегоЧасов ВРЗ ПремияКоэф_2/ всегоЧасов * к3, где к3 - устанав Премия коэффи ливаемый руководителем циентом коэффициент. По умолчанию к3 = 10 для всех сотрудников ВР4 ПремияСум_2 / Сумма премии Премия суммой ВР5 Премия1234_2/ (ВР1 | ВР2 + ВРЗ + ВР4) * к5, где Премия 1234 к5 - постоянный для всех сотруд ников коэффициент Кредит ВР6 НДФЛ_2/Налог (ВР1 | ВР2 + ВРЗ + ВР4 + ВР5) * на доходы физи ставкаНалога ческих лиц ВР7 ВБанк_2 / Пере Целая часть от (ВР0 + ВР1 | ВР2 + числение в банк ВРЗ + ВР4 + ВР5 - ВР6)
Очеред ность 1
ХО
Документ
1
2013000 НачПериода_2 2013000 Табель
1 5
2013000 " 2013001 Премия
1
2013001
"
10
2013001
"
15
2017002 Табель
20
2300100 "
Замечания: 1. Начальное сальдо - это долг за предприятием, если больше нуля, или долг за ра ботником - в противном случае 2. Коэффициент к5 и ставкаНалога добавляются в конфигурацию 1С как периодиче ские константы. 3. ВР Тариф_2, поскольку все сотрудники справочника Сотрудники_2 "сидят" на ок ладе, в конфигурацию не вводится. Чтобы им воспользоваться, нужно модифици ровать справочник Сотрудники_2, добавив в него реквизиты флагОклада и Тариф, задавая флагОклада равным единице, если оплата сотрудника осуществляется по окладу, или равным нулю, если по тарифу. Приведенных ВР вполне достаточно для демонстрации средств встроенного языка 1С, поддерживающих расчеты и их журнал.
Расчеты с ВР НачСальдо_2, Оклад_2 | Тариф_2, НДФЛ_2 и ВБанк_2 являются обя зательными и присутствуют в ЖЗ для каждого сотрудника. В принципе они могли бы вводиться одним документом, например Табель. Однако с целью демонстрации мето дов ВР и журнала расчетов начальное сальдо будем вводить непосредственно в модуле формы списка ЖЗ, связывая с ВР НачСальдо_2 документ НачПериода_2. Это допусти мо, поскольку начальное сальдо не зависит от иных расчетов текущего месяца. Ос тальные обязательные расчеты будет добавлять в ЖЗ документ Табель. Премии являются дополнительными расчетами и могут не появляться в ЖЗ (или появляться не в полном объеме) для отдельных сотрудников. Премия суммой в общем случае может быть назначена сотруднику несколько раз по разным поводам. Это об стоятельство надо учесть при расчете зависящих от этой премии записей, то есть рас четов с ВР Премия1234_2, Н Д Ф Л 2 и ВБанк_2. Для всех премий предусмотрим один документ Премия_2. Результаты ВР5-7 зависят от результатов других расчетов, поэтому подобного ро да ВР называются зависимыми. Они, понятно, должны оцениваться после каждого из менения результатов расчетов, от которых они зависят. Это достигается за счет про граммирования перерасчетов. Все приведенные в табл. 7.2 расчеты, конечно же, реализуются интерактивными средствами 1С. Но наша задача - освоение возможностей встроенного языка програм мирования, поэтому мы остановимся на вопросах создания документов, порождающих расчеты, проведения этих документов, то есть ввода в ЖЗ расчетов и организации по следующих вычислений. Документы, связанные с расчетами, мы будем размещать в перечисленных в табл. 7.3 журналах документов. Таблица 7.3 Журналы документов, порождающих расчеты Журнал документов Расчеты Табель
Ускоритель
Документ НачПериода_2, Премия_2
Alt+U Alt+B
Табель
7.3.3. ДОСТУП К ДОКУМЕНТАМ, ВВОДЯЩИМ РАСЧЕТЫ В ЖУРНАЛ ЗАРПЛАТЫ В конфигурацию 1С подсистемы Заработная плата и кадры занесено более 200 ВР. Их точное число вернет вызов Сообщить(Метаданные.ВидРасчета( ));
//
Сообщит о числе ВР в конфигурации
Число документов в конфигурации существенно меньше - несколько десятков. Не все из них порождают расчеты. Однако немало и таких. Выбор документа, вводя щего в ЖЗ расчеты, из списка, содержащего несколько десятков наименований, обре менителен. Поэтому поступим так. Разместим в новом перечислении В Р 2 (рис. 7.6) имена документов из табл. 7.2, вводящих расчеты.
Рис. 7.6. Перечисление для выбора вводимого в ЖЗ расчета В представлениях значений перечисления В Р 2 отобразим имена соответствую щих ВР: • Начальное сальдо (представление элемента НачСальдо); • Расчеты Оклад/Тариф, НДФЛ и ВБанк (представление элемента Табель); • Премия коэффициентом, суммой и 1234 (представление элемента Премия). В форме списка ЖЗ, вопросы конструирования которой рассматриваются в разд. 7.4, разместим кнопку Ввод расчета, связав с ней процедуру, предоставляющую возможность выбрать из перечисления ВР_2 необходимый для ввода расчетов документ.
7.3.4. ПОРЯДОК ВВОДА И ВЫЧИСЛЕНИЯ РАСЧЕТОВ ЖУРНАЛА ЗАРПЛАТЫ. ПРИОРИТЕТ ВИДОВ РАСЧЕТОВ Последовательность ввода расчетов в ЖЗ произвольная. Однако в таком случае желательно предусмотреть автоматический перерасчет записей после изменения ре зультата расчета, от которого эти записи зависят. В нашем случае зависимыми являют ся ВР Премия1234_2, НДФЛ_2 и ВБанк_2. При наличии таких перерасчетов оконча тельный верный результат будет получаться и без исполнения команды Рассчитать объект, которая находит результат каждого расчета объекта, не меняя значения фикси рованных и исправленных вручную результатов. Порядок вычисления результатов расчетов уже не может быть произвольным, и для его регулирования в 1С каждый ВР снабжается приоритетом, задаваемым целым числом и определяющим очередность выполнения расчета. Причем чем меньше чис ло, указанное для приоритета, тем скорее произойдет вызов процедуры, выполняющей расчет. Очередности (приоритеты) ВР предприятия указаны в табл. 7.2 и пояснений, пожалуй, не требуют. Заметим только, что между значениями приоритетов оставляют промежутки, чтобы при необходимости разместить в них приоритеты вновь появившихся ВР. Чтобы продемонстрировать методы работы с ЖЗ и его расчетами, определим все расчеты как самовытесняющиеся, а также зададим ВР4 (премия суммой) как вытес няющий ВРЗ (премия коэффициентом).
7.3.5. ДОБАВЛЕНИЕ ВИДОВ РАСЧЕТОВ В КОНФИГУРАЦИЮ Каждое начисление (удержание) задается в виде отдельного объекта типа ВидРасчета, хотя в принципе некоторые начисления или удержания можно было бы оформить как один объект, например для начисления всех премий создать в конфигурации ВР Премия и передавать процедуре ПровестиРасчет модуля ВР флаг, указывающий на вид начисляемой премии.
Однако разделение расчетов оправданно, поскольку позволяет задавать приоритеты ВР, управлять вытеснением и перерасчетом, формировать подходящие группы расчетов. Последуем и мы этой линии разделения ВР. Откроем конфигурацию, переместим ся на закладке Метаданные в раздел Виды расчетов и введем новые ВР, а результат отобразим на рис. 7.7.
Рис. 7.7. ВР сотрудников из справочника Сотрудники_2. По имеющейся у нас информации мы можем пока что назначить введенным ВР ука занный в табл. 7.2 приоритет, и присвоить идентификаторам ВР имена и синонимы. Замечание. ВР хранятся в файле конфигурации системы 1CV7.MD. 7.3.6. В И Д РАСЧЕТА НАЧАЛЬНОЕ САЛЬДО 7.3.6.1. СВОЙСТВА ВИДА РАСЧЕТА НАЧАЛЬНОЕ САЛЬДО Зададим их в соответствии с рис. 7.8.
Рис. 7.8. Свойства ВР Начальное сальдо Это простой по свойствам ВР. Он не является вытесняющим, длинным или зависи мым. Правда, он является обязательным и самовытесняющимся. Даты начала и окон чания расчетов с ВР Начальное сальдо всех сотрудников одинаковы. Для определен ности установим их равными дате начала текущего периода, возвращаемой методом ЖР НачалоТекущегоПериода. Замечание. Идентификатор, синоним и комментарий ВР выводятся следующими сообщениями:
Сообщить(ВидРасчета.НачСальдо_2.Код); Сообщить("" + ВидРасчета.НачСальдо_2); Сообщить(ВидРасчета.НачСальдо_2.Наименование); Результат: НачСальдо_2 Начальное сальдо Долг за предприятием или сотрудником 7.3.6.2. МОДУЛЬ ВИДА РАСЧЕТА НАЧАЛЬНОЕ САЛЬДО Проста и формула расчета результата ВР Начальное сальдо: // начСальдоСтар - начальное сальдо предшествующего расчетного периода начСальдоНовое = начСальдоСтар + Дебет - Кредит + Сторно; В нашем случае Дебет = Оклад_2 | Тариф_2 + ПремияКоэф_2 + ПремияСум_2 + Премия 1234_2; Кредит = НДФЛ_2 + ВБанк_2; // Сторно - результаты расчетов, возникших при исправлении ошибок При отсутствии какого-либо ВР соответствующее слагаемое в формуле расчета начального сальдо равно нулю. В модуль ВР Начальное сальдо включим следующий код: функция НачСальдо(Сотрудник, нтп) далее процедура ПровестиРасчет() // Выполняется при проведении расчета // Результат, Объект - атрибуты ЖЗ Результат = НачСальдо(Объект, НачалоТекущегоПериода()); конецПроцедуры // Считает начальное сальдо функция НачСальдо(Сотрудник, нтп) жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); сальдо = 0; жз.ВыбратьПериодПоОбъекту(Сотрудник, нтп- 1); пока жз.ПолучитьЗапись( )=1 цикл если жз.ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда сальдо = сальдо - жз.Результат; иначеЕсли жз.ВидРасч = ВидРасчета.ВБанк_2 тогда сальдо = сальдо - жз.Результат; иначе // Дебет или сторнированный расчет сальдо = сальдо + жз.Результат; конецЕсли; конецЦикла; // пока возврат сальдо; конецФункции // НачСальдо
В функции НачСальдо есть ссылка на группу ВР ВсеУдержания_2. Технология объединения ВР в группы, используемая 1 С, весьма продуктивна, позволяя, в частнос ти, существенно сокращать код создаваемых программ. Внешне группа ВР представ ляется как список в нее входящих ВР. Но по существу группа ВР - это объект 1С, об ладающий атрибутами и методами. В нашем случае в группу ВсеУдержания_2 войдет лишь ВР НДФЛ_2 (рис. 7.9), и мы вместо строки кода если жз.ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда могли бы записать если жз.ВидРасч = ВидРасчета.НДФЛ_2 тогда
Рис. 7.9. Новая группа ВР ВсеУдержания_2 Однако чтобы сохранить работоспособность кода на случай добавления новых удер жаний, оставим его без изменений. Конечно, нужно всегда помнить: каждое новое удер жание должно быть добавлено в группу ВсеУдержания_2, иначе ВР Начальное сальдо и все иные ссылающиеся на эту группу программы будут давать неверные результаты. Возможность включения ВР в существующие в конфигурации группы предостав ляется 1С уже при вводе ВР (см. рис. 7.8). Однако чтобы это действие выполнять осоз нанно, нужно иметь информацию, какие группы и для каких целей используются. Ее можно сосредоточить в описании группы ВР (рис. 7.10), корректируя его каждый раз при добавлении или удалении ссылок на группу.
Рис. 7.10. Включаем в описание группы ВР сообщение об объекте, в котором есть ссылка на группу Как мы ранее условились, ввод расчетов с ВР Начальное сальдо будет осуществ ляться непосредственно из модуля формы списка ЖЗ. Это выполняется методами ЖР Новая, УстановитьРеквизит и Записать. Специфика метода Записать в том, что он не проверяет правила вытеснения и перерасчета ВР, даже если они есть. Поскольку ВР Начальное сальдо является самовытесняющимся, то надо либо запретить ввод началь ного сальдо, если расчет с таким ВР объект уже имеет, либо заменить существующий
на новый. Мы остановимся на первом варианте. Он логичен, поскольку начальное сальдо вычисляется по данным закрытого, отнесенного в архив периода и не должно изменяться во времени. При вводе ВР Начальное сальдо сразу же будем вычислять и фиксировать его зна чение, вызывая метод ЖР ВыполнитьРасчет и устанавливая в атрибут ЖР Фиксирова на число 1. 7.3.6.3.
ДОКУМЕНТ НачПериода_2
Назначение документа НачПериода_2 - это фиксация факта смены расчетного пе риода: каждая плановая смена должна сопровождаться расчетом начального сальдо сотрудников. На документ НачПериода_2 согласно табл. 7.2 должны ссылаться расче ты с ВР НачСальдо_2. Эта ссылка фиксируется атрибутами ЖР Документ и РодительскийДокумент и носит формальный характер, ибо без нее нельзя ввести расчет в ЖЗ. С другой стороны, такой документ полезен, поскольку в нем будут отражаться такие важные операции, как смены расчетных периодов. Документ будем фиксировать в новом журнале Расчеты, в котором будут разме щаться и все иные, кроме Табеля, документы, вводящие расчеты в ЖЗ Зарплата_2. Удобнее всего факт смены расчетного периода было бы выполнять в предопреде ленной процедуре модуля формы ЖР ПриСменеРасчетногоПериода. Однако такой процедуры нет, но взамен есть одноименная процедура глобального модуля. Именно в нее мы поместим вызов процедуры ФиксироватьСменуРП. Чтобы гарантировать вы зов этой процедуры после выполнения всех проверок, код ее вызова нужно располо жить в конце предопределенной процедуры ПриСменеРасчетногоПериода. Вызов процедуры ФиксироватьСменуРП может быть таким: // Это предварительное описание разместим до кода // предопределенной процедуры ПриСменеРасчетногоПериода процедура ФиксироватьСменуРЩжз, период) далее процедура ПриСменеРасчетногоПериода(Жрн, Период) // Меняем расчетный период в ЖЗ Зарплата_2 если Жрн.Вид() = "Зарплата_2" тогда ФиксироватьСменуРЩЖрн, Период); конецЕсли; конецПроцедуры // ПриСменеРасчетногоПериода Саму же процедуру ФиксироватьСменуРП расположим в конце глобального модуля: процедура ФиксироватьСменуРЩжз, период) // Тело процедуры ФиксироватьСменуРП конецПроцедуры // ФиксироватьСменуРП Написание кода процедуры завершим после создания документа НачПериода_2, в табличной части которого определим 3 реквизита: НовПериод, ДатаУстановки и Пользователь. Первому и третьему назначим символьный тип с длиной строки в 15 символов, а второму - тип Дата. Такие реквизиты позволят нам фиксировать не только факт смены расчетного периода, но и лицо, смену осуществившее.
Все столбцы табличной части документа сделаем недоступными для редактирова ния, а в шапке документа разместим лишь заголовок таблицы (рис. 7.11).
Рис. 7.11. Диалог формы документа Начальное сальдо Иные свойства документа определим в соответствии с рис. 7.12.
Рис. 7.12. Свойства документа НачПериода_2 Теперь ясен и текст процедуры ФиксироватьСменуРП: // Заносит данные о смене расчетного периода в документ НачПериода_2 процедура ФиксироватьСменуРП(жз, период) // период - переменная типа РасчетныйПериод перем док; док = СоздатьОбъект("Документ.НачПериода_2"); // Номер документа не меняется и всегда равен единице док.НайтиПоНомеру(1, Дата(0)); если док.Выбран( ) = 1 тогда док.НоваяСтрока(); // Добавляем в документ новую строку док.НовПериод = жз.ОписательПериода(период.ДатаНачала); // Дата смены расчетного периода ЖЗ Зарплата_2 док.ДатаУстановки = ТекущаяДата();
док.Пользователь = ИмяПользователя( ); док.Записать(); // Открываем документ НачПериода_2 под номером 1 для контрольного просмотра ОткрытьФорму(док.ТекущийДокумент()); иначе Предупреждение("Документа НачПериода_2 под номером 1 нет."); конецЕсли; конецПроцедуры // ФиксироватьСменуРП Чтобы предотвратить интерактивное редактирование документа НачПериода_2, снабдим модуль формы документа следующими предопределенными процедурами: процедура ПриУдаленииСтроки() Предупреждение("В этом документе нельзя удалять записи."); СтатусВозврата(0); конецПроцедуры // ПриУдаленииСтроки процедура ПриВводеСтроки() Предупреждение("В этот документ нельзя добавлять записи."); СтатусВозврата(0); конецПроцедуры // ПриВводеСтроки процедура ПриОткрытии() форма.ПанельИнструментов(0); конецПроцедуры // ПриОткрытии
//
Отключаем панель инструментов
Запрет на редактирование данных мы наложили, сделав недоступными столбцы табличной части документа. Для создания и записи в журнал Расчеты документа НачПериода_2 с номером 1 воспользуемся обработкой Проба, разместив в ней на этот раз следующий код: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем док; док = СоздатьОбъект("Документ.НачПериода_2"); // Номер документа не меняется и всегда равен единице док.НайтиПоНомеру(1, Дата(0)); если док.Выбран() = 1 тогда Предупреждение("Документ НачПериода_2 под номером 1 уже есть."); иначе док.Новый(); док.НомерДок = 1; док.ДатаДок = ТекущаяДата(); док.Записать(); ОткрытьФорму("Журнал.Расчеты"); конецЕсли; конецПроцедуры // Выполнить Результат приведен на рис. 7.13
Рис. 7.13. Создан документ НачПериода_2 под номером 1
В дальнейшем иных документов вида НачПериода_2 создаваться не будет, а все записи о смене расчетных периодов будут направляться в документ № 1.
7.3.7. СПРАВОЧНИК ХОЗЯЙСТВЕННЫХ ОПЕРАЦИЙ ВИДОВ РАСЧЕТОВ С каждым ВР связана некоторая хозяйственная операция. Эту взаимосвязь отобра зим в справочнике ХозОпДляВР, разместив в нем два дополнительных реквизита: 1) ВР типа ВидРасчета; 2) ХозОп с разновидностью типа Справочник.ХозяйственнаяОперация. Число уровней справочника - 1. Длину атрибута Код установим равной трем, На именования - нулю. Для редактирования используем форму списка (рис. 7.14).
Рис. 7.14. Хозяйственные операции ВР
Вызов справочников будем осуществлять из колонки Бухучет зарплаты меню ин терфейса Ученик (рис. 7.15).
Рис. 7.15. Вспомогательные справочники для ведения бухучета зарплаты Справочник ХозяйственнаяОперация имеется в базовой конфигурации Заработная плата и кадры и содержит, если заполнен, приведенные на рис. 7.16 данные.
Рис. 7.16. Фрагмент справочника ХозяйственнаяОперация
Значения полей справочника ХозОпДляВР выбираются из раскрывающихся пре доставляемых 1С списков. После заполнения, которое производится по данным табл. 7.2, справочник отобразится в соответствии с рис. 7.14. Замечание. Данные, необходимые для бухгалтерского учета заработной платы и, в частности, для использования справочником ХозяйственнаяОперация, передаются в конфигурацию Заработная плата и кадры из конфигурации Бухгалтерский учет следую щим образом. Первоначально из конфигурации Бухгалтерский учет в результате выполне ния цепочки Сервис - Обмен данными - Выгрузка данных в конфигурацию "Зарплата + Кадры" в текстовый файл, например D:\lCv77\lsbtrans.txt, выгружаются объекты бухгал терского учета и счета (из плана счетов) и соответствующие им статьи аналитического учета. (Если счета не выбраны, то из бухгалтерии будет выгружен заданный по умолчанию набор счетов.) Затем данные обменного файла загружаются программой Заработная плата и кадры в результате применения цепочки Сервис - Обмен данными - Загрузка данных выбор файла с данными, например D:\lCv77\lsbtrans.txt, - Загрузить. Принятые данные используются в процессе заполнения справочника ХозяйственнаяОперация. Создадим теперь журнал Зарплата_2 и введем в него расчеты с ВР НачСальдо_2.
7.4. ПОСТРОЕНИЕ ЖУРНАЛА ЗАРПЛАТА 7.4.1. АТРИБУТЫ ЖУРНАЛА РАСЧЕТОВ И ЕГО ПЕРИОДА ЖР - это вполне готовый к употреблению объект, снабженный большим числом атрибутов и методов. Атрибуты журнала - это и есть те самые графы, заполняемые для каждого расчета (записи) журнала. Поэтому, учитывая хорошую оснащенность журнала, нам не придется потратить много сил на создание свой версии ЖЗ (напомним, что ЖЗ - это одна из возможных реализаций ЖР), но понадобится детально ознакомиться с заложенными в него разра ботчиками 1С свойствами. Прежде остановимся на атрибутах, разместив их в табл. 7.4. Таблица 7.4 Атрибуты ЖР и его периода Атрибут
Описание Атрибуты расчетного периода ЖР ДатаНачала Дата начала расчетного периода (РП). Устанавливается при смене РП. Является компонентом объекта типа РасчетныйПериод ДатаОкончания Дата окончания РП. Устанавливается при его смене. Является компонентом объекта типа РасчетныйПериод ОписательПериода Символьное представление РП. Так, если ДатаНачала = '01.12.2001', а ДатаОкончания = '31.12.2001', то ОписательПе риода = "Декабрь 2001 г.". Полезен при формировании отчетов Атрибуты ЖР Документ Ссылка на документ, на основании которого расчет введен в ЖР. Автоматически заполняется в момент проведения документа. Если документа-основания нет, то совпадает с родительским документом
Тип Дата "
Символь ный
Документ
Атрибут Родительский Документ Объект
ВидРасч ДатаНачала
ДатаОкончания
ПериодДействия
Период Регистрации
Тип
Описание Документ, который ввел расчет в ЖР. Автоматически заполняется в момент проведения документа, добавляющего расчет в ЖР Ссылка на элемент справочника-объекта расчета. В нашем случае имеет разновидность типа Справочник.Сотрудники_2. Для сотрудников из этого справочника в ЖЗ заносятся записи о начислениях и удержаниях, из которых складывается зар плата Ссылка на процедуру, осуществляющую расчет текущей записи. Предназначен для чтения Дата начала действия расчета. Определяется при проведении родительского документа; если дата в нем не задана, то ста новится равной дате начала текущего расчетного периода. Предназначен для чтения Дата окончания действия расчета. Определяется так же, как и ДатаНачала. Значения атрибутов ДатаНачала и ДатаОкон чания каждого расчета лежат в пределах одного расчетного периода. Предназначен для чтения РП, в который попадают даты начала и окончания действия расчета. Пользователем не устанавливается, а определяется значениями атрибутов ЖР ДатаНачала и ДатаОкончания. Предназначен для чтения РП, в котором расчет введен в ЖР. Пользователем не уста навливается. Предназначен для чтения
"
Зависит от вида объекта
Вид Расчета Дата
"
Расчетный Период "
Сторно - бухгалтерская запись, сделанная красными чернилами для исправления ошибок пу тем внесения дополнительной бухгалтерской проводки отрицательными числами. При подсче те итогов числа, записанные красными чернилами, вычитаются.
Наши ближайшие задачи - это определение дополнительных реквизитов ЖЗ и кон струирование журнала.
7.4.2. ДОБАВЛЕНИЕ ЖУРНАЛА ЗАРПЛАТЫ В КОНФИГУРАЦИЮ СИСТЕМЫ Взамен имеющегося в 1С ЖЗ, например в конфигурации Заработная плата и кад ры, создадим новый ЖЗ, дав ему имя Зарплата_2. Откроем конфигурацию, остановимся на пункте Журналы расчетов, присутст вующем на закладке Метаданные, и добавим в него новый подпункт (рис. 7. 17).
Рис. 7.17. Новый журнал расчетов Отвечая на вопросы конструктора ЖЗ, определим объект и период в соответствии с рис. 7.18.
а
б
Рис. 7.18. Задание свойств ЖЗ: а - объект ЖЗ; б - его период
В качестве дополнительных используем приведенные в табл. 7.5 реквизиты. Таблица 7.5 Дополнительные реквизиты ЖЗ Реквизит всегоЧасов хозОп
строкаДок
Описание
Примечание
Число часов, отработанных сотрудником в расчетном периоде Связанная с расчетом хозяйственная операция
Имеет числовой тип и формат 5.1
Номер строки табличной части документа, породившего расчет
Имеет разновидность типа Справочник.Хозяйственная Операция Имеет числовой тип и формат 5.0
Встроенных атрибутов ЖЗ и трех дополнительных реквизитов вполне достаточно для расчета зарплаты, ее бухгалтерского учета, выпуска сопровождающих расчет отче тов и даже для перечислений в банк. Кстати, последние возможны, если в справочнике Сотрудники_2 хранятся лицевые счета объектов расчета. Для упрощения положим, что номера лицевых счетов совпадают с кодами сотрудников, но предваряются пре фиксом Б-, например Б-301. Реквизит строкаДок полезен, если один документ порождает несколько расчетов. Такие документы у нас есть, например Табель. Тогда, зная значение строкаДок, мето дом документа ПолучитьСтрокуПоНомеру сразу же находится строка табличной части документа, отвечающая рассматриваемой записи ЖЗ. Правда, доверять значению это го реквизита можно, если между строками табличной части документа и расчетами существует устойчивая связь. Она может нарушиться, например, если после удаления строки табличной части документ сохраняется без перепроведения. Таким образом, для документа передающего в ЖЗ значение реквизита строкаДок, необходимо в моду ле документа вызвать метод документа, причем с единичным параметром, например, так: процедура ПриЗаписи() // ПриЗаписиПерепроводить(1); конецПроцедуры // ПриЗаписи
Предопределенная процедура модуля документа
Можно обойтись и без вызова метода ПриЗаписиПерепроводить, если с кнопкой ОК формы документа связать такую последовательность команд: #3аписать Провести Закрыть Окно с заданными свойствами и реквизитами ЖЗ приведем на рис. 7.19.
Рис. 7.19. Характеристики вновь построенного ЖЗ
Графами отбора могут быть реквизиты справочника, элементы которого исполь зуются в качестве объектов ЖЗ. Отмеченная нами графа отбора Родитель позволит отображать в ЖЗ не всех сотрудников одновременно, а лишь работающих в выбран ном подразделении. Закладки появляются и удаляются методом модуля формы журна ла ЗакдадкиОтбора. Если графой отбора является периодический реквизит справочни ка, то для него нужно выбрать радиокнопку Записывать на конец или начало расчетно го периода. Замечания: 1. Для нового ЖЗ система создаст файл, возможно CJ4287.DBF. 2. Впоследствии мы введем еще одну графу отбора - Образование (разд. 7.13, пример 9).
7.4.3. ФОРМА СПИСКА ЖУРНАЛА ЗАРПЛАТЫ В форме списка ЖЗ отобразим необходимую для визуального контроля расчетов информацию: ФИО сотрудника, ВР, а точнее, его представление, число отработанных часов (графа Часы), результат и даты начала и окончания расчета (рис. 7.20).
Рис. 7.20. Графы ЖЗ
Приведем предложенный 1С диалог формы к представленному на рис. 7.21 виду.
Рис. 7.21. Диалог формы списка ЖЗ Все столбцы, кроме столбцов Хоз. оп. и Результат, оставим доступными только для просмотра. (Свойства столбца, напомним, меняются после его выделения и выпол нения цепочки Действия - Свойства или нажатия на Alt + Enter.) Возможность смены хозяйственной операции в ЖЗ должна быть предусмотрена, так как в документах, по рождающих расчеты, изменить хозяйственную операцию нельзя: она берется как есть из созданного нами справочника ХозОпДляВР (разд. 7.3.7). Заметим, что изменить хо зяйственную операцию фиксированных расчетов не удастся и в ЖЗ. Первый столбец ЖЗ предназначен для показа сопровождающих расчеты иконок (пик тограмм). Столбец с именем ВР добавлен в таблицу после выбора иконки на панели ин струментов Элементы диалога. В поле Формула этого столбца размещено выражение "" + ВидРасч выводящее в ячейки столбца не идентификатор ВР, а его синоним. Размещенный в верхнем правом углу флажок, имеющий заголовок По цехам и иден тификатор закл, включат и отключает закладки отбора, то есть управляет режимом вывода данных по цехам. Радиокнопки Сотрудник и Цех, задают режимы расчетов Начало месяца, итоговой заработной платы, а также режимы вывода документов - печати расчетного листка и ведомости перечислений в банк. Радиокнопки, или переключатели, являются новым для нас элементом диалога. Для взаимосвязи радиокнопки размещаются в группе, в нашем случае имеющей назва ние Режим расчета. Группа формируется следующим образом: 1.
2.
3.
Добавляется радиокнопка (выбирается иконка на панели инструментов Элемен ты диалога) с заголовком Сотрудник, ей присваивается идентификатор кто (имя может быть произвольным), который и будет служить идентификатором группы. На закладке Дополнительно этой радиокнопке задается свойство Первый в группе. Добавляется радиокнопка с заголовком Цех, и ей присваивается идентификатор кто2, который нам пригодится для управления доступностью кнопки. Никаких дополнительных свойств для нее не задается. Кнопки обводятся рамкой группы (задается иконкой заголовок Режим расчета.
), которой присваивается
Замечание. Бывает, что 1С "капризничает" и не хочет объединять обведенные ра диокнопки в группы. Однако с этим можно справиться, если имя кто и свойство Пер вый в группе приписать крайнему справа переключателю. •
•
Переменная диалога кто принимает значения: 1, если выбрана радиокнопка Сотрудник; и в этом случае команды ЖЗ, например ко манда печати расчетного листка или ведомости перечислений в банк, выполняются для одного выбранного в ЖЗ сотрудника; 2, если выбрана радиокнопка Цех; команды ЖЗ выполняются для сотрудников ото бражаемого в ЖЗ подразделения.
Если в ЖЗ представлено все предприятие (закл = 0), то кто = 1 и радиокнопки не доступны, и тогда расчеты производятся для одного сотрудника. В верхней правой части диалога разместим кнопки с картинками - обычные кноп ки, в которых заголовок заменен на картинку, выбираемую на соответствующей зак ладке окна задания свойств кнопки (рис. 7.22).
Рис. 7.22. Задание картинки для кнопки С элементами диалога формы списка ЖЗ свяжем перечисленные в табл. 7.6 фор мулы. Они, напомним, пишутся на закладке Дополнительно в окне задания свойств элементов диалога. Таблица 7.6 Элементы диалога формы списка ЖЗ и их формулы Элемент диалога
Формула/команда
Описание
По цехам (имеет идентификатор закл)
ПоЦехам()
Включает/отключает режим вывода расчетов по цехам. Радиокнопки становятся недоступными, если закл = 0. При этом значение кто устанавливается равным единице
Режим расчета; радиокнопки Со трудник и Цех (имеет идентифи катор кто) Ввод расчета
ЗиачКтоСтар()
Запоминает значение переменной кто, для его восстановления при выборе режима отображения по цехам, то есть когда закл = 1
ВводРасчета()
Позволяет выбрать документ, вводящий новые расчеты, открыть форму документа и ввести нужные расчеты (описание ВР см. в табл. 7.2)
Замечание. Процедуры РасчетЗП, ПечатьРЛ и ВедомостьБанк, а также расчет на чального сальдо выполняются для одного сотрудника, если кто = 1 (активна радио кнопка Сотрудник), для сотрудников выбранного подразделения, либо, если кто = 2 (активна радиокнопка Цех). Теперь можно добавить в меню интерфейса Ученик команду вызова ЖЗ Зарплата_2 (рис. 7.23), связав с ним указанные на рис. 7.24 свойства и задав акселератор Alt+C.
Рис. 7.23. Новый пункт меню интерфейса Ученик
Рис. 7.24. Свойства пункта Журнал зарплаты_2 Теперь ЖЗ доступен для приема данных и просмотра. Для их обработки мы раз местим в модуле формы списка ЖЗ подходящие программы.
7.4.4. МОДУЛЬ ФОРМЫ СПИСКА ЖУРНАЛА ЗАРПЛАТЫ Содержит процедуры, управляющие элементами диалога, позволяющие выбирать ВР для ввода в ЖЗ, выполнить расчет зарплаты и сформировать расчетные листки и ведомости перечислений в банк. Чтобы новый сеанс работы начинался с того же мес та, где завершился прежний, в программе сохраняются в предопределенной процедуре ПриЗакрытии и восстанавливаются в предопределенной процедуре ПриОткрытии со ответствующие параметры формы списка ЖЗ.
7.4.4.1.
ПРОЦЕДУРЫ,
УПРАВЛЯЮЩИЕ
ЭЛЕМЕНТАМИ ДИАЛОГА
// ктоСтар - значение элемента кто до установления значения закл = О перем ктоСтар; перем нтп; // Начало текущего периода перем тДок; // Текущий документ // Объект с разновидностью типа Справочник.ХозОпДляВР перем хозОп; процедура ПоЦехам() далее // Процедура нужна, чтобы правильно реагировать на переключения флажка закл процедура ЗначКтоСтар() ктоСтар = кто; конецПроцедуры // ЗначКтоСтар процедура ПриЗакрытии() // Предопределенная процедура перем значОтбора; // Текущее значение отбора при закл = 1 перем реж, значРеж; // Представление: списком или по одному объекту // Сохраняем на диске значения переменных диалога закл, кто и иных установок // для следующего сеанса работы СохранитьЗначение("ЗакладкиВЗарплате", закл); СохранитьЗначение("КтоВЗарплате", кто); СохранитьЗначение("ЗначениеОтбора", Объект.Родитель); // Представление: список или один сотрудник ПолучитьПредставление(реж, значРеж); СохранитьЗначение("РежимПредставления", реж); СохранитьЗначение("ЗначениеПредставления", значРеж); конецПроцедуры // ПриЗакрытии процедура ПриОткрытии() // Предопределенная процедура перем реж, значРеж; // Восстанавливаем с диска значения переменных диалога закл и кто // для текущего сеанса работы закл = ВосстановитьЗначение("ЗакладкиВЗарплате"); если ТипЗначения(закл) = 0 тогда // Если значение переменной закл не восстановлено закл = 1; кто= 1; иначе кто = ВосстановитьЗначение("КтоВЗарплате"); конецЕсли; ктоСтар = кто; ПоЦехам(); если закл = 1 тогда значОтбора = ВосстановитьЗначение("ЗначениеОтбора"); ЗакладкиОтбора("Родитель", значОтбора); конецЕсли; реж = ВосстановитьЗначение("РежимПредставления"); значРеж = ВосстановитьЗначение("ЗначениеПредставления"); если реж = 1 тогда УстановитьПредставление(1);
иначе УстановитьПредставление(реж, значРеж); конецЕсли; нтп = НачалоТекущегоПериода(); конецПроцедуры // ПриОткрытии // Включает/отключает режим вывода по цехам. Если закл = 0, то устанавливает // кто = 1 и делает недоступными радиокнопки группы Режим расчета процедура ПоЦехам() если закл = 1 тогда // Если используются закладки отбора ЗакладкиОтбора("Родитель"); кто = ктоСтар; Форма.Кто. Доступность(1); Форма.Кто2.Доступность(1); иначе ЗакладкиОтбора(""); кто = 1; Форма.Кто.Доступность(0); Форма.Кто2.Доступность(0); конецЕсли; конецПроцедуры // ПоЦехам() 7.4.4.2.
ВЫБОР ВИДА РАСЧЕТА
ДЛЯ ВВОДА В ЖУРНАЛ ЗАРПЛАТЫ
Осуществляется из перечисления ВР_2. Ввод начального сальдо выполняется процедурами модуля ЖЗ, а иных ВР - соответствующими документами. процедура НачСальдо() далее процедура ВводРасчета() перем значПер, флаг, докВид; значПер = "ВР_2"; // Вид ВР выбирается из диалога, представленного на рис. 7.25 флаг = ВвестиПеречисление(значПер, "Выберите виды расчетов для ввода"); // Если нажали OK, Enter или дважды ударили мышью по выбранному значению если флаг = 1 тогда докВид = значПер.Идентификатор(); если докВид = "НачСальдо" тогда НачСальдо(); // Ввод начального сальдо жз = 0; иначе // Табель или Премия // Расчеты вводятся документом ОткрытьФорму(" Документ." + докВид); конецЕсли; иначе Предупреждение("Ничего не выбрано."); возврат; конецЕсли; конецПроцедуры // ВводРасчета
Рис. 7.25. Диалог для выбора вида документа 7.4.4.3.
ВВОД НАЧАЛЬНОГО
САЛЬДО
Выполняется, если кто = 1, для выбранного в справочнике Сотрудники_2 сотруд ника либо для выбранного подразделения. Если сотрудник уже имеет в текущем рас четном периоде расчет в ВР НачСальдо_2, то при повторном вводе меняется лишь результат расчета. Выбранное подразделение возвращается методом модуля формы ЖР ПолучитьОтбор. процедура одинСотр(сотр) далее функция ЕстьВЖЗ(сотр) далее // Заполняет ЖЗ расчетами с ВР НачСадьдо_2 процедура НачСальдо() перем док, грОт, подр; // подр - значение отбора перем сСотр_2, флаг; нтп = НачалоТекущегоПериода(); док = СоздатьОбъект("Документ.НачПериода_2"); если док.НайтиПоНомеру(1) = 0 тогда Предупреждение("Нельзя ввести начальное сальдо. Нет документа НачПериода_2 №1"); возврат; конецЕсли; тДок = док.ТекущийДокумент(); // Определяемся относительно хозяйственной операции для ВР НачСальдо_2 хозОп = СоздатьОбъект("Справочник.ХозОпДляВР"); // Ищем простым перебором в справочнике ХозОпДляВР вид расчета НачСальдо_2 хозОп.ВыбратьЭлементы(); флаг = 0; пока хозОп.ПолучитьЭлемент() = 1 цикл если хозОп.ВР = ВидРасчета.НачСальдо_2 тогда флаг= 1; прервать; конецЕсли; конецЦикла; // пока если флаг = 0 тогда Предупреждение("Хозяйственная операция для ВР Начальное сальдо не найдена"); возврат; конецЕсли; // Начало месяца для одного сотрудника. Разрешим множественные выбор если кто = 1 тогда ОткрытьПодбор("Справочник.Сотрудники_2", "ФормаСписка",, 1); иначеЕсли закл = 1 тогда // Используем закладки отбора // При первом использовании ЖЗ закладки отбора в нем не отображаются, // даже если закл = 1; в этом случае начальное сальдо вводится для всех сотрудников
ПолучитьОтбор(грОт, подр); // подр - значение отбора; имеет тип Справочник сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ИспользоватьРодителя(подр); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 0 тогда // Ввод начального сальдо для одного сотрудника одинСотр(сСотр_2.ТекущийЭлемент()); конецЕсли; конецЦикла; // пока УстановитьПредставление(1); // Отображаем записи по всем объектам // Если в ЖЗ нет записей, тогда подр имеет пустое значение; вводятся все // сотрудники и для отображения закладок отбора вызываем процедуру ПоЦехам если ПустоеЗначение(подр) = 1 тогда ПоЦехам(); конецЕсли; Форма.Обновить(); конецЕсли; конецПроцедуры // НачСальдо процедура ОбработкаПодбора(сотр, конт) одинСотр(сотр); // если закл = 0 тогда закл = 1; // ПоЦехам(); конецЕсли; УстановитьПредставление(2, сотр); конецПроцедуры // ОбработкаПодбора
Начальное сальдо для одного сотрудника Показываем закладки отбора
//
Отображаем записи по одному объекту
процедура одинСотр(сотр) // Если расчет с ВР НачСальдо_2 объект уже имеет, то обновляем только результат если ЕстьВЖЗ(сотр) = 0 тогда // Добавляем расчет с ВР НачСальдо_2 для выбранного сотрудника Новая(); УстановитьРеквизит("Документ", тДок); УстановитьРеквизит("РодительскийДокумент", тДок); УстановитьРеквизит("Объект", сотр); УстановитьРеквизит("ВидРасч", ВидРасчета.НачСальдо_2); УстановитьРеквизит("хрзОп", хозОп.ХозОП); УстановитьРеквизит("ДатаНачала", нтп); УстановитьРеквизит("ДатаОкончания", нтп); иначе // Делаем расчет нефиксированным // Журнал позиционирован функцией ЕстьВЖЗ на нужной записи // Подготовка к обновлению результата ОсвободитьЗапись(); // Делаем запись нефиксированной конецЕсли; Записать( ); // Не забываем записать новые значения реквизитов // Обращаемся к предопределенной процедуре ПровестиРасчет ВР НачСальдо_2 // Метод ВыполнитьРасчет должен быть расположен после вызова метода Записать ВыполнитьРасчет( ); // или Рассчитать() ФиксироватьЗапись(); конецПроцедуры // одинСотр
// Вернет 1, если начальное сальдо для выбранного объекта уже введено, // или О-в противном случае функция ЕстьВЖЗ(сотр) если ВыбратьПериодПоОбъекту(сотр) = 1 тогда пока ПолучитьЗапись( ) = 1 цикл если ВидРасч = ВидРасчета.НачСальдо_2 тогда возврат 1; конецЕсли; конецЦикла // пока конецЕсли; возврат 0; конецФункции // ЕстьВЖЗ Замечания: 1. Процедуры РасчетЗП, ПечатьРЛ и ВедомостьБанк будут рассмотрены после за вершения разработки программ ввода в ЖЗ всех ВР. 2.
В ЖЗ сотрудники сортируются так же, как и в справочнике-объекте.
5.
Расчеты не должны вводиться для уволенных или еще не приступивших к работе сотрудников, записи о которых, однако, есть в справочнике Сотрудники_2. Проб лема решается просто, если есть соответствующие приказы с датами приема и увольнения сотрудника.
4.
Записи ЖЗ, в которых неверно определены атрибуты, например в атрибут Доку мент занесено пустое значение типа Документ, сопровождаются в ЖЗ иконкой Такие записи из ЖЗ нужно изъять. Интерактивно это можно выполнить, применив цепочку Действия - Очистить журнал расчетов.
5.
Встроенная процедура СохранитьЗначение записывает на диск (в файл 1CV7.CFG) за данное вторым параметром значение. Встроенная функция ВосстановитьЗначение вы полняет обратное действие. Если восстанавливается значение реквизита формы типа СписокЗначений, то функция вызывается как процедура, возвращая результат в свой второй параметр, например: ВоостановитьЗначение("СписокПодразделений", сПодр);
7.4.5. УДАЛЕНИЕ ЗАПИСЕЙ ЖУРНАЛА ЗАРПЛАТЫ И МЯГКАЯ СМЕНА РАСЧЕТНОГО ПЕРИОДА На период отладки вам может понадобиться процедура удаления записей ЖЗ те кущего периода. Ее код прост: процедура ОчиститьЖЗ() перем жз; // Журнал заработной платы жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьПериод(жз.НачалоТекущегоПериода()); пока жз.ПолучитьЗапись() = 1 цикл жз.УдалитьЗапись(); конецЦикла // пока конецПроцедуры // ОчиститьЖЗ
Однако у этой процедуры есть особенность: записи удаляются через одну. Это объясняется тем, что после проставления DBF-пометки удаления запись из ЖЗ исчеза ет и текущей становится следующая запись. Далее вступает в действие метод ПолучитьЗапись, перемещающий позицию ЖЗ еще на одну запись. Поэтому процедуру ОчиститьЖЗ нужно запускать неоднократно, что достигается следующим кодом: функция ОчиститьЖЗ(жз, нтп) перем флаг; // флаг = жз.ВыбратьПериод(нтп); если флаг = 1 тогда пока жз.ПолучитьЗапись() = 1 цикл жз.УдалитьЗапись(); конецЦикла // пока конецЕсли; возврат флаг; // конецФункции // ОчиститьЖЗ
Журнал заработной платы
флаг = О, если в ЖЗ нет записей
процедура Выполнить() перем жз; // Журнал заработной платы перем нтп; // Начало текущего периода перем флаг; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); флаг=1; пока флаг = 1 цикл флаг = ОчиститьЖЗ(жз, нтп); конецЦикла // пока // Вызов формы ЖЗ для просмотра результата ОткрытьФорму('ЖурналРасчетов.Зарплата_2"); конецПроцедуры // Выполнить После удаления записей текущего периода, возможно, потребуется вернуться к прежнему расчетному периоду и сделать его текущим. Если смена периода вперед была выполнена интерактивно, то записи сменяемого периода получают статус архи вных. При интерактивном откате назад произойдет обнуление р езультатов записей, пе риод регистрации которых принадлежит к устанавливаемому расчетному периоду. Чтобы этого не произошло, следует воспользоваться следующей программой: // Осуществляет мягкую, то есть без отработки системных действий, // смену расчетного периода ЖЗ процедура Выполнить() перем жз; // Журнал заработной платы перем пер; // Переменная типа РасчетныйПериод если Вопрос("Вернуться к предыдущему расчетному периоду?", "Да+Нет") = "Нет" тогда возврат; конецЕсли; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Перемещаемся на один период назад пер = жз.ТекущийПериод( ).ПрибавитьПериод(-1); // Устанавливаем период пер в качестве текущего, не отрабатывая системные действия если жз.УстановитьТекущийПериод(пер, 0) — 1 тогда
Предупреждение("Готово."); // Вызов формы ЖЗ для просмотра результата // После открытия формы, возможно, придется сменить границу просмотра ЖЗ ОткрытьФорму("ЖурналРасчетов.Зарплата_2"); иначе Предупреждение("Сменить период не удалось."); конецЕсли; конецПроцедуры // Выполнить
7.4.6. ЗАГРУЗКА НАЧАЛЬНОГО САЛЬДО ИЗ DBF-ФАЙЛА При смене программных средств неизбежно возникают задачи переноса остатков из старых файлов в новые. Нередко переносимые данные можно записать в DBFфайлы. Предположим, что так оно и есть и начальное сальдо нужно перенести из фай ла bal.dbf, фрагмент которого представлен в табл. 7.7, в созданный нами и пока что пустой ЖЗ. Таблица 7.7 Фрагмент файла bal.dbf с начальным сальдо Employee Абрамова Лариса Сергеевна Агальцов Юрий Алексеевич Бараненков Иван Ильич
Balance 2.60 1.70 0.30
Алгоритм переноса нетруден: |. Начало. 2. Открыть файл bal.dbf и переместиться на его первую запись. , 3. Пока не обнаружен конец файла bal.dbf, выполнить: 3.1. Осуществить поиск по наименованию элемента справочника Сотрудники_2, применяя в качестве наименования значение поля Employee файла bal.dbf. 3.2. Если элемент найден, то Добавить в ЖЗ запись, объектом которой является найденный элемент, используя в качестве результата значение поля Balance файла bal.dbf. конец если 3.2. Перейти к следующей записи файла bal.dbf. конец цикла 3. 4. Добавить запись в документ НачПериода_2 о первом расчетном периоде ЖЗ. р. Конец. В 1С подобные алгоритмы программируются с помощью объектов типа XBase, позво ляющих управлять DBF-файлами. Разместим по обычаю код, реализующий алгоритм пе реноса в модуле обработки Проба. Рисунки, отображающие результаты работы программы переноса данных, приведены сразу после ее текста. Процедура одинСотр(жз, сотр, тДок, хозОп, нтп, начС) далее функция ЕстьВЖЗ(жз, сотр) далее Процедура ФиксироватьСменуРП(жз, док, нтп) далее
процедура Выполнить( ) перем дбф; перем файл, папка, фио, сотр; перем сСотр_2, жз, док, тДок, хозОп, флаг, флагЖЗ, нтп; ОчиститьОкноСообщений(); // Открываем диалог для выбора файла bal.dbf флаг = ФС.ВыбратьФайл(0, файл, папка, "Выберите файл"," | *.DBF"); если флаг = 0 тогда возврат; конецЕсли; дбф = СоздатьОбъект("ХВаве"); дбф.ОткрытьФайл(папка + файл); если дбф.Открыта() = 0 тогда Предупреждение("Не удалось открыть файл " + файл); возврат; конецЕсли; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); хозОп = СоздатьОбъект("Справочник.ХозОпДляВР"); // Ищем простым перебором в справочнике ХозОпДляВР вид расчета НачСальдо_2 хозОп.ВыбратьЭлементы(); флаг = 0; пока хозОп.ПолучитьЭлемент() = 1 цикл если хозОп.ВР = ВидРасчета.НачСальдо_2 тогда флаг = 1; прервать; конецЕсли; конецЦикла; // пока если флаг = 0 тогда Предупреждение("Хозяйственная операция для ВР Начальное сальдо не найдена."); возврат; конецЕсли; док = СоздатьОбъект("Документ.НачПериода_2"); // Номер документа не меняется и всегда равен единице док.НайтиПоНомеру(1, Дата(0)); если док.Выбран() = 0 тогда Предупреждение("Создаю документ НачПериода_2 под номером 1.", 2); док.Новый(); док.НомерДок = 1; док.ДатаДок = ТекущаяДата(); док.Записать(); конецЕсли; тДок = док.ТекущийДокумент(); нтп = жз.НачалоТекущегоПериода(); флагЖЗ = 0; дбф.КодоваяСтраница(0); // Используем Windows-кодировку дбф.Первая(); // Перемещаемся на первую запись DBF-файла // Перебираем все, кроме удаленных, записи ранее открытого DBF-файла дбф.Показывать Удаленные(0);
пока дбф.ВКонце( ) = 0 цикл // Пока не достигнут конец DBF-файла фио = дбф.Employee; // ФИО сотрудника // Задаем поиск во всем справочнике Сотрудники_2 флаг = сСотр_2.НайтиПоНаименованию(фио, 0); если флаг = 1 тогда флагЖЗ = 1; // В ЖЗ добавлена по крайней мере одна запись сотр = сСотр_2.ТекущийЭлемент(); // Запись для одного сотрудника одинСотр(жз, сотр, тДок, хозОп, нтп, дбф.Balance); конецЕсли; дбф.Следующая(); // Переход на следующую DBF-запись конецЦикла; // Результат см. на рис. 7.26 если флагЖЗ = 1 тогда // Если в ЖЗ добавлены записи // Добавляем запись в документ НачПериода_2 о первом расчетном периоде ЖЗ // Результат см. на рис. 7.27 ФиксироватьСменуРП(жз, док, нтп); // Вызовы форм ЖЗ и документа НачПериода_2 под номером 1 // для просмотра результата ОткрытьФорму("ЖурналРасчетов.Зарплата_2"); ОткрытьФорму(тДок); иначе Предупреждение("В ЖЗ не перенесено ни одной записи."); конецЕсли; конецПроцедуры // Выполнить процедура одинСотр(жз, сотр, тДок, хозОп, нтп, начС) // Если расчет с ВР НачСальдо_2 объект уже имеет, то обновляем только результат если ЕстьВЖЗ(жз, сотр) = 0 тогда // Добавляем расчет с ВР НачСальдо_2 для выбранного сотрудника жз.Новая(); жз.УстановитьРеквизит("Документ", тДок); жз.УстановитьРеквизит("РодительскийДокумент", тДок); жз.УстановитьРеквизит("Объект", сотр); жз.УстановитьРеквизит("ВидРасч", ВидРасчета.НачСальдо_2); жз.УстановитьРеквизит("хозОп", хозОп.ХозОП); жз.УстановитьРеквизит("ДатаНачала", нтп); жз.УстановитьРеквизит("ДатаОкончания", нтп); жз.УстановитьРеквизит("Рассчитана", 1); иначе // Делаем расчет нефиксированным // Журнал позиционирован функцией ЕстьВЖЗ на нужной записи // Подготовка к обновлению результата жз.ОсвободитьЗапись(); // Делаем запись нефиксированной конецЕсли; // Заносим в атрибут Результат величину начального сальдо сотрудника жз.УстановитьРеквизит("Результат", начС); // Не забываем записать новые значения реквизитов жз.3аписать(); жз.ФиксироватьЗапись(); конецПроцедуры // одинСотр
// Вернет 1, если начальное сальдо для выбранного объекта уже введено, // или О-в противном случае функция ЕстьВЖЗ(жз, сотр) если жз.ВыбратьПериодПоОбъекту(сотр) = 1 тогда пока жз.ПолучитьЗапись() = 1 цикл если жз.ВидРасч = ВидРасчета.НачСальдо_2 тогда возврат 1; конецЕсли; конецЦикла // пока конецЕсли; возврат 0; конецФункции // ЕстьВЖЗ // Заносит данные о смене расчетного периода в документ НачПериода_2 процедура ФиксироватьСменуРЩжз, док, нтп) // период - переменная типа РасчетныйПериод док.НоваяСтрока(); // Добавляем в документ новую строку док.НовПериод = жз.ОписательПериода(нтп); // Дата смены расчетного периода ЖЗ Зарплата_2 док.ДатаУстановки = ТекущаяДата(); док.Пользователь = ИмяПользователя(); док.Записать(); конецПроцедуры // ФиксироватьСменуРП
Рис. 7.26. Расчеты НачСальдо_2 введены из файла bal.dbf
Рис. 7.27. Первый расчетный период в документе НачПериода_2 № 1 Замечания: 1.
Первый расчетный период задается при создании ЖЗ в поле Дата отсчета (см. рис. 7.19).
2.
Не исключено, что для изменения кодировки записей DBF-файла, вам придется при бегнуть к встроенным функциям OemToAnsi и AnsiToOem, приведенным в табл. 2.8.
7.4.7. ОТЧЕТ ПО НАЧАЛЬНОМУ САЛЬДО Отчеты, как мы уже знаем, сподручнее всего строить на базе запросов. Напишем запрос, осуществляющий выбор из ЖЗ расчетов с заданным типом ВР, и разместим его в модуле обработки Проба. Запрос и сопровождающий его код крайне просты:
// Формирует и выполняет запрос запр функция ВыпЗапр(запр, ВРДляЗапр) далее процедура ВыводЗапрТЗнач(запр) далее процедура ВыводЗапрСооб(запр) далее
// //
Выводит запрос в таблицу значений Выводит запрос в окно сообщений
процедура Выполнить() перем запр, ВРДляЗапр; ВРДляЗапр = ВидРасчета.НачСальдо_2; запр = СоздатьОбъект("Запрос"); // Передаем функции ВыпЗапр значение ВР для отбора расчетов // Функция ВыпЗапр формирует и выполняет запрос запр // Если запрос выполнен если ВыпЗапр(запр, ВРДляЗапр) = 1 тогда ВыводЗапрТЗнач(запр); // Выводит запрос в таблицу значений ВыводЗапрСооб(запр); // Выводит запрос в окно сообщений конецЕсли; конецПроцедуры // Выполнить функция ВыпЗапр(запр, ВРДляЗапр) // Формирует и выполняет запрос запр перем жз, нтп, текстЗапр; // текстЗапр - текст запроса жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); текстЗапр =" | период с нтп по нтп; // Подходящий для Начального сальдо период // Определяем переменные запроса; они содержат нужные нам данные | сотр = ЖурналРасчетов.Зарплата_2.0бъект; | ВР = ЖурналРасчетов.Зарплата_2.ВидРасч; | рез = ЖурналРасчетов.Зарплата_2.Результат; // Задаем порядок выборки данных | группировка сотр упорядочить по сотр.Наименование; // Условие отбора данных | условие (ВР = ВРДляЗапр);"; // Выполняем запрос возврат запр.Выполнить(текстЗапр); конецФункции // ВыпЗапр процедура ВыводЗапрТЗнач(запр) // Выводит запрос в таблицу значений перем тЗнач; // Объект тЗнач создаем для промежуточной демонстрации выборки запроса тЗнач = СоздатьОбъект("ТаблицаЗначений"); // Выгружаем для предварительного просмотра все переменные запроса // в таблицу значений тЗнач запр.Выгрузить(тЗнач, 1); // Просмотр таблицы значений (результатов запроса). Результат см. на рис. 7.28 тЗнач.ВыбратьСтроку(, "Начальное сальдо в таблице значений"); энецПроцедуры // ВыводЗапрТЗнач выводит запрос в окно сообщений Результат имеет ту же структуру, что и данные в таблице, приведенной на рис. 7.28 эоцедура ВыводЗапрСооб(запр) // В этой процедуре еще раз иллюстрируется метод запросов Группировка пока запр.Группировка("сотр") = 1 цикл если запр.ЭтоГруппа("сотр") = 1 тогда Сообщить("Сотрудники подразделения " + запр.сотр);
иначе Сообщить(СокрЩзапр.сотр) + СимволТабуляции + запр.ВР + СимволТабуляции + запр.рез); конецЕсли; конецЦикла; // пока конецПроцедуры // ВыводЗапрСооб
Рис. 7.28. Результат запроса о начальном сальдо с группами Группы (имена родителей) не попадут в запрос, если оператор Группировка до полнить опцией Без групп: [группировка сотр упорядочить по сотр.Наименование без групп; что приведет к следующему результату:
Рис. 7.29. Результат запроса о начальном сальдо с группами Из рис. 7.28 и 7.29 видно, что элементы-родители справочника Сотрудники_2 яв ляются в запросе группами и метод запр.ЭтоГруппа("сотр") вернет для них единицу. Для иных записей этот метод вернет нуль. Первая строка таблицы значений отображает начало выборки. Строка содержит пустое значение переменной запроса сотр. Первое же употребление метода запр.Группировка("сотр"); приведет к перемещению позиции выборки на ее первую, содержащую непустое зна чение переменной сотр запись. Если в текст запроса добавить переменную род, содержащую родителя объекта: | род = ЖурналРасчетов.Зарплата_2.0бъект.Родитель; то результат запроса не изменится. Если же вдобавок в тексте запроса разместить и группировку по род, то есть пере дать методу Выполнить такой текст: текстЗапр =" | период с нтп по нтп; | род = ЖурналРасчетов.Зарплата_2.Объект.Родитель; | сотр = ЖурналРасчетов.Зарплата_2.Объект;
I BP = ЖурналРасчетов.Зарплата_2.ВидРасч; | рез = ЖурналРасчетов.Зарплата_2.Результат; | группировка род; | группировка сотр упорядочить по сотр.Наименование; | условие (ВР = ВРДляЗапр);"; то в таблице значений мы увидим дополнительные строки, в которых переменная за проса сотр имеет пустое значение (рис. 7.30).
Рис. 7.30. Запрос с двумя группами Эти строки, кроме первой, начинают группы род. Первая строка - это начало вы борки. В ней и род и сотр имеют пустые значения. При двойной группировке, чтобы получить доступ к данным запроса более низко го уровня, необходимо прежде выбрать вышестоящие группы. Поэтому придется из менить код процедуры ВыводЗапрСооб на следующий: // Выводит запрос в окно сообщений для случая двойной группировки процедура ВыводЗапрСооб(запр) // В этой процедуре еще раз иллюстрируется метод запросов Группировка пока запр.Группировка("род") = 1 цикл Сообщить("Сотрудники подразделения " + запр.род); пока запр.Группировка("сотр") = 1 цикл если запр.ЭтоГруппа("сотр") = 0 тогда Сообщить(СокрП(запр.сотр) + Символ Табуляции + запр.ВР + СимволТабуляции + запр.рез); конецЕсли; конецЦикла; // пока запр.Группировка("сотр") = 1 конецЦикла; // пока запр.Группировка("род") =1 конецПроцедуры // ВыводЗапрСооб Результат соответствует рис. 7.28: Сотрудники подразделения 01 Цех Сотрудники подразделения 01/1 Агальцов Юрий Алексеевич Добрецов Борис Юрьевич Сотрудники подразделения 01/2 Ьараненков Иван Ильич
Начальное сальдо Начальное сальдо
1.7 0.1
Начальное сальдо
0.3
Сравнение двух запросов показывает, что при запросах к ЖЗ для вывода сотруд ников с разбивкой по цехам нет необходимости вводить в текст запроса переменную род и устраивать вторую группу. Все необходимые данные даст группировка по пере менной запроса сотр.
7.5. КАЛЕНДАРИ И ПРАЗДНИКИ Наша задача - употребить календари 1С для вычисления общего числа рабочих дней и часов. Учет рабочего времени осуществляется по табелю, который мы оформим как самостоятельный документ 1С.
7.5.1. СОЗДАНИЕ КАЛЕНДАРЯ Пусть на нашем виртуальном предприятии имеются служащие и рабочие. Созда дим для этих категорий свои календари. Войдем в конфигурацию, откроем подменю Календарь и разместим в нем календари Служащие_2 и Рабочие_2 (рис. 7.31).
Рис. 7.31. Новые календари системы Свойства календарей определим в соответствии с рис. 7.32.
а
б
Рис. 7.32. Рабочая неделя: а - календаря Служащие_2; б - календаря Рабочие_2 Добавим в меню интерфейса Ученик колонку Календари и табель и определим в ней указанные на рис. 7.33 пункты.
Рис. 7.33. Новая колонка меню интерфейса Ученик С новыми пунктами Календари и Праздники свяжем приведенные на рис. 7.34 наз вание, объект и команды.
а б Рис. 7.34. Свойства пунктов меню: а - Календари; б - Праздники Объект и команду для пункта Табель зададим несколько позже - после создания документа Табель. Сохраним изменения конфигурации. Загрузим 1С:Предприятие. Откроем прежде таблицу для ввода праздников и внесем в нее праздники текущего года, проставив в столбце Значение нулевую продолжительность рабочего дня (рис. 7.35).
Рис. 7.35. Два праздника Откроем далее последовательно календари Служащие_2 и Рабочие_2 и выполним их автоматическое заполнение с включенным флажком Учитывать праздники (рис. 7.36).
Рис. 7.36. Календарь Служащие_2 в ноябре 2001 г. В поле Значение по текущей дате (рис. 7.36) заполненного календаря отображается число часов, взятое из определенного в конфигурации и изображенного на рис. 7.32, а календаря Сотрудники_2. Для праздничных дней календарь использует данные приве денной на рис. 7.35 таблицы. Замечание. И календари и праздники хранятся в файле CL.DBF информационной базы.
7.5.2. ПРИМЕНЕНИЕ КАЛЕНДАРЕЙ И ПРАЗНИКОВ 7.5.2.1. ДОСТУП К КАЛЕНДАРЯМ И ПРАЗДНИКАМ Объекты типа Календарь присутствуют в конфигурации и доступны непосредст венно по именам (идентификаторам), которые можно использовать с методами для ка лендарей и чтения их атрибутов.
Пример. Методом Часов вычисляется число рабочих часов в ноябре 2001 г. по календарю Служащие_2. всегоЧасов = Календари.Служащие_2.Часов('01.11.2001', КонМесяца('01.11.2001')); Сообщить(всегоЧасов); //Напечатает 167 Тот же результат метод предоставит, будучи употребленным с объектом типа Ка лендарь, возвращаемым функцией СоздатьОбъект: кален = СоздатьОбъект("Календарь.Служащие_2"); всегоЧасов = кален.Часов('01.11.2001', КонМесяца('01.11.2001')); Сообщить(всегоЧасов); // Напечатает 167 Еще один вариант применения методов календарей, например метода Часов, - это использование в составе префикса метода ПолучитьАтрибут: всегоЧасов = Календари. ПолучитьАтрибут("Служащие_2").Часов('01.11.2001', КонМесяца('01.11.2001')); Сообщить(всегоЧасов); //Напечатает 167 Переменные типа Праздники относятся к внешним объектам и создаются, как и иные внешние объекты, например таблицы значений, функцией СоздатьОбъект. Имя переменной используется в качестве префикса методов и атрибутов объекта типа Праздники. Пример. Печатаются праздники текущего года. процедура Выполнить() перем празд, нг, кг, флаг; ОчиститьОкноСообщений(); нг = НачГода(ТекущаяДата()); // Начало и конец текущего года кг = КонГода(ТекущаяДата()); праздн = СоздатьОбъект("Праздники"); // Перемещаемся на первую запись (дату) выборки флаг = праздн.ВыбратьДаты(нг, кг); если флаг = 1 тогда пока праздн.СледующаяДата() = 1 цикл Сообщить("" + праздн.Дата + " - это праздник"); // или Сообщить("" + праздн.ПолучитьАтрибут("Дата") + " - это праздник"); конецЦикла; // пока иначе Сообщить("В текущем году нет праздников."); конецЕсли; конецПроцедуры // Выполнить Результат (соответствует рис. 7.35): 07.11.01 - это праздник 12.12.01 - это праздник
7.5.2.2. А ТРИБУТЫ КАЛЕНДАРЕЙ И ПРАЗДНИКОВ Календари имеют следующие атрибуты: - <имя календаря>; имеет тип Календарь и возвращается методом ПолучитьАтрибут; - Дата; имеет тип Дата, читается непосредственно по имени или методом ПолучитьАт рибут; - Значение; имеет числовой тип. Пример. Печатаются нерабочие даты текущего месяца (с нулевой продолжитель ностью рабочего дня) календаря Служащие_2. процедура Выполнить() перем кален, нм, км, нерДни; ОчиститьОкноСообщений(); нм = НачМесяца(ТекущаяДата()); // Начало и конец текущего месяца км = КонМесяца(ТекущаяДата()); попытка // Создаем объект типа Календарь. Для этого у нас имеется 3 возможности кален = Календари.ПолучитьАтрибут("Служащие_2"); // или кален = Календари.Служащие_2; // или кален = СоздатьОбъект("Календарь.Служащие_2"); исключение Предупреждение("Нет такого календаря."); возврат; конецПопытки; нерДни = ""; // Перемещаемся на первую запись (дату) выборки кален.ВыбратьДаты(нм, км); пока кален.СледующаяДата() = 1 цикл если кален.Значение = 0 тогда // Если нерабочий день нерДни = нерДни + Строка(кален.Дата) + "; "; // или нерДни = нерДни + Строка(кален.ПолучитьАтрибут("Дата")) + ";"; конецЕсли; конецЦикла; // пока если СтрДлина(нерДни) = 0 тогда Сообщить("В текущем месяце все дни рабочие"); иначе // Заменяем в строке нерДни последнюю точку с запятой на точку нерДни = Лев(нерДни, СтрДлина(нерДни) - 2) + "."; Сообщить("Нерабочие дни текущего месяца: " + нерДни); конецЕсли; конецПроцедуры // Выполнить Результат: Нерабочие дни текущего месяца: 02.12.01; 09.12.01; 12.12.01; 16.12.01; 23.12.01; 30.12.01. Праздники имеют два атрибута: • Дата; имеет тип Дата; • Значение; имеет числовой тип.
Замечание. Атрибуты календарей и праздников Дата и Значение читаются непо средственно по имени или методом ПолучитьАтрибут; изменяются методом УстановитьЗначение. 7.5.2.3. МЕТОДЫ КАЛЕНДАРЕЙ И ПРАЗДНИКОВ Сведены в табл. 7.8, в которой первоначально следуют общие методы рассматри ваемых объектов, а далее их специфические методы. Таблица 7.8 Методы календарей и праздников Метод
Описание Общие методы календарей и праздников
знач = кален|праздн.Получить Атрибут("Дата"|"Значение");
Возвращает значение атрибута Дата или Значение в текущей записи объекта. Возможно прямое обра щение к атрибуту, например дт = кален.Дата; часы = праздн.Значение;
кален|праздн.УстановитьАтрибут ("Дата"|"3начение", знач);
Изменяет значение атрибута Дата или Значение в текущей записи объекта на величину знач
флаг = кален|праздн.ВыбратьДаты (датаНачала, датаКонца);
Открывает выборку записей объекта, расположен ных между датами датаНачала и датаКонца, пере мещая его позицию на первую запись выборки. Вернет 1, если в выборке есть хотя бы один эле мент, или 0, если выборка пуста
флаг = кален|праздн.Следующая Дата();
Выбирает текущую запись и перемещает позицию выборки на следующую запись. Вернет 1, если за пись выбрана, или 0, если вся выборка исчерпана Методы календарей
кален = Календари.ПолучитьАтрибут (идент);
Возвращает объект типа Календари с разновидно стью типа идент, где идент - строка, содержащая идентификатор календаря, например "Служащие_2". Возникнет завершающая ошибка, если идент содержит несуществующий идентификатор
идент = кален.Вид();
Вернет строку, содержащую идентификатор кален даря кален. Если, например, в справочнике сСотр_2 есть реквизит Календарь типа Календари, то иден тификатор календаря выбранного сотрудника вер нет вызов идент = сСотр_2.Календарь.Вид();
флаг = кален.Выбран();
Вернет 1, если календарь выбран, или О-в против ном случае. Для справочника сСотр_2, в котором есть реквизит Календарь типа Календари, для теку щей записи признак выбран календарь или нет вер нет вызов флаг = сСотр_2.Календарь.Выбран();
Описание
Метод числоРабДней = кален. Дней (датаНачала, датаКонца);
Вернет число дней с ненулевой величиной атрибута Значение в календаре кален, расположенных между датами датаНачала и датаКонца. Выдаст сообще ние "Неверные даты", если датаКонца < датаНа чала
числоРабЧасов = кален.Часов {датаНачала, датаКонца);
Вернет сумму значений атрибута Значение календа ря кален для записей, расположенных между датами датаНачала и датаКонца. Выдаст сообщение "Не верные даты", если датаКонца < датаНачала
флаг = кален. Автозаполнение (датаНачала, датаКонца);
Заполняет календарь кален в заданном параметрами датаНачала и датаКонца временном интервале, используя определенные в конфигурации для этого календаря данные о продолжительности рабочей недели (разд. 7.5.1). При заполнении учитываются занесенные в базу данных праздники (разд. 7.5.1, рис. 7.35), если перед вызовом метода употреблен метод УчитыватьПраздники(1). Вернет 1, если за полнение произведено, или 0 - при неудаче
праздСтар = кален.УчитыватьПраздники (праздНов);
Задает, если числовой параметр праздНов = 1, ре жим учета занесенные в базу данных праздников (разд. 7.5.1, рис. 7.35) при выполнении метода Авто заполнение для календаря кален. Если праздНов = 0, то праздники при автозаполнении календаря кален не учитываются. Если метод для календаря кален не вызывался, то при его автозаполнении праздники учитываются. Возвращает текущую установку ре жима учета праздников
дат = кален.ПолучитьДату (датаНачала, числоРабДней);
Возвращает дату, отстоящую от даты, заданной па раметром датаНачала, на число рабочих дней, за данное параметром числоРабДней. День считается рабочим, если атрибут Значение соответствующей записи календаря кален не равен нулю. Параметр числоРабДней не должен быть меньше нуля Методы праздников
праздн.Новый(датаПраздн, числоРабЧасов);
Добавляет в базу данных (файл CL.DBF) новый праздник. Параметр датаПраздн задает дату празд ника, а числоРабЧасов - число рабочих часов в праздник. Параметр числоРабЧасов неотрицате лен и меньше 100
праздн.Удалить(датаПраздн);
Удаляет из базы данных праздник, приходящийся на дату датаПраздн. Если записи о таком празднике нет, то метод никаких изменений не производит
Замечание. Имена кален и праздн, употребленные в табл. 7.8 перед названиями ме тодов, могут быть произвольными.
Пример. В текущем году праздник 12 декабря переносится на 14 декабря. По скольку перенос затрагивает все календари, то для их модификации используем объект Метаданные и методы календарей Автозаполнение и УчитыватьПраздники. процедура Выполнить() перем празд, тГод, пСтар, пНов, нм, км, всегоКален, ин, идеи, кален; ОчиститьОкноСообщений(); тГод = ДатаГод(ТекущаяДата()); // Текущий год, например 2001 пСтар = Дата("12.12." +тГод); //Дата старого праздника пНов = Дата("14.12." + тГод); // Дата нового праздника праздн = СоздатьОбъект("Праздники"); // Перенос праздника праздн.Удалить(пСтар); праздн.Новый(пНов, 0); // Число рабочих часов равно нулю нм = Дата("01.12." + тГод); // Начало и конец декабря км = КонМесяца(нм); // Всего календарей в конфигурации (в метаданных) всегоКален = Метаданные.Календарь(); для ин = 1 по всегоКален цикл // Атрибут очередного календаря идеи = Метаданные.Календарь(ин).Идентификагор; // Календарь как объект кален = Календари.ПолучитьАтрибут(иден); // Учитываем при автозаполнении праздники кален.У читыватьПраздники(1); кален.Автозаполнение(нм, км); конецЦикла; // для Предупреждение("Готово."); конецПроцедуры // Выполнить Замечание. Функция ОткрытьФорму непригодна для активизации формы списка объекта Праздники (см. рис. 7.35) и отображения календарей. 7.5.2.4.
УСОВЕРШЕНСТВОВАНИЕ
СПРАВОЧНИКА
СОТРУДНИКИ_2
Если стоит задача автоматизировать учет рабочего времени сотрудника, то его следует связать с подходящим календарем. Такие календари у нас уже есть (см. рис. 7.32). Если нет отклонений, то рабочее время сотрудника полностью определяется его календарем. В противном случае необходимо использовать табель. Поскольку учет рабочего времени - задача трудоемкая, дополним справочник Сотрудники_2 реквизитом Календарь (рис. 7.37, а), задав ему свойства в соответствии с рис. 7.37, б.
Рис.
7.37. Новый реквизит Календарь и его свойства: а -реквизит; б - свойства
Внесем изменения и в диалог формы элемента, приведя его в соответствие с рис. 7.38. (О порядке выполнения изменений см. разд. 5.7.)
Рис. 7.38. Обновленный диалог формы элемента справочника Сотрудники_2 Календарь - обязательный для заполнения реквизит справочника, поэтому модуль формы элемента, его предопределенную процедуру ПриЗаписи (разд. 5.3.4.2), допол ним следующей проверкой: если Календарь.Выбран() = 0 тогда Предупреждение("3адайте Календарь"); СтатусВозврата(0); // Данные не записываются; форма не закрывается // Перемещаемся на элемент диалога Календарь Активизировать("Календарь", 0); возврат; конецЕсли; Формы списка (основную и для ввода) справочника Сотрудники_2 оставим без изменений. Ускорим ввод начальных значений нового реквизита, запустив в обработке Проба программу, назначающую работникам с высшим и неоконченным высшим образова нием календарь Служащие_2, а всем прочим - Рабочие_2. процедура Выполнить() перем сСотр_2; ОчиститьОкноСообщений(); сСотр_2 = СоздатьОбьект("Справочник.Сотрудники_2"); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если (сСотр_2.0бразование.Наименование = "Высшее") или (сСотр_2.0бразование.Наименование = "Неоконченное высшее") тогда сСотр_2.Календарь = Календари.Служащие_2; иначе сСотр_2. Календарь = Календари.Рабочие_2; конецЕсли; // Не забываем сохранить изменения сСотр_2.3аписать(); конецЦикла; // пока Предупреждение("Готово."); // Просмотр результата. Для просмотра необходимо в открытой форме списка // войти в режим редактирования элемента ОткрытьФорму("Справочник.Сотрудники_2"); конецПроцедуры // Выполнить
7.6. ТАБЕЛЬ 7.6.1. ПОРЯДОК ЗАПОЛНЕНИЯ ТАБЕЛЯ На предприятии табель - это документ, учитывающий отработанное время. Изна чально табель сотрудника может формироваться на бумажном носителе. Далее данные переносятся в документ 1С, которому мы также дадим имя Табель. В дальнейшем, употребляя это имя, мы будем иметь в виду документ 1С. Табель играет важную роль в задаче начисления зарплаты. В частности, все доку менты, вводящие начисления (удержания) и зависящие от проработанного времени, должны оформляться на основании табеля. Алгоритм учета рабочего времени за месяц (для одного сотрудника): 1. Начало. 2. Выбрать сотрудника. 3. Заполнить, используя календарь сотрудника, табель 4. Внести при необходимости в табель коррективы. 5. Вычислить суммарное отработанное время. Результат разместить в столбце Всего часов. 6. Провести документ. Результатом его проведения является добавление в ЖЗ расчетов с Оклад_2, НДФЛ_2 и ВБанк_2. Расчет регистрируется в текущем расчетном периоде. 7. Конец. Данный алгоритм воспроизводится для каждого сотрудника предприятия, зане сенного в табель, то есть в общем случае один табель порождает несколько расчетов. Предоставим пользователю следующие возможности по заполнению табеля: 1. Добавление в табель (его табличную часть) по одному сотруднику. Производится при помощи метода модуля формы ОткрытьПодбор и предопределенной процеду ры модуля формы ОбработкаПодбора; существующие записи сохраняются. 2.
Заполнение табличной части для сотрудников выбранного подразделения; сущест вующие записи удаляются.
3.
Занесение в табличную часть всех сотрудников предприятия; существующие запи си удаляются.
В каждом из режимов табличная часть после перенесения в нее сотрудника авто матически заполняется данными его календаря. Также предусмотрим в интерфейсе документа удаление одной выбранной записи табличной части или всех ее записей. При каждом сохранении документа будем выполнять его перепроведение. Позаботимся о том, чтобы в документ один и тот же сотрудник не попал дважды. Однако если по какой-либо причине данные о сотруднике введены дважды в разные табели, то проведение любого из этих табелей должно приводить к обновлению соот ветствующих ему расчетов в ЖЗ. Замечание. Имеющийся в 1С документ ВводОтработанногоВремени весьма сло жен, так как рассчитан на многие случаи жизни. Внушителен и объем кода, сопровож дающий этот документ. Так, модуль формы документа содержит около 600 строк плотного кода. Поскольку наш алгоритм ввода рабочего времени и последующей про-
водки документа гораздо проще, то имеет смысл взамен существующего в 1С доку мента создать и использовать свой, более простой и эффективный. Так и поступим.
7.6.2. РЕКВИЗИТЫ И ДИАЛОГ ТАБЕЛЯ Перечисленные требования к табелю позволяют определиться относительно сос тава диалога документа и его реквизитов (табл. 7.9). Таблица 7.9 Реквизиты документа Табель Реквизиты кто
текСтрока
ДатаДок НомерДок Сотрудник ч1-ч31
Описание Способ заполнения табличной части документа. Используется как идентификатор радиокнопок диалога формы списка документа. Если кто = 1, то подбор выполняется по одному сотруднику; если кто = 2, то в табличную часть документа заносятся сотрудники выбранного подразделе ния, и если кто = 3, в табличную часть заносят ся сотрудники всего предприятия. Используется при открытии проведенного документа Номер выбранной строки в списке подразделе • ний сПодр. Используется при открытии прове денного документа для верного позиционирова ния списка подразделений Дата документа (реквизит задан по умолчанию) Номер документа (реквизит задан по умолча нию) Сотрудник, табель которого вводится
Тип (разновидность типа) /Длина.Точность Числовой/ 1.0
Числовой/3.0
Дата Числовой /5.0
Справочник. Сотрудники 2 чi, - число фактически отработанных часов в день Числовой/4.1 i,i=1,2, ..,31
Создавая документ, попутно, пользуясь услугами конструктора (рис. 7.39), доба вим в конфигурацию журнал Табель, в котором и будут размещаться формируемые пользователями документы учета отработанного времени.
Рис. 7.39. Новый журнал для документа Табель Задавая свойства документа Табель (рис. 7.40), не забудем активизировать флажок Расчет.
Рис. 7.40. Свойства и реквизиты документа Табель Диалог формы табеля оформим, следуя рис. 7.41.
Рис. 7.41. Диалог формы табеля Замечания: 1. Чтобы просмотреть неотображенные столбцы табличной части формы, следует воспользоваться стрелками горизонтальной полосы прокрутки. 2.
Элемент диалога сПодр является списком значений. Он заполняется в предопреде ленной процедуре ПриОткрытии, которая, используя справочник Сотрудики_2, заносит в сПодр список подразделений предприятия.
Нажав для проверки диалога Ctrl+R, обнаружим некоторое несоответствие в расположении граф 1-31, а также дополнительную неименованную графу 32 (рис. 7.42).
Рис. 7.42. Расположение граф 1-32
Графа 32 введена с целью симметричного размещения столбцов 1-16 и 17-31, что и получилось на рис. 7.42. Для достижения такого результата необходимо ячейки 1-32 сделать одной ширины. К сожалению, простого средства выравнивания ширины столбцов табличной части в 1С нет. С 32-й графой связана формула "X", обеспечивающая вывод этой буквы в ячейки столбца. Добавление столбца таблицы, напомним, осуществляется при помощи иконки , расположенной на панели инструментов Элементы диалога. Чтобы разместить столбцы 17-32 под столбцами 1-16, столбец 1 получил свойство Новая колонка (рис. 7.43, а), а последующие столбцы - свойство В той же колонке (рис. 7.43, б).
а
6 Рис. 7.43. Положение полей ввода ч1-ч32: положение поля ввода ч1; положение полей ввода ч2-ч32 Соответствие между заголовками столбцов табличной части документа и их иден тификаторами (реквизитами документа) отображено в табл. 7.10. Таблица 7.10 Заголовками столбцов табличной части документа и их идентификаторы
Замечание. Ввод одного сотрудника, так же как и группы сотрудников, осуществ ляется при нажатии на кнопку Заполнить. То есть мы отказались от выбора сотрудника непосредственно в табличной части, задав свойства ячеек столбца Сотрудник в соот ветствии с рис. 7.44.
Рис. 7.44. Свойства ячеек столбца Сотрудник
Это, несомненно, делает диалог более дружественным. Действительно, каждый выббр сотрудника приводит к заполнению реквизитов ч1-ч31. При этом случайный выбор сотрудника в строке, содержащей ранее введенные данные, приведет к обнов лению значений этих реквизитов. Если же они содержали ручную правку, то ее резуль таты будут этим обновлением уничтожены. Теперь же, когда ячейки столбца недос тупны, такой ошибки быть не может. Группа радиокнопок, имеющая название Режим заполнения, формируется сле дующим образом: 1. Добавляется радиокнопка с заголовком Сотрудник, ей присваивается идентифика тор, например кто, который и будет служить идентификатором группы. На зак ладке дополнительно этой радиокнопке задается формула Проследить() и свойст во Первый в группе. 2. Добавляются радиокнопки с заголовками Цех и Все. Никаких дополнительных свойств для них не задается. 3. Кнопки обводятся рамкой группы, которой присваивается заголовок Режим запол нения. После таких действий переменная диалога кто принимает значение: 1, если выбрана радиокнопка Сотрудник; и в этом случае табель заполняется в резуль тате подбора сотрудников; • 2, если выбрана радиокнопка Цех; табель заполняется на сотрудников подразделения, выбранного в поле сПодр; • 3, если выбрана радиокнопка Все; в табель заносятся все сотрудники предприятия. Кроме задания режима заполнения, радиокнопки управляют доступностью эле ментов диалога сПодр и Заполнить. С текстом заголовка свяжем следующую формулу: •
"Ввод отработанного времени за "+ Формат(ДатаДок, "ДММММГПТ") + ", " + ?(кто = 2, сПодр.ПолучитьЗначение(сПодр.ТекущаяСтрока()), ?(кто = 3, "все предприятие", "")) + "." Тогда, если, например, ДатаДок - это '12.12.200Г и выбран второй цех, в заголовке появится сообщение, в котором благодаря встроенной функции Формат дата '12.12.2001' преобразована в текст Декабрь 2001 г.: Ввод отработанного времени за Декабрь 2001 г., 02 Цех
Формулы (процедуры), с которыми связываются поля диалога, перечислим в табл. 7.11, в которой в графе Элементы диалога курсивом приводятся идентифика торы элементов диалога, а прямым шрифтом - их имена (идентификаторы не заданы). Имена, соответствующие идентификаторам, легко узнаваемы. Таблица 7.11 Элементы диалога документа Табель и их формулы
Управляет доступностью элемента диалога сПодр. Если кто = 1 (активна радиокнопка Сотрудник) или кто = 3, то элемент сПодр недоступен; если кто = 2, то доступен сПодр ОтобразитьСПодр() Отображает имя выбранного в списке сПодр подразделения и запоминает значение текущей строки списка текСтрока Заполнить Заполнить( ) Заполняет табличную часть для сотрудников выбранного подразделения, если кто = 2, или всего предприятия, если кто = 3. Открывает форму подбора сотрудников, если кто = 1 Удалить УдалитьЗап(1) Удаляет текущую строку табличной части. Удалению предшествует запрос о необходимости удаления. Доступен, если в табличной части есть хотя бы одна запись Очистить УдалитьЗап(2) Удаляет все строки табличной части. Удалению предшествует запрос о необходимости удаления. Доступен, если в табличной части есть хотя бы одна запись ФормаТ-13 Т13() Распечатывает, если не пуста табличная часть, табель по форме Т-13, которая предоставляется 1С Действия глДействия(, сДейст) Открывает документ в журнале (если он в нем есть). Переменная модуля сДейст (список значений) заполняется в процедуре ПриОткрытии формы документа значением "Открыть в журнале". Процедура глДействия расположена в глобальном модуле и поставляется с 1С Провести #Провести Выполняет проведение (перепроведение) документа Табель, то есть заносит в ЖЗ расчеты с ВР Оклад_2, НДФЛ_2 и ВБанк_2 ОК #3аписать Провести Проводит (перепроводит) и сохраняет документ Закрыть в журнале документов Табель Закрыть Закрывает документ #3акрыть кто
Проследить()
Теперь у нас есть достаточно информации, чтобы перейти к созданию кода модуля формы документа и самого документа. Напомним, что в модуле документа можно разместить его предопределенные процедуры ОбработкаПроведения и/или Обрабо ткаУдаленияПроведения.
7.6.3. МОДИФИКАЦИЯ МЕНЮ ИНТЕРФЕЙСА УЧЕНИК Для доступа к созданным журналам и документам модифицируем отдельные ко лонки меню интерфейса Ученик (рис. 7.45). Для вновь введенных в колонку Журналы документов пунктов (Расчеты и Табель) укажем соответственно объекты Журнал.Расчеты и Журнал.Табель.
Рис. 7.45. Модифицированные колонки меню интерфейса Ученик
7.6.4. МОДУЛЬ ФОРМЫ СПИСКА ДОКУМЕНТА Содержит, кроме названных в табл. 7.11, процедуру ПриОткрытии. перем часы[31], сДейст;
//
часы - массив часов по календарю
процедура Проследить() далее процедура процедура процедура процедура процедура процедура
Заполнить() далее ОбработкаПодбора(значен, конт) далее ОдинСотрудник() далее Ч131 () далее Всего() далее УдалитьЗап(флаг) далее
// Формирует список подразделений сПодр и устанавливает начальные значения // переменных диалога процедура ПриОткрытии() перем сСотр_2, дост; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Формируем, анализируя справочник Сотрудники_2, список подразделгний // Задаем выбор элементов справочника в порядке возрастания их кодов сСотр_2.ПорядокКодов(); сСотр_2.ВыбратьЭлементы( ); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда сПодр.ДобавитьЗначение(сСотр_2.ТекущийЭлемент(), сСотр_2.Наименование); конецЕсли; конецЦикла; // пока если Проведен()= 0 тогда // Если документ не проведен кто = 2; // Табель на сотрудников выбранного подразделения текСтрока = 1; // Позиционируемся на цехе 01 в списке сПодр иначе // Документ проведен если кто = 2 тогда // Правильно позиционируем список значений сПодр сПодр.ТекущаяСтрока(текСтрока); конецЕсли; конецЕсли; Проследить();
// дост = 1, если в табличной части есть записи. Переменная используется // для управления доступностью элементов диалога формы списка документа дост = ?(КоличествоСтрок() = 0, 0, 1); форма.Удалить.Доступность(дост); форма.Очистить.Доступность(дост); форма.ПанельИнструментов(0); // Отключаем панель инструментов форма.Заголовок("Табель"); // Для кнопки Действия (рис. 7.40) сДейст = СоздатьОбъект("СписокЗначений"); сДейст.ДобавитьЗначение("Открыть в журнале"); конецПроцедуры // ПриОткрытии // Управляет доступностью элемента диалога сПодр процедура Проследить() если (кто = 1) или (кто = 3) тогда // Табель на отобранных или всех сотрудников форма.СПодр.Доступность(0); иначе // кто = 2 форма.СПодр.Доступность(1); конецЕсли; конецПроцедуры // Проследить процедура ОтобразитьСПодр() // Формула элемента диалога сПодр текСтрока = сПодр.ТекущаяСтрока(); сПодр.ПолучитьЗначение(текСтрока); конецПроцедуры // ОтобразитьСПодр процедура ОдинСотрудник() // Заполняет массив часы для одного сотрудник перем ин, кален; для ин - 1 по 31 цикл // Инициализация массива часов часы[ин] = 0; конецЦикла; // для кален = Сотрудник.Календарь; если кален.Выбран() = 1 тогда кален.ВыбратьДаты(НачМесяца(ДатаДок), КонМесяца(ДатаДок)); ин = 0; // Заносим в массив часы данные календаря сотрудника пока кален.СледующаяДата() = 1 цикл ин = ин+ 1; часы[ин] = кален.Значение; конецЦикла; // пока конецЕсли; конецПроцедуры // ОдинСотрудник // Определяем значения реквизитов документа ч1 - чЗ 1 // Далее эти значения могут быть отредактированы процедура Ч131() ч1 = часы[1]; ч2 = часы[2]; чЗ = часы[3]; ч4 = часы[4]; ч5 = часы[5]; чб = часы[6]; ч7 = часы[7]; ч8 = часы[8]; ч9 = часы[9]; ч10 = часы[10]; ч11 = часы[11]; ч12 = часы[12]; ч13 = часы[13]; ч14 = часы[14]; ч15 = часы[15];
ч16 = часы[16]; ч17 - часы[17]; ч18 = часы[18] ч19 = часы[19]; ч20 = часы[20]; ч21 = часы[21] ч22 = часы[22]; ч23 = часы[23]; ч24 = часы[24] ч25 = часы[25]; ч26 = часы[26]; 427 = 4асы[27] 428 = 4асы[28]; 429 = часы[29]; 430 = часы[30]; 431 = 4асы[31]; конецПроцедуры // Ч131 процедура Всего() // Возвращает общее 4исло отработанных 4асов всегоЧасов = ч1 + ч2 + ч3 + ч4 + ч5 + ч6 + ч7 + ч8 + ч9 + ч10 +
ч11 +ч12+ч13+ч14+ч15+ч16+ ч17+ ч18+ч19 + ч20 + ч21 + ч22 + ч23 + ч24 + ч25 + ч26 + ч27 + ч28 + ч29 + ч30 + ч31; конецПроцедуры // Всего // Заполняет табличную часть документа Табель для одного или группы сотрудников процедура Заполнить() перем подр; // Удаляем, если табличная часть заполняется для подразделение или всего предприятие, // ее строки, но только с согласия пользователя если (кто > 1) и (КоличествоСтрок() > 0) тогда если Вопрос("Удалить имеющиеся строки?", "Да+Нет") = "Да" тогда УдалитьСтроки(); // 04ищаем табли4ную 4асть документа иначе возврат; конецЕсли; конецЕсли; если кто = 1 тогда // Указываем явно имя основной формы списка для метода ОткрытьПодбор // Задаем режим множественного выбора // Добавляем в табличную часть в результате каждого выбора в открытой форме // данные одного сотрудника. Собственно добавление осуществляет // предопределенная процедура модуля формы ОбработкаПодбора ОткрытьПодбор("Справо4ник.Сотрудники_2", "ФормаСписка",, 1); ина4еЕсли кто >= 2 тогда // Подразделение или все предприятие сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Заносим в табличную часть данные о сотрудниках выбранного подразделения подр если кто = 2 тогда // Подразделение подр = сПодр.ПолучитьЗначение(сПодр.ТекущаяСтрока()); сСотр_2.ИспользоватьРодителя(подр); конецЕсли; сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 0 тогда // Только сотрудники // Используем уже имеющуюся процедуру ОбработкаПодбора, // выполняющую обработку одного сотрудника ОбработкаПодбора(сСотр_2,""); конецЕсли; конецЦикла; // пока конецЕсли; если КоличествоСтрок() > 0 тогда форма.Удалить. Доступность(1); форма.Очистить.Доступность(1); конецЕсли; конецПроцедуры // Заполнить
процедура ОбработкаПодбора(значен, конт) // Запрещаем, если добавляем по одному сотруднику, выбор того же лица дважды если (кто = 1) и (ВыбратьСтроки( ) = 1) тогда пока ПолучитьСтроку( ) = 1 цикл если Сотрудник = значен.ТекущийЭлемент( ) тогда Предупреждение("Сотрудник уже выбран."); возврат; конецЕсли; конецЦикла; // пока конецЕсли; НоваяСтрока(); // Новая строка в табличной части табеля Сотрудник = значен.ТекущийЭлемент(); ОдинСотрудник(); // Заполняет массив часы для одного сотрудника // Определяем значения реквизитов документа ч1-чЗ 1 и всегоЧасов Ч131(); Всего(); // Если есть хотя бы одна запись, то есть что удалять если (кто = 1) и (КоличествоСтрок() = 1) тогда форма.Удалить. Доступность(1); форма.Очистить.Доступность(1); конецЕсли; конецПроцедуры // ОбработкаПодбора // Удаляет с согласия пользователя, если флаг = 1, текущую запись табличной // части документа, и, если флаг = 2, все записи процедура УдалитьЗап(флаг) перем вопр; вопр = "Удалить " + ?(флаг= 1, "одну запись", "все записи") + "?"; если Вопрос(вопр, "Да+Нет") = "Да" тогда если флаг = 1 тогда УдалитьСтроку(); иначе // флаг = 2 УдалитьСтроки(); конецЕсли; если КоличествоСтрок() = 0 тогда форма.Удалить.Доступность(0); форма.Очистить.Доступность(0); конецЕсли; конецЕсли; конецПроцедуры // УдалитьЗап Результат автоматического заполнения табеля для сотрудников второго цеха при веден на рис. 7.46.
Рис. 7.46. Фрагмент табеля цеха 02
7.6.5. МОДУЛЬ ДОКУМЕНТА Содержит предопределенную процедуру ОбработкаПроведения. Она для каждого сотрудника табличной части заносит в ЖЗ расчеты Оклад_2, Н Д Ф Л 2 и ВБанк_2, а также вычисляет результат каждого введенного расчета. Поскольку вводимые расче ты должны обновляться при перепроведении, то метод их ввода - ВвестиРасчет, а не ЗаписатьРасчет. Даты начала и окончания всех расчетов совпадают с датами начала и конца текущего периода ЖЗ. Функция НайтиХозОп возвращает связанную с ВР хозяйственную операцию. Эти связи хранит справочник ХозОпДляВР (разд. 7.3.7). Число отработанных часов вводится только для ВР Оклад_2. функция НайтиХозОп(хозОп, ВР) далее процедура ВводРасчВЖЗ(жз, нтп, ктп, хозОп, ВР) далее процедура ОбработкаПроведения() перем жз, нтп, ктп; // Объект с разновидностью типа Справочник.ХозОпДляВР перем хозОп; // Для поиска хозяйственных операций ВР хозОп = СоздатьОбъект("Справочник.ХозОпДляВР"); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); ктп = жз.КонецТекущегоПериода(); ВыбратьСтроки(); // Открываем выборку строк документа пока ПолучитьСтроку( ) = 1 цикл // Устанавливаем реквизиты для каждого вводимого ВР // ВР Оклад_2 жз.УстановитьРеквизит("всегоЧасов", всегоЧасов); ВводРасчВЖЗ(жз, нтп, ктп, хозОп, ВидРасчета.Оклад_2); // ВР НДФЛ_2 ВводРасчВЖЗ(жз, нтп, ктп, хозОп, ВидРасчета.НДФЛ_2); // ВР ВБанк_2 ВводРасчВЖЗ(жз, нтп, ктп, хозОп, ВидРасчета.ВБанк_2); конецЦикла; // пока // Вычисляем результаты введенных расчетов // Выборка записей ЖЗ по документу не дает верного результата // Поэтому используем выбор записей текущего периода по объекту ВыбратьСтроки(); // Открываем выборку строк документа пока ПолучитьСтроку() = 1 цикл жз.ВыбратьПериодПоОбъекту(Сотрудник, нтп); пока жз.ПолучитьЗапись() = 1 цикл если жз.Документ = ТекущийДокумент() тогда // Метод Рассчитать вызывает процедуру ПровестиРасчет соответствующего ВР жз.Рассчитать( ); // или жз.ВыполнитьРасчет конецЕсли; конецЦикла; // пока конецЦикла; // пока
если Проведен() = 0 тогда // Если документ проводится впервые // Ограничиваем время показа окна с предупреждением тремя секундами Предупреждение("Документ проведен.", 3); иначе Предупреждение("Документ перепроведен.", 3); конецЕсли; конецПроцедуры // ОбработкаПроведения // Вводит новые или редактирует имеющиеся в ЖЗ расчеты с заданным ВР процедура ВводРасчВЖЗ(жз, нтп, ктп, хозОп, ВР) жз.УстановитьРеквизит("строкаДок", НомерСтроки); жз.УстановитьРеквизит("хозОп", НайтиХозОп(хозОп, ВР)); жз.ВвестиРасчет(Сотрудник, ВР, нтп, ктп); конецПроцедуры // ВводРасчВЖЗ // Возвращает хозяйственную операцию ВР функция НайтиХозОп(хозОп, ВР) // Ищем простым перебором в справочнике ХозОпДляВР вид расчета ВР хозОп.ВыбратьЭлементы(); флаг = 0; пока хозОп.ПолучитьЭлемент() = 1 цикл если хозОп.ВР = ВР тогда флаг = 1; прервать; конецЕсли; конецЦикла; // пока если флаг = 1 тогда возврат хозОп.хозОп; иначе возврат ПолучитьПустоеЗначение(хозОп); конецЕсли; конецФункции // НайтиХозОп
Фрагмент ЖЗ после проведения табеля, например для сотрудников 2-го цеха, по казан на рис. 7.47.
Рис. 7.47. Документ Табель проведен для сотрудников 2-го цеха
Замечания: 1. Расчеты одного объекта располагаются в ЖЗ в порядке очередности их исполнения. 2. Чтобы быстро найти в ЖЗ сотрудника, следует переместиться на поле Сотрудник и начать набирать на клавиатуре его фамилию (рис. 7.48).
Рис. 7.48. Локализация сотрудника в форме списка ЖЗ
При поиске должна быть выбрана иконка , обеспечивающая отображение всех сотрудников текущего подразделения. 3. Ввод нового документа Табель проще всего выполнить, воспользовавшись кноп кой Ввод расчета, предусмотренной в диалоге формы ЖЗ (см. рис. 7.21). 4. Чтобы открыть документ Табель, породивший текущий расчет, достаточно, нахо дясь в ЖЗ, нажать на правую кнопку мыши и выбрать соответствующий пункт появившегося меню (рис. 7.49).
Рис. 7.49. Открываем документ Табель Тот же пункт можно выбрать и из колонки Действия меню интерфейса Ученик. Команда проведения документа при выполнении перепроведения отслеживает все возможные произошедшие в документе изменения. Так, если в предопределенной про цедуре ОбработкаПроведения после проведения документа закомментировать код, вводящий, например, расчеты с ВР ВБанк_2, то соответствующие расчеты исчезнут из ЖЗ в результате перепроведения документа. Если из проведенного документа уда лить табель для какого-либо сотрудника, то в результате перепроведения все ранее по рожденные документом расчеты, относящиеся к удаленному сотруднику, будут удале ны и из ЖЗ и т. д. Если по какой-либо причине табель на сотрудника заполняется повторно, то его проведение не приведет к появлению в ЖЗ новых расчетов, поскольку расчеты с ВР Оклад_2, НДФЛ_2 и ВБанк_2 созданы как самовытесняющиеся. Однако при этом ста рые расчеты будут заменены на вновь вводимые, поскольку нами использован метод ВвестиРасчет. Метод ЗаписатьРасчет также вводит расчеты в ЖЗ, но при этом само вытесняющиеся расчеты остаются в ЖЗ неизмененными. Если же перепроводится существующий Табель, то методы ВвестиРасчет и Запи сатьРасчет работают совершенно одинаково. Результаты введенных документом Табель расчетов оцениваются в процедуре Об работкаПроведения после ввода расчетов следующим кодом: // Выполняем расчет всех введенных записей жз.ВыбратьЗаписиПоДокументу(ТекущийДокумент( ));
пока жз.ПолучитьЗапись() = 1 цикл // Метод Рассчитать вызывает процедуру ПровестиРасчет соответствующего ВР жз.Рассчитать( ); // или жз.ВыполнитьРасчет( ); конецЦикла; // пока Рассмотрим теперь, как задаются свойства используемых нами ВР, и запишем для них код, возвращающий результат расчета. Прежде, однако, зададим правила перера счета ВР.
7.6.6. ФОРМА СПИСКА ЖУРНАЛА ДОКУМЕНТОВ ТАБЕЛЬ Документы Табель отображаются в одноименном журнале документов (см. рис. 7.40). Диалог формы списка этого журнала документов создадим в соответствии с рис. 7.50.
Рис. 7.50. Диалог формы списка журнала документов Табель Модуль формы списка журнала документов Табель содержит предопределенную процедуру ПриОткрытии. Назначение процедуры такое же, как и у одноименной про цедуры модуля формы списка журнала кадровых приказов (раз. 5.8.3.2). // Список действий по документу. Передается процедуре глобального модуля // глДействия(ТекущийДокумент, сДейст) перем сДейст; // Формирует список действий и устанавливает интервал журнала, // отображающий все введенные документы вида Табель процедура ПриОткрытии() перем дНач; // Дата начала интервала журнала Табель перем флаг, док; ОчиститьОкноСообщений(); // Определяем список действий для кнопки "Действия" сДейст.ДобавитьЗначение("Структура подчиненности"); сДейст.ДобавитьЗначение("Ввести на основании"); сДейст.ДобавитьЗначение("Движения документа"); флаг = 1; // Равен единице, если удалось создать док попытка док = СоздатьОбъект("Документ.Табель"); исключение флаг = 0; конецПопытки; если флаг = 1 тогда // Если документ Табель создан // Находим документ с наименьшей датой. По умолчанию документы // располагаются в выборке по возрастанию их дат док.ВыбратьДокументы();
если док.ПолучитьДокумент() = 1 тогда дНач = док.ДатаДок; иначе дНач = ТекущаяДата(); конецЕсли; иначе // дНач = ТекущаяДата(); конецЕсли; УстановитьИнтервал(дНач, ТекущаяДата()); конецПроцедуры // ПриОткрытии
Искомого документа нет
// В основной программе модуля всего один оператор сДейст = СоздатьОбъект("СписокЗначений");
7.7. ПРАВИЛА ПЕРЕРАСЧЕТА ВИДОВ РАСЧЕТОВ Все используемые нами ВР в конфигурацию уже добавлены (разд. 7.3.5). Теперь нам нужно уточнить их свойства. Известно, что результаты одних расчетов зависят от результатов других. Так, на логом облагаются ВР Оклад_2 и все премии. Поэтому ВР НДФЛ_2, возвращающий размер налога, называется зависимым или подчиненным. Виды расчетов, от которых ВР Н Д Ф Л 2 зависит, называются ведущими. Другой пример: ВР ВБанк_2 зависит от всех иных ВР и должен пересчитываться при вводе каждого расчета. Система информирует о необходимости расчета зависимых записей ЖЗ, меняя значение их атрибута Рассчитана с 1 на 0. Соответствующим образом изменяются и иконки, сопровождающие записи. Такая смена значения атрибута Рассчитана обес печивается правилами перерасчета, которые определяются в конфигурации в подраз деле Правила перерасчета раздела Виды расчетов. Добавим новое правило с именем Н Д Ф Л 2 , определив ведущие и зависимый ВР в соответствии с рис. 7.51.
Рис. 7.51. Правило перерасчета для ВР НДФЛ_2
Это правило будет срабатывать при вводе ВР, отмеченных в левом списке рисун ка, приводя к обнулению значения атрибута Рассчитана у ВР, отмеченного в правом списке. В нашем случае галочку возле ВР Оклад_2 из левого списка можно убрать, по скольку расчеты с ВР Оклад_2 и НДФЛ_2 вводятся в ЖЗ одним документом, что яв ным образом обеспечивает изменение значения атрибута Рассчитана. Добавим теперь в конфигурацию и другие нужные нам правила перерасчета. Их общий список приведем в табл. 7.12. Таблица 7.12 Новые правила перерасчета Идентифи катор
Ведущие ВР
Зависимый ВР
НДФЛ_2
Оклад_2, ПремияКоэф_2, ПремияСум_2, Премия 1234_2
НДФЛ_2
ВБанк_2
НачСальдо 2, Оклад 2, ПремияКоэф 2, ПремияСум 2, Премия1234_2, НДФЛ_2
ВБанк_2
Премия 1234_2
Оклад_2, ПремияКоэф_2, ПремияСум_2
Премия1234_2
Таким образом, благодаря этим правилам мы будем практически всегда иметь в ЖЗ верные сведения о том, какие записи нужно рассчитать заново, воспользовав шись, например, соответствующим пунктом колонки Действия меню системы (рис. 7.52).
Рис. 7.52. Расчет отдельной записи ЖЗ Правила перерасчета срабатывают не только при вводе, но и при ручной правке ведущего ВР. Замечание. Ситуация, когда не работают правила перерасчета, анализируется в разд. 7.10.1.
7.8. ВИДЫ РАСЧЕТОВ ДОКУМЕНТА ТАБЕЛЬ 7.8.1. ВИД РАСЧЕТА ОКЛАД_2 Выполним всю цепочку задания свойств ВР Оклад_2, включающую также и напи сание кода процедуры получения результата. Предварительно, однако, нам потребуется создать группу ВР ВсеНачисления_2, в которую мы будем включать расчеты-начисления, облагаемые НДФЛ. Войдем к конфигурацию, добавим группу ВР ВсеНачисления_2 (рис. 7.53), вклю чив в нее указанные на рис. 7.54 ВР.
Рис. 7.53. Добавляем группу ВР ВсеНачисления_2 в конфигурацию системы
Рис. 7.54. Группа ВР ВсеНачисления_2 Раскроем далее окно задания свойств ВР Оклад_2 (рис. 7.55).
-••
Рис. 7.55. Окно задания свойств ВР Оклад_2
Воспользовавшись кнопкой Настройка вытеснения, зададим ВР Оклад_2 как само вытесняющийся (рис. 7.56).
Рис. 7.56. Теперь ВР Оклад_2 является самовытесняющимся Допишем в модуль ВР следующий код: процедура ПровестиРасчет() // Процедура модуля ВР Оклад_2 // Процедура выполняется при проведении расчета с ВР Оклад_2 перем всегоЧасовПоКалендарю, нтп, ктп; // На случай непредусмотренной ошибки
если ПустоеЗначение(Объект.Календарь) = 1 тогда Сообщить("Сотрудник " + СокрП(Объект.Наименование) + " не имеет календаря."); возврат; конецЕсли; нтп = НачалоТекущегоПериода(); ктп = КонецТекущегоПериода(); всегоЧасовПоКалендарю = Объект.Календарь.Часов(нтп, ктп); если всегоЧасовПоКалендарю > 0 тогда результат = Объект.Оклад.Получить(ктп) * всегоЧасов / всегоЧасовПоКалендарю; иначе // На случай непредусмотренной ошибки Сообщить("В календаре сотрудника " + СокрП(Объект.Наименование) + " нет рабочих дней."); конецЕсли; конецПроцедуры // ПровестиРасчет Замечания: \i.
Значение периодического реквизита Оклад, возвращается методом справочника Получить. Простое использование Объект.Оклад даст нулевой результат
2.
Весь код, если бы не проверки, можно было бы разместить в одном операторе: результат = Объект.Оклад.Получить(КонецТекущегоПериода()) * всегоЧасов / Объект.Календарь.Часов(НачалоТекущегоПериода(), КонецТекущегоПериода());
7.8.2. ВИД РАСЧЕТА НДФЛ_2 ВР НДФЛ_2 также сделаем самовытесняющимся. Ранее этот ВР мы включили в группу ВР ВсеУдержания_2, которую употребили при оценке начального сальдо. Нам же для расчета налога понадобится группа ВР ВсеНачисления_2. Ее присутствие сократит код процедуры вычисления результата: процедура ПровестиРасчет() // Процедура модуля ВР НДФЛ_2 // Процедура выполняется при проведении расчета с ВР НДФЛ_2 перем жз, нтп, ктп, налог; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); ктп = жз.КонецТекущегоПериода(); налог = 0; жз.ВыбратьПериодПоОбъекту(Объект, нтп); пока жз.ПолучитьЗапись() = 1 цикл если жз.ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1 тогда налог = налог + жз.результат; конецЕсли; конецЦикла; // пока // Константа СтавкаНалога является периодической результат = налог * Константа.СтавкаНалога.Получить(ктп) /100; КонецПроцедуры // ПровестиРасчет Замечание. Будет неверным вместо метода жз.ВыбратьПериодПоОбъекту(Объект, нтп); использовать метод жз.ВыбратьЗаписиПоОбъекту(Объект, нтп, ктп);
поскольку последний выбирает записи, период действия которых лежит между нтп и ктп. В то же время метод ВыбратьПериодПоОбъекту выбирает все записи, зарегист рированные в текущем периоде и действующие как в текущем, так и в будущих или прошлых периодах. Чтобы процедура работала, в конфигурацию необходимо добавить периодическую числовую константу СтавкаНалога, положив ее равной, например, 13 %. Последнее можно сделать, запустив после создания константы следующую процедуру: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба Константа.СтавкаНалога.Установить(ТекущаяДата(), 13); конецПроцедуры // Выполнить
7.8.3. ВИД РАСЧЕТА ВБАНК_2 Самовытесняющийся ВР ВБанк_2 оценивается следующей процедурой: процедура ПровестиРасчет() // Процедура модуля ВР ВБанк_2 // Процедура выполняется при проведении расчета с ВР ВБанк_2 // Формула расчета: // банк = Целая часть(Начальное сальдо + Все начисления - Все удержания) перем жз, нтп, ВР, банк; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); банк = 0; // См. замечание относительно метода ВыбратьЗаписиПоОбъекту в предыдущем разделе жз.ВыбратьПериодПоОбъекту(Объект, нтп); пока жз.ПолучитьЗапись() = 1 цикл ВР = жз.ВидРасч; если (ВР.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1) или (ВР = ВидРасчета.НачСальдо_2) тогда банк = банк + жз.результат; иначеЕсли ВР.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда банк = банк - жз.результат; конецЕсли; конецЦикла;//пока результат = Цел(банк); // Для записи в ЖЗ берем целую часть результата конецПроцедуры // ПровестиРасчет
7.9. ПРЕМИИ 7.9.1. СВОЙСТВА ДОКУМЕНТА В ЖЗ на данный момент введены расчеты только с обязательными ВР (рис. 7.47). Позаботимся теперь о премиях. Ввод расчетов-премий будем, как и договаривались, выполнять одним докумен том - Премия (см. табл. 7.2). Этим документом можно вводить как один выбранный ВР, например ПремияКоэф_2, так несколько премиальных ВР. Проведение документа осуществляется лишь в том случае, когда все сотрудники, представленные к премии и перечисленные в табличной части документа, имеют ненулевую премию. Причем
в режиме одновременного начисления нескольких премий, например ПремияКоэф_2, ПремияСум_2 и Премия 12342, они должны быть ненулевыми для каждого сотрудни ка, занесенного в документ. Расчеты, порождаемые документом, будут иметь один период действия, задавае мый в шапке документа. В табличной части мы перечислим сотрудников, получивших премию и ее размер. Напомним формулы для расчета премиальных: ПремияКоэф_2 = всегоЧасов * к3; // к3 - устанавливаемый руководителем коэффициент // По умолчанию к3 = 10 для всех сотрудников ПремияСум_2 = Сумма_премии; // Сумма премии устанавливается руководителем // к5 - постоянный для всех сотрудников коэффициент // Берется из списка периодических констант Премия 1234_2 = (Оклад_2 + ПремияКоэф_2 + ПремияСум_2) * к5; Премия 12342 не требует ввода какого-либо числового значения. Достаточно ука зать факт ее начисления, что можно сделать, например, при помощи, перечисления ДаНет. По умолчанию, если начисляется Премия1234_2, всем попавшим в список до кумента сотрудникам установим значение Да. Сотрудники, не получающие премию это вида, из списка удаляются. Период действия ВР-премий может быть разным. Поэтому нужно иметь возмож ность задавать даты начала и конца действия каждого ВР. Учитывая приведенные сведения, определим свойства документа в соответствии с рис. 7.57.
Рис. 7.57. Свойства документа Премия
Свойства реквизитов документа, кроме заданных по умолчанию, опишем в табл. 7.13. Таблица 7.13 Реквизиты документа Премия Реквизиты
ДатаНачКоэф, ДатаКонКоэф
Описание
Реквизиты шапки документа Соответственно дата начала и конца действия расчетов с ВР ПремияКоэф_2
ДатаНачСум, ДатаКонСум ДатаНач1234, ДатаКон1234
Соответственно дата начала и конца действия расчетов с ВР ПремияСум_2
кто
Способ заполнения табличной части документа. Используется как идентификатор радиокнопок диалога формы списка документа. Если кто = 1, то подбор выполняется по одному сотруднику; если кто = 2, то в табличную часть документа заносятся сотрудники выбранного подразделения. Используется при открытии проведенного документа Флаги начисления премий соответственно коэффициентом, суммой или 1234. Премия начисляется, если флаг равен единице, и не начисляется, если флаг равен нулю Номер выбранной строки в списке подразделений сПодр. Используется при открытии проведенного документа для верного позиционирования списка подразделений Реквизиты табличной части документа Сотрудник, получающий премию
коэф, сум, п1234
текСтрока
Сотрудник к3
Сумма Премия1234
Тип (разновидность типа) /Длина.Точность Дата "
Соответственно дата начала и конца действия расчетов с ВР Премия 1234_2
Коэффициент для расчета ПремииКоэф_2. По умолчанию к3 = 10. Соответствующий столбец табличной части имеет заголовок Коэф. Сумма премии по ВР ПремияСум_2 Флаг начисления премии по ВР Премия 1234_2
Числовой /1.0
Числовой /1.0
Числовой / 3.0
Справочник. Сотрудники_2 Числовой / 3.0
Числовой /10.2 Перечисление ДаНет
Новый документ можно завести как из формы списка ЖЗ Зарплата_2, нажав на кнопку Ввод расчета (см. рис. 7.21), так и из колонки Документы меню интерфейса Ученик (см. рис. 7.45). Старые документы по премии доступны из ЖЗ и могут быть открыты в журнале документов Расчеты.
Замечание. Выбор значения перечисления ДаНет выполняется из отображенного на рис. 7.58 списка.
Рис. 7.58. Выбор значения перечисления в ячейке столбца Премия1234 табличной части документа
7.9.2. ДИАЛОГ ФОРМЫ СПИСКА ДОКУМЕНТА 7.9.2.1. СЛОИ ДИАЛОГА Диалог формы списка документа Премия оформим (больше с учебной целью) с двумя закладками, расположив его реквизиты на четырех слоях. Состав основного слоя приведем в соответствие с рис. 7.59.
Рис. 7.59. Основной слой диалога формы списка документа Премия На втором слое, которому мы дали имя ВидПремии, расположим флажки коэф, сум и и 1234, указывающие на вид начисляемой премии (рис. 7.60).
Рис. 7.60. Элементы диалога слоя ВидПремии Конечно же, основной и второй слои можно было бы благополучно совместить, переместив вид премии на первый слой. Однако, вновь руководствуясь учебными целями, мы сохраним существующее разбиение элементов диалога по слоям. На слое номер 3, имя которого ИнтервалыРасчетов, предусмотрим задание дат начала и конца всех ВР (рис. 7.61), оставив справа полосу для размещения трех нижних кнопок четвертого слоя.
Рис. 7.61. Состав слоя третьего слоя
Последний, четвертый слой отведем под кнопки (рис. 7.62).
Рис. 7.62. Расположение кнопок на четвертом слое диалога
Замечание. При вставке в диалог числового реквизита формата 1.0 (или несколь ких числовых реквизитов такого формата) имеется возможность разместить его как поле ввода, флажок или переключатель. Если хотя бы один вставляемый реквизит имеет иной тип или формат, то все реквизиты можно вставить только как поля ввода. Поэтому мы выполнили поэтапную вставку, выбрав для реквизитов кто и прем поле типа переключатель. Задание дополнительных слоев осуществляется в результате выполнения цепочки Диалог - Слои - выбрать иконку Новый слой ( ) в появившемся диалоге (рис. 7.63) ввести имя слоя (рис. 7.64) - ОК.
Рис. 7.63. Слои диалога формы списка документа Премия
Рис. 7.64. Новый слой диалога В окне слоев (рис. 7.63) также осуществляется переход со слоя на слой, удаление слоев и их сортировка. Перемещение выделенных элементов диалога с одного слоя на другой производится посредством пункта Поместить колонки Диалог меню систе мы (рис. 7.65).
Рис. 7.65. Пункты меню для перемещения выделенных элементов диалога на другой слой 7.9.2.2.
ЗАКЛАДКИ ДИАЛОГА
Созданные слои будем отображать на двух закладках: Ввод премий и Интервалы расчетов. На первой закладке одновременно выводятся слои 1, 2 и 4 (Основной, ВидПремии и Кнопки), а на второй - слои 3 (ИнтервалыРасчетов) и 4. Причем при выводе слоя 4 на закладке 2 первые 3 кнопки невидимы. Описание закладок заносится в атрибут формы Закладки, который является объек том типа СписокЗначений. Это можно сделать, например, в предопределенной проце дуре модуля формы ПриОткрытии, разместив в ней следующий код: // Следующий метод обеспечивает доступ к атрибуту Закладками Форма.ИспользоватьЗакладки(1); Форма.Закладки.ДобавитьЗначение(1, "Ввод премий"); Форма.Закладки.ДобавитьЗначение(2, "Интервалы расчетов"); // Теперь можно задать отображаемые на закладке слои Форма.ИспользоватьСлой("Основной, ВидПремии, Кнопки"); Таким образом, с каждой закладкой можно связать один или более слоев. Информация о том, какие слои и какие их элементы должны отображаться при выборе закладки, распо лагается в предопределенной процедуре модуля формы ПриВыбореЗакладки. В нашем случае она может содержать следующий код: процедура ПриВыбореЗакладки(номЗакл, значЗакл) // номЗакл, значЗакл - соответственно номер и значение закладки. В нашем случае // значения этих параметров совпадают если номЗакл = 1 тогда Форма.ИспользоватьСлой("Основной, ВидПремии, Кнопки");
// заполнить, удалить и очистить - идентификаторы одноименных кнопок Форма.Заполнить.Видимость(1); Форма.Удалить.Видимость(1); Форма.Очистить.Видимость(1); иначе Форма.ИспользоватьСлой("ИнтервалыРасчетов, Кнопки"); Форма.Заполнить.Видимость(0); Форма.Удалить.Видимость(0); Форма.Очистить.Видимость(0); конецЕсли конецПроцедуры // ПриВыбореЗакладки В результате проделанной работы мы будем получать на введенных заклад: приведенные на рис. 7.66 и 7.67 изображения.
Рис. 7.66. Закладка Ввод премий
Рис. 7.67. Закладка Интервалы расчетов Замечание. Код подготовки закладок несколько упростится, если кнопки Запол нить, Удалить и Очистить разместить на основном слое.
7.9.2.3. ЭЛЕМЕНТЫ ДИАЛОГА И ИХ ФОРМУЛЫ Свод элементов диалога дадим в табл. 7.14. Таблица 7.14 Элементы диалога формы списка документа Премия и их формулы Элементы диалога
Формула/команда
Описание
Режим заполнения; Проследить() радикнопки Со трудник и Цех (имеет идентифи катор кто) ОтобразитьСПодр() сПодр
Управляет доступностью элемента диалога сПодр. Если кто = 1 (активна радиокнопка Сотрудник), то элемент сПодр недоступен, если кто = 2, то элемент доступен
флажки коэф, сум и п1234
Столбцы()
Задают вид вводимого ВР. Регулируют види мость столбцов табличной части следующим образом: если коэф = 1, то виден только столбец Коэф.; если сум = 2, то - столбец Сумма, если п1234 = 1, то - столбец 1234, если какая-либо переменная диалога равна нулю, то соответствующий столбец не виден
Заполнить
Заполнить()
Заполняет табличную часть для сотрудников выбранного подразделения, если кто = 2, или открывает форму подбора сотрудников, если кто = 1
Удалить
УдалитьЗап(1)
Удаляет текущую строку табличной части. Удалению предшествует запрос о необходимости удаления. Доступен, если в табличной части есть хотя бы одна запись
Очистить
УдалитьЗап(2)
Удаляет все строки табличной части. Удалению предшествует запрос о необходимости удаления. Доступен, если в табличной части есть хотя бы одна запись
Провести
#Провести
Выполняет проведение (перепроведение) документа Премия, то есть заносит в ЖЗ расчеты выбранными ВР-премиями
ОК
#3аписать Провести Закрыть
Выполняют проведение (перепроведение), запись и закрытие документа. Документ сохраняется в журнале документов Расчеты
Закрыть
#3акрыть
Закрывает документ
Отображает имя выбранного в списке сПодр под разделения и запоминает значение текущей строки списка текСтрока
Элементы диалога НомерДок, ДатаДок, № и Сотрудник сделаем недоступными. Доступность и видимость других элементов определяется состоянием диалога.
7.9.3. МОДУЛЬ ФОРМЫ СПИСКА ДОКУМЕНТА Содержит, кроме названных в табл. 7.14, процедуры ПриОткрытии, ПриЗаписи и ПремияДа. перем кЗПоУмолчанию;
//
Значение задается в процедуре ПриОткрытии
процедура Проследить() далее процедура Столбцы() далее процедура ПремияК10() далее процедура ПремияДа(пЗнач) далее процедура Заполнить() далее процедура ОбработкаПодбора(значен, конт) далее процедура УдалитьЗап(флаг) далее процедура КнопкиВидимость(флаг) далее // Формирует список подразделений сПодр и устанавливает начальные значения // переменных диалога. Причем, если документ проведен, то начальные // значения берутся из ЖЗ процедура ПриОткрытии() перем жз, сСотр_2, дост; // Значение коэффициента к3 по умолчанию; можно оформить как константу 1С кЗПоУмолчанию = 10; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Формируем, анализируя справочник Сотрудники_2, список подразделений сСотр_2.ПорядокКодов(); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 1 тогда сПодр.ДобавитьЗначение(сСотр_2.ТекущийЭлемент(), сСотр_2.Наименование); конецЕсли; конецЦикла; // пока если Проведен() = 0 тогда жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Начальные значения для периодов действия вводимых расчетов датаНачКоэф = жз.НачалоТекущегоПериода(); датаКонКоэф = датаНачКоэф +10; датаНачСум = датаКонКоэф + 1; датаКонСум = датаНачСум + 10; датаНач1234 = датаКонСум + 1; датаКон1234 = жз.КонецТекущегоПериода(); жз = 0; кто = 2; // Премия сотрудникам выбранного подразделения коэф = 1; // Можно начислить премию коэффициентом сум = 0; п1234 = 0; текСтрока = 1; // Позиционируемся на цехе 01 в списке сПодр иначе // Документ проведен если кто = 2 тогда // Правильно позиционируем список значений сПодр сПодр.ТекущаяСтрока(текСтрока); конецЕсли; конецЕсли;
// Регулируем доступность и видимость элементов диалога Проследить( ); Столбцы( ); // дост — 1, если в табличной части есть записи дост = ?(КоличествоСтрок() = 0, 0, 1); форма.Удалить.Доступность(дост); форма.Очистить.Доступность(дост); форма.ПанельИнструментов(0); // Отключаем панель инструментов форма.Заголовок("Премии"); // Следующий метод обеспечивает доступ к атрибуту Закладками Форма.ИспользоватьЗакладки(1); Форма.Закладки.ДобавитьЗначение(1, "Ввод премий"); Форма.Закладки.ДобавитьЗначение(2, "Интервалы расчетов"); // Теперь можно задать отображаемые на закладке слои Форма.ИспользоватьСлой("Основной, ВидПремии, Кнопки"); конецПроцедуры // ПриОткрытии // Распределяет слои диалога формы по закладкам процедура ПриВыбореЗакладки(номЗакл, значЗакл) // номЗакл, значЗакл - соответственно номер и значение закладки // В нашем случае значения этих параметров совпадают если номЗакл = 1 тогда Форма.ИспользоватьСлой("Основной, ВидПремии, Кнопки "); КнопкиВидимость(1); // Показываем все кнопки иначе Форма.ИспользоватьСлой("ИнтервалыРасчетов, Кнопки "); КнопкиВидимость(0); // Скрываем три верхние кнопки конецЕсли конецПроцедуры // ПриВыбореЗакладки // Управляет видимостью кнопок Заполнить, Удалить и Очистить (см. рис. 7.61) процедура КнопкиВидимость(флаг) // заполнить, удалить и очистить - идентификаторы одноименных кнопок Форма.Заполнить.Видимость(флаг); Форма.Удалить.Видимость(флаг); Форма.Очистить.Видимость(флаг); конецПроцедуры // КнопкиВидимость // Управляет доступностью элемента диалога сПодр процедура Проследить() если кто = 1 тогда // форма.СПодр.Доступность(0); иначе // форма.СПодр.Доступность(1); конецЕсли; КонецПроцедуры // Проследить
Премия отобранным сотрудникам кто = 2
//Управляет видимостью столбцов Коэф., Сумма и Премия 1234 табличной части документа // и доступностью элементов задания дат на слое ИнтервалыРасчетов
процедура Столбцы() форма.к3.Видимость(коэф); // Премия коэффициентом форма.ДатаНачКоэф.Доступность(коэф); форма.ДатаКонКоэф.Доступность(коэф); форма.Сумма.Видимость(сум); // Премия суммой форма. ДатаНачСум.Доступность(сум); форма.ДатаКонСум.Доступность(сум); форма.Премия1234.Видимость(п1234); // Премия 1234 фор'ма.ДатаНач1234.Доступность(п1234); форма.ДатаКон1234.Доступность(п1234); если коэф = 1 тогда // Премия коэффициентом // Заносим в пустые столбцы ПремияКоэф равный кЗПоУмолчанию коэффициент ПремияК10(); конецЕсли; если п1234 = 1 тогда // Премия 1234 // Заносим в пустые столбцы Премия 1234 значения Да перечисления ДаНет ПремияДа(Перечисление. ДаНет.ЗначениеПоНомеру(1)); конецЕсли; конецПроцедуры // Столбцы процедура ОтобразитьСПодр() текСтрока = сПодр.ТекущаяСтрока(); сПодр.ПолучитьЗначение(текСтрока); конецПроцедуры // ОтобразитьСПодр
//
Формула элемента диалога сПодр
// Заносит в пустые ячейки столбца задания коэффициента к3 // равный кЗПоУмолчанию коэффициент процедура ПремияК10() если ВыбратьСтроки() = 1 тогда пока ПолучитьСтроку() = 1 цикл к3 = ?(к3 = 0, кЗПоУмолчанию, к3); конецЦикла; // пока конецЕсли; конецПроцедуры // ПремияК10 // Заносит в пустые столбцы Премия 1234 значения Да перечисления ДаНет процедура ПремияДа(пЗнач) если ВыбратьСтроки() = 1 тогда пока ПолучитьСтроку() = 1 цикл если ПустоеЗначение(Премия1234) = 1 тогда Премия 1234 =пЗнач; конецЕсли; конецЦикла; // пока конецЕсли; конецПроцедуры // ПремияДа // Заполняет табличную часть документа Премия для одного (кто = 1) // или группы сотрудников процедура Заполнить() перем подр;
// Удаляем, если табличная часть заполняется для подразделения // или всего предприятия, ее строки, но только с согласия пользователя если (кто > 1) и (КоличествоСтрок( ) > 0) тогда если Вопрос("Удалить имеющиеся строки?", "Да+Нет") = "Да" тогда УдалитьСтроки(); // Очищаем табличную часть документа иначе возврат; конецЕсли; конецЕсли; если кто = 1 тогда // Указываем явно имя основной формы списка для метода ОткрытьПодбор // Задаем режим множественного выбора // Добавляем в табличную часть в результате каждого выбора в открытой форме // данные одного сотрудника. Собственно добавление осуществляет // предопределенная процедура модуля формы ОбработкаПодбора ОткрытьПодбор("Справочник.Сотрудники_2", "ФормаСписка",, 1); иначеЕсли кто = 2 тогда // Работаем с подразделением сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Заносим в табличную часть данные о сотрудниках выбранного подразделения подр подр = сПодр.ПолучитьЗначение(сПодр.ТекущаяСтрока()); сСотр_2.ИспользоватьРодителя(подр); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл если сСотр_2.ЭтоГруппа() = 0 тогда // Только сотрудники // Используем уже имеющуюся процедуру ОбработкаПодбора, // выполняющую обработку одного сотрудника ОбработкаПодбора(сСотр_2,""); конецЕсли; конецЦикла; // пока конецЕсли; если КоличествоСтрок() > 0 тогда форма.Удалить.Доступность(1); форма.Очистить.Доступность(1); конецЕсли; конецПроцедуры // Заполнить процедура ОбработкаПодбора(значен, конт) // Запрещаем, если добавляем по одному сотруднику, выбор того же лица дважды если (кто = 1) и (ВыбратьСтроки() = 1) тогда пока ПолучитьСтроку() = 1 цикл если Сотрудник = значен.ТекущийЭлемент() тогда Предупреждение("Сотрудник уже выбран."); возврат; конецЕсли; конецЦикла; // пока конецЕсли; НоваяСтрока(); // Новая строка в табличной части документа Сотрудник = значен.ТекущийЭлемент();
// Премия коэффициентом или 1234 если (коэф = 1) или (п1234 = 1) тогда к3 = кЗПоУмолчанию; // Коэффициент по умолчанию конецЕсли; если п1234 = 1 тогда // Премия 1234 или все премии // Заносим в столбец Премия 1234 значение Да перечисления ДаНет Премия 1234 = Перечисление.ДаНет.ЗначениеПоНомеру(1); конецЕсли; // Если есть хотя бы одна запись, то есть что удалять если (кто = 1) и (КоличествоСтрок() = 1) тогда форма.Удалить.Доступность(1); форма.Очистить.Доступность(1); конецЕсли; конецПроцедуры // ОбработкаПодбора // Удаляет с согласия пользователя, если флаг = 1, текущую запись табличной // части документа, и, если флаг = 2, все записи процедура УдалитьЗап(флаг) перем вопр; вопр = "Удалить " + ?(флаг = 1, "одну запись", "все записи") + "?"; если Вопрос(вопр, "Да+Нет") = "Да" тогда если флаг = 1 тогда УдалитьСтроку(); иначе // флаг = 2 УдалитьСтроки(); конецЕсли; если КоличествоСтрок() = 0 тогда форма.Удалить.Доступность(0); форма.Очистить.Доступность(0); конецЕсли; конецЕсли; конецПроцедуры // УдалитьЗап // Выполняет проверку дат процедура ПриЗаписи() перем ош; ош = 0; если (коэф = 1) и (датаНачКоэф > датаКонКоэф) тогда ош = 1; конецЕсли; если (сум = 1) и (датаНачСум > датаКонСум) тогда ош = 1; конецЕсли; если (п1234 = 1) и (датаНач1234 > датаКон1234) тогда о ш = 1; конецЕсли; если ош = 1 тогда Предупреждение("Неверный период действия расчета."); СтатусВозврата(0); // Запрещаем запись документа конецЕсли; конецПроцедуры // ПриЗаписи
7.9.4. МОДУЛЬ ДОКУМЕНТА Содержит предопределенную процедуру ОбработкаПроведения. Она для каждого сотрудника табличной части заносит в ЖЗ либо выбранный расчет, либо все расчеты. Причем расчеты заносятся с вычисленными результатами. Документ можно провести, лишь когда все заданные премии имеют ненулевое значение. В противном случае выдается сообщение; расчеты в ЖЗ не переносятся. функция ПроверитьНаНуль() далее функция НайтиЧасы(жз, нтп) далее функция НайтиХозОп(хозОп, ВР) далее процедура ВводРасчВЖЗ(жз, хозОп, ВР, датаНач, датаКон, рез) далее процедура ОбработкаПроведения() перем жз, нтп; перем ВР[3], рассч[3], всегоЧасов[3], рез[3], датаНач[3], датаКон[3]; перем а, б, ин; // Объект с разновидностью типа Справочник.ХозОпДляВР перем хозОп; датаНач[1] = датаНачКоэф; датаКон[1] = датаКонКоэф; датаНач[2] = датаНачСум; датаКон[2] = датаКонСум; датаНач[3] = датаНач1234; датаКон[3] =датаКон1234; если ПроверитьНаНуль() = 1 тогда Предупреждение("Есть расчеты с нулевой премией."); возврат; конецЕсли; // Для поиска хозяйственных операций ВР хозОп = СоздатьОбъект("Справочник.ХозОпДляВР"); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); // ВР - массив видов премий ВР[1] = ВидРасчета.ПремияКоэф_2; ВР[2] = ВидРасчета.ПремияСум_2; ВР[3] = ВидРасчета.Премия1234_2; для ин = 1 по 3 цикл // Инициализация вспомогательных массивов // Массив рассч используется для задания значения атрибута ЖЗ Рассчитана рассч[ин] = 0; всегоЧасов[ин] = 0; рез[ин] = 0; конецЦикла; // для ВыбратьСтроки(); // Открываем выборку строк документа пока ПолучитьСтроку() = 1 цикл // Устанавливаем реквизиты для каждого вводимого ВР а = 1; б = 2; // Параметры цикла Для, вводящего расчеты в ЖЗ если коэф = 1 тогда // Премия коэффициентом // Найдем по ЖЗ число часов, отработанных сотрудником всегоЧасов[1] = НайтиЧасы(жз, нтп); рез[1] = всегоЧасов[ 1 ] * к3; // Начисленная премия рассч[1] = 1; // Запись рассчитана конецЕсли;
если сум = 1 тогда // Премия суммой рез[2] = Сумма; // Начисленная премия рассч[2] = 1; // Запись рассчитана конецЕсли; // Для ввода премии 1234 (и 1234 = 1) все готово если п1234 = 1 тогда // Премия 1234 а =1; б = 3; // Параметры цикла Для, вводящего расчеты в ЖЗ конецЕсли; // Ввод расчетов для ин = а по б цикл если (ин < 3) и (рассч[ин] = 0) тогда продолжить; конецЕсли; жз.УстановитьРеквизит("Рассчитана", рассч[ин]); жз.УстановитьРеквизит("всегоЧасов", всегоЧасов[ин]); ВводРасчВЖЗ(жз, хозОп, ВР[ин], датаНач[ин], датаКон[ин], рез[ин]); конецЦикла // для конецЦикла; // пока // Вычисляем результаты введенных расчетов // Выборка записей ЖЗ по документу не дает верного результата // Поэтому используем выбор записей текущего периода по объекту // В данном документе нижеследующий расчет результата нужен только //для ВР Премия 1234. Остальные премии уже подсчитаны если п1234 = 1 тогда ВыбратьСтроки(); // Открываем выборку строк документа пока ПолучитьСтроку() = 1 цикл жз.ВыбратьПериодПоОбъекту(Сотрудник, нтп); пока жз.ПолучитьЗапись() = 1 цикл если (жз.Документ = ТекущийДокумент()) и (жз.ВидРасч = ВР[3]) тогда жз.Рассчитать(); // или жз.ВыполнитьРасчет конецЕсли; конецЦикла; // пока конецЦикла; // пока конецЕсли; если Проведен() = 0 тогда // Если Документ проводится впервые // Ограничиваем время показа окна с предупреждением тремя секундами Предупреждение("Документ проведен.", 3); иначе Предупреждение("Документ перепроведен.", 3); конецЕсли; конецПроцедуры // ОбработкаПроведения // Проверяет на наличие расчетов с нулевым результатом // Если таковые есть, то записи не проводятся функция ПроверитьНаНуль() ВыбратьСтроки( ); // Открываем выборку строк документа пока ПолучитьСтроку() = 1 цикл // Отрицательных значений к3 и Сумма быть не может - так заданы свойства // этих элементов диалога. Кроме того, для элемента Сумма задано // свойство Разделять триады
// Премия коэффициентом если (коэф = 1) и (к3 = 0) тогда возврат 1; конецЕсли; // Премия суммой если (сум = 1) и (Сумма = 0) тогда возврат 1; конецЕсли; //Премия 1234 если (п1234 = 1) и (Премия 1234 = Перечисление.ДаНет.ЗначениеПоНомеру(2)) тогда возврат 1; конецЕсли; конецЦикла; // пока возврат 0; конецФункции // ПоверитьНаНуль // Ищет для премии коэффициентом по ЖЗ число часов, отработанных сотрудником // Это число занесено в ЖЗ вместе с расчетом, имеющим ВР Оклад_2 функция НайтиЧасы(жз, нтп) жз.ВыбратьПериодПоОбъекту(Сотрудник, нтп); пока жз.ПолучитьЗапись() = 1 цикл если жз.ВидРасч = ВидРасчета.Оклад_2 тогда возврат жз.ВсегоЧасов; конецЕсли; конецЦикла; // пока возврат 0; // Расчет с ВР,Оклад_2 для сотрудника не введен конецФункции // НайтиЧасы // Вводит новые или редактирует имеющиеся в ЖЗ расчеты с заданным ВР процедура ВводРасчВЖЗ(жз, хозОп, ВР, датаНач, датаКон, рез) жз,УстановитьРеквизит("строкаДок", НомерСтроки); жз.УстановитьРеквизит("хозОп", НайтиХозОп(хозОп, ВР)); жз.ВвестиРасчет(Сотрудник, ВР, датаНач, датаКон, рез); конецПроцедуры // ВводРасчВЖЗ // Возвращает хозяйственную операцию ВР функция НайтиХозОп(хозОп, ВР) // Ищем простым перебором в справочнике ХозОпДляВР вид расчета ВР хозОп.ВыбратьЭлементы(); флаг = 0; пока хозОп.ПолучитьЭлемент() = 1 цикл если хозОп.ВР = ВР тогда флаг = 1; прервать; конецЕсли; конецЦикла; // пока если флаг = 1 тогда возврат хозОп.хозОп; иначе возврат ПолучитьПустоеЗначение(хозОп); конецЕсли; конецФункции // НайтиХозОп
,
7.9.5. ФОРМА СПИСКА ЖУРНАЛА ДОКУМЕНТОВ РАСЧЕТЫ Документы НачПериода_2 и Премия отображаются в журнале документов Расчеты (см. рис. 7.57). Форму списка этого журнала документов создадим такую же, как и форму списка журнала документов Табель (разд. 7.6.5). Модуль формы списка журнала документов Расчеты содержит предопределенную процедуру ПриОткрытии. Назначение процедуры такое же, как и у одноименной про цедуры модуля формы списка журнала кадровых приказов (разд. 5.8.3.2). // Список действий по документу. Передается процедуре глобального модуля // глДействия(ТекущийДокумент, сДейст) перем сДейст; // Формирует список действий и устанавливает интервал журнала, // отображающий все введенные документы вида НачПериода_2 и Премия процедура ПриОткрытии() перем дНач; // Дата начала интервала журнала Расчеты перем флаг, док; ОчиститьОкноСообщений(); // Определяем список действий для кнопки Действия сДейст.ДобавитьЗначение("Структура подчиненности"); сДейст.ДобавитьЗначение("Движения документа"); флаг = 1; // Равен единице, если удалось создать док попытка док = СоздатьОбъект("Документ.НачПериода_2"); исключение попытка док = СоздатьОбъект("Документ.Премия"); исключение флаг = 0; конецПопытки; конецПопытки; // Если документ НачПериода_2 или Премия создан если флаг = 1 тогда // Находим документ с наименьшей датой. По умолчанию документы располагаются // в выборке по возрастанию их дат док.ВыбратьДокументы(); если док.ПолучитьДокумент() = 1 тогда дНач = док.ДатаДок; иначе дНач = ТекущаяДата(); конецЕсли; иначе // Искомых документов нет дНач = ТекущаяДата(); конецЕсли; УстановитьИнтервал(дНач, ТекущаяДата( )); конецПроцедуры // ПриОткрытии // В основной программе модуля всего один оператор сДейст = СоздатьОбъект("СписокЗначений");
7.10. В И Д Ы Р А С Ч Е Т О В Д О К У М Е Н Т А П Р Е М И Я 7.10.1. ПРЕМИЯ КОЭФФИЦИЕНТОМ ВР ПремияКоэф_2 является самовытесняющимся. Включен в группу ВР ВсеНачисления_2, которая применяется при оценке налога и размера выплат в банк. Являет ся согласно правилам перерасчета ведущим по отношению к ВР Н Д Ф Л 2 , ВБанк_2 и Премия 1234_2 (разд. 7.7). То есть при вводе расчета с ВР ПремияКоэф_2 или ручном исправлении его результата атрибут Рассчитана нефиксированных расчетов с такими ВР и не имеющих вдобавок неотмененной ручной правки становится равен нулю. Что бы получить верные результаты, придется после ввода расчета с ВР ПремияКоэф_2 рассчитать заново либо зависимые расчеты, либо весь объект. Важно, однако, иметь в виду, что если сначала ввести ручную правку расчета с ВР ПремияКоэф_2, затем снять флаг ручной правки и рассчитать заново запись с новым результатом, то атрибут Рассчитана зависимых расчетов не изменится, а общий ре зультат расчета объекта окажется неверным (хотя система будет "думать", что объект рассчитан). Напомним, что рассчитанные записи сопровождаются в ЖЗ иконкой , а нерассчитанные - иконкой процедура ПровестиРасчет() // Процедура модуля ВР ПремияКоэф_2 перем жз; // Процедура выполняется при проведении расчета с ВР ПремияКоэф_2 // Имеет следующий алгоритм: // По значению реквизита ЖЗ сторокаДок в документе-родителе ищется запись, // породившая расчет, в этой записи читается значение реквизита к3, //зная которое находим искомую величину премии. Переменная всего Часов, // необходимая для расчета результата, является реквизитом ЖЗ // Если расчет вводился до ввода документа Табель, то всегоЧасов = 0 если всегоЧасов = 0 тогда // Найдем значение реквизита всегоЧасов жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьПериодПоОбъекту(Объект, жз.НачалоТекущегоПериода()); пока жз.ПолучитьЗапись() = 1 цикл если жз.ВидРасч = ВидРасчета.Оклад_2 тогда всегоЧасов = жз.ВсегоЧасов; конецЕсли; конецЦикла; // пока конецЕсли; Документ.ПолучитьСтрокуПоНомеру(строкаДок); результат = всегоЧасов * Документ.к3; конецПроцедуры // ПровестиРасчет
7.10.2. ПРЕМИЯ СУММОЙ ВР ПремияСум_2 является самовытесняющимся. Включен в группу ВР ВсеНачисления_2. Является ведущим по отношению к ВР НДФЛ_2, ВБанк_2 и Премия 12342 (разд. 7.7). Вытесняет ВР ПремияКоэф_2. процедура ПровестиРасчет( ) // Процедура модуля ВР ПремияСум_2 // Процедура выполняется при проведении расчета с ВР ПремияСум_2
// Имеет следующий алгоритм: // По значению реквизита ЖЗ сторокаДок в документе-родителе ищется запись, // породившая расчет, в этой записи читается значение реквизита Сумма, // которое и является искомой величиной премии Документ.ПолучитьСтрокуПоНомеру(строкаДок); результат = Документ.Сумма; конецПроцедуры // ПровестиРасчет
7.10.3. ПРЕМИЯ 1234 . ВР Премия1234_2 является самовытесняющимся. Включен в группу ВР ВсеНачисления_2. Является ведущим по отношению к ВР НДФЛ_2, ВБанк_2 и зависимым от ВР Оклад_2, ПремияКоэф_2, ПремияСум_2 (разд. 7.7). процедура ПровестиРасчет() // Процедура модуля ВР Премия перем жз, нтп, ктп, прем 1234; // Процедура выполняется при проведении расчета с ВР Премия1234_2 // Вычисляется по следующей формуле: // Премия 12342 = (Оклад_2 + ПремияКоэф_2 + ПремияСум_2) * к5, где // к5 - постоянный для всех сотрудников коэффициент // Коэффициент к5 - берется из списка периодических констант жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); нтп = жз.НачалоТекущегоПериода(); ктп = жз.КонецТекущегоПериода(); прем 1234 = 0; жз.ВыбратьПериодПоОбъекту(Объект, нтп); пока жз.ПолучитьЗапись() = 1 цикл если (жз.ВидРасч = ВидРасчета.Оклад_2) или (жз.ВидРасч = ВидРасчета.ПремияКоэф_2) или (жз.ВидРасч = ВидРасчета.ПремияСум_2) тогда прем 1234 = прем 1234 + жз.результат; конецЕсли; конецЦикла; // пока результат = прем1234 * Константа.К5.Получить(ктп); конецПроцедуры // ПровестиРасчет
1234_2
Значение числовой константы к5, которая должна быть периодической, после ее добавления в конфигурацию определим, запустив следующую процедуру: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба Константа.К5.Установить(ТекущаяДата(), 0.5); конецПроцедуры // Выполнить
7.11. ВЗАИМОДЕЙСТВИЕ ВИДА РАСЧЕТА ПРЕДПРИЯТИЯ Характер взаимодействия ВР предприятия представлен в табл. 7.15, где под взаи модействием понимаются такие свойства ВР, как способность вытеснять иные ВР, быть по отношению к ним зависимым или ведущим, а также очередность исполнения.
Таблица 7.15 Взаимодействие ВР предприятия Вид расчета
Ведущий по отношению к ВР
Зависит от ВР
Вытесняет ВР
Очеред ность
НачСальдо_2
ВБанк_2
-
-
1
Оклад_2
ВР НДФЛ_2, ВБанк_2
-
-
1
ПремияКоэф_2
Премия1234.2, НДФЛ_2, ВБанк_2
-
5
ПремияСум_2
НДФЛ_2, ВБанк_2
-
ПремияКоэф_2
1
Премия1234_2
//
Оклад_2, ПремияКоэф_2, ПремияСум_2
10
Оклад_2, ПремияКоэф_2, ПремияСум 2, Премия1234_2
15
НДФЛ_2
ВБанк_2
-
От всех ВР
-
20
Замечания: 1. Это важная таблица, о составлении такой или ей подобной надлежит позаботиться ли цам, сопровождающим и поддерживающим программы расчета заработной платы. 2. Все ВР табл. 7.15 являются самовытесняющимися. 3. Задание ВР ПремияСум_2 вытесняющим по отношению к ВР ПремияКоэф_2 обу словлено учебными целями. На примере взаимодействия этих двух ВР мы проде монстрируем работу алгоритма вытеснения 1С. Напомним также, что ВР Оклад_2, ПремияКоэф_2, ПремияСум_2 и Премия 12342 входят в группу ВР ВсеНачисления_2, а группа ВР ВсеУдержания_2 содержит пока что лишь один ВР - НДФЛ_2. Все приведенные в табл. 7.15 свойства уже введены в конфигурацию системы. Те перь посмотрим на практике, как они реализуются. Пока что можно сказать, что пол ностью полагаться на правила перерасчета нельзя (это уже отмечалось выше), по скольку они срабатывают при вводе ведущих ВР или при их ручной правке, но не ока зывают влияния на зависимые ВР при расчете записи с ведущим ВР в результате выбора пункта меню Рассчитать запись (разд. 7.10.1).
7.12. ДЛИННЫЕ РАСЧЕТЫ. ЭФФЕКТ ВЫТЕСНЕНИЯ РАСЧЕТА В этом разделе мы рассмотрим поведение длинных расчетов на примере ВР ПремияКоэф_2 и ПремияСум_2. Первый мы удлиним вперед, в будущий период (БП), а второй назад, в прошлый период (ПП). Учитывая, что ВР ПремияСум_2 вытесняет ВР ПремияКоэф_2, действие расчета с ВР ПремияСум_2 ограничим сверху серединой текущего периода (ТП) (рис. 7.68).
Рис. 7.68. ВР ПремияСум_2 и ПремияКоэф_2 на временной оси документа по начислению премии После оформления документа (рис. 7.69, 7.70) и его проведения в ЖЗ на фамилию Агальцова добавятся 4 записи о премиях (рис. 7.71).
Рис. 7.69. Премии Агальцова Ю. А. в документе Премия
Рис. 7.70. Интервалы действия премий Агальцова Ю. А
Рис. 7.71. Расчеты-премии Агальцова Ю. А Знакомясь с новыми записями, во-первых, отметим, что в ЖЗ нет длинного задан ного в документе расчета. Он разбился на обычные так, что период действия каждого лежит в пределах некоторого расчетного периода. Во-вторых, обратим внимание, что разбиение затрагивает только временной пара метр расчетов. Сумма премии переносится в каждый интервал разбиения. То есть в нашем случае 1С удваивает премии Агальцова Ю. А. по сравнению с величинами, заданными в документе. В-третьих, как и ожидалось, расчет Премия суммой вытеснил расчет Премия коэф фициентом, укоротив (на временной оси) последний на 15 дней слева. При этом вновь изменились только временные параметры расчета. Его результат сохранился. Единственный случай, когда наблюдается изменение результата, - это полное вытеснение рас чета.
В-четвертых, скорректируем наши представления об упорядочении расчетов: в пределах выбранного объекта расчеты располагаются по приоритетам (очередности исполнения), в пределах одного приоритета - в порядке возрастания атрибута расчета ДатаНачала, а при равных датах - в алфавитном порядке. Итак, на временной оси ЖЗ введенные расчеты располагаются в соответствии с рис. 7.72, а количественные характеристики каждого расчета удвоились.
Рис. 7.72. ВР ПремияСум_2 (ПС) и ПремияКоэф_2 (ПК) из приведенного на рис. 7.69 и 7.70 документа на временной оси ЖЗ Если рис. 7.72 закономерен, то двукратное увеличение результатов входит в про тиворечие с документом и реальными событиями. В самом деле, если ваш отпуск длится, скажем, 40 дней, то в документе-приказе об отпуске, в котором отпускные вводятся единой суммой, нужно указать именно эту сумму, а также даты начала и конца отпуска. После проведения документа мы получим 2 или 3 расчета (в зависимости от отпускного периода), но общая сумма отпускных должна сохра ниться. То есть, разбивая длинный расчет на к частей, мы должны иметь некоторый алгоритм деления его суммы на те же к частей. Если расчет А вытесняется расчетом Б, интервал действия которого лежит в пре делах интервала действия расчета А (см. рис. 7.4, б), наблюдается та же, что и в слу чае длинных ВР, картина: интервал действия расчета А разбивается на 2 части, но при этом результат расчета сохраняется для каждой из частей разбиения. Это приводит к удвоению результата по сравнению со значением, определенным в документе, поро дившим расчет. Этот эффект иллюстрирует рис. 7.73, в котором в качестве расчета А выступает Премия коэффициентом, а Премия суммой является расчетом Б.
Рис. 7.73. После разбиения результат расчета Премия коэффициентом удвоился В документе для расчетов Агальцова Ю. А. Премия суммой и Премия коэффици ентом заданы приведенные на рис. 7.74 интервалы действия.
Рис. 7.74. Интервал действия расчета Б (Премия суммой) лежит внутри интервала действия расчета А (Премия коэффициентом), причем расчет Б вытесняет расчет А Ясно, что наблюдаемый эффект потребует ручной правки результата.
7.13. МЕТОДЫ ЖУРНАЛА РАСЧЕТОВ И ЕГО ПЕРИОДА Приводятся в табл. 7.16. Таблица 7.16 Методы ЖР и его периода Описание
Метод Метод периода ЖР пер = пер.ПрибавитьПериод (кол);
Прибавляет к периоду пер, где пер = жр.ПериодДействия | жр.ПериодРегистрации | жр.ТекущийПериод() или иное значение типа РасчетныйПериод, заданное параметром кол число периодов
нтп = жр.НачалоТекущего Периода();
Возвращает дату начала текущего периода ЖР
ктп = жр.КонецТекущего Периода();
Возвращает дату конца текущего периода ЖР
нтп = жр.НачалоПериода ПоДате(дата);
Возвращает дату начала расчетного периода ЖР, которому принадлежит заданная дата
ктп = жр.КонецПериода ПоДате(дата);
Возвращает дату конца расчетного периода ЖР, которому принадлежит заданная дата
Методы ЖР
пер = жр.ПериодПоДате(дата); Возвращает расчетный период ЖР (объект типа РасчетныйПериод), которому принадлежит заданная дата флаг = жр.УстановитьТекущий Устанавливает в качестве текущего период пер, где пер Период(пер, [способ]); значение типа РасчетныйПериод. Если способ= 1, то при смене периода отрабатываются ее сопровождающие системные действия, то есть смена периода осуществляется так же, как и при ее интерактивном выполнении. Если способ = 0, то системные действия не отрабатываются, в частности не производится архивация документов при смене периода вперед и не изменяется значение атрибута записей Рассчитана при смене назад пер = жр.ТекущийПериод();
Возвращает текущий расчетный период - значение типа РасчетныйПериод
флаг = жр.ПолучитьЗапись();
Позиционирует выборку на ее следующей записи и возвра щает 1 или за пределами выборки и возвращает 0, если вы борка пуста или исчерпана
флаг = жр.ВыполнитьРасчет();
Рассчитывает текущую запись ЖР, обращаясь к процедуре ПровестиРасчет, которой снабжен ВР этой записи
on = жр.ОписательПериода (дата);
Возвращает строку, описывающую расчетный период, со держащий дату, заданную параметром дата. Формат строки зависит от продолжительности расчетного периода. Если продолжительность - месяц, то результат имеет, например, следующий вид: Январь 2002 г. Возвращает текущую запись ЖР - значение типа ЗаписьЖурнал аРасчетов
зап = жр.ТекущаяЗапись();
Метод флаг = жр.НайтиЗапись(зап);
Описание Осуществляет в ЖР поиск записи зап. Переменная зап должна иметь тип ЗаписьЖурналаРасчетов Устанавливает в атрибут Фиксирована текущей записи ЖР значение, равное единице
флаг = жр.Фиксировать Запись(); флаг = жр.ОсвободитьЗапись(); Устанавливает в атрибут Фиксирована текущей записи ЖР значение, равное нулю
флаг = жр.ВвестиПерерасчет(); Вводит в текущий период запись, которая является копией текущей записи одного из прошлых периодов, обнуляя ее результат. Введенная запись называется перерасчетом, и ее атрибут Перерасчет равен единице. Результат перерасчета уменьшается на значение атрибута Результат первичной записи. Перерасчет нельзя ввести, если первичная запись сама является перерасчетом флаг = жр.ВвестиПерерасчет НаОсновании(док);
Метод выполняет те же действия, что и метод ВвестиПерерасчет, с той лишь разницей, что перерасчет вводится на ос новании документа док
названиеЖР = жр.Вид();
Возвращает заданный в конфигурации идентификатор ЖР, например Зарплата_2. Результат имеет символьный тип
предстЖР = жр.Представление Возвращает заданный в конфигурации синоним ЖР, а при Вида(); отсутствии синонима - идентификатор ЖР. Результат имеет символьный тип жр.НазначитьТип(рекв, тип, длина, точность);
Назначает тип или разновидность типа, заданную парамет ром тип, реквизиту рекв неопределенного типа. Символьный параметр тип может принимать значение базового типа ("Число", "Строка", "Дата"), любой определенной в конфи гурации разновидности типа, например "Справочник.Дети", или "Документ.ИзменениеОклада", или значение вида суб конто
жр.УстановитьРеквизит (рекв, знач);
Устанавливает значение реквизита ЖР. Для сохранения зна чения после применения метода вызывается один из сле дующих методов: ВвестиРасчет, ВвестиРасчетНаОсновании, ЗаписатьРасчет или ЗаписатьРасчетНаОсновании. При этом если расчет вводится в документе, то в нем эти методы запи сывают дополнительные реквизиты ЖР, а также (по мере не обходимости) атрибуты Сторно, Рассчитана, Исправлена, Фиксирована, Перерасчет и Результат; значения атрибутов Документ, РодительскийДокумент, Объект, ВидРасч, ДатаНачала, ДатаОкончания и ПервичнаяЗапись в этом случае методом УстановитьРеквизит не задаются. Также метод УстановитьРеквизит употребляется с методами Новая и Записать. Причем метод Новая предшествует вызову метода УстановитьРеквизит. Методы могут применяться как в документах, так и в модулях иных объектов. При этом Ус тановитьРеквизит должен определить значения обязатель ных атрибутов (Документ, РодительскийДокумент, Объект, ВидРасч, ДатаНачала и ДатаОкончания). Другие атрибуты и дополнительные реквизиты ЖР определяются по мере необ ходимости. Атрибуты ЖР ПериодДействия и ПериодРегистрации пользователем не устанавливаются
Метод
Описание
*флаг = жр.ВвестиРасчет (объект, видРасч, датаНачала, датаОкончания, [результат]);
Вводит расчет в ЖР. При вводе длинных расчетов метод вводит в ЖР несколько расчетов. Также несколько записей может быть введено при вводе вытесняющих расчетов. Мо жет быть вызван в модуле документа или в модуле ВР. В первом случае атрибуты Документ и РодительскийДокумент принимают значение текущего документа (возвращается ме тодом ТекущийДокумент). Во втором значения этих атрибу тов вновь введенных расчетов становятся равными соответ ствующим значениям текущего расчета, то есть расчета, ре зультат которого определяется. Параметры метода определяют значения одноименных атрибутов вводимого расчета
*флаг = жр.ВвестиРасчетНа Основании(документ, (объект, видРасч, датаНачала, датаОкончания, [результат]);
Выполняет те же действия, что и метод ВвестиРасчет, за тем исключением, что атрибут Документ введенного расчета заполняется значением параметра документ, указывающим на документ-основание. Атрибут РодительскийДокумент принимает, как и в методе ВвестиРасчет, значение текущего документа
*флаг — жр.ЗаписатьРасчет (объект, ВР, датаНач, датаКон, [результат]);
Выполняет те же действия, что и метод ВвестиРасчет, за тем исключением, что расчет не вытесняет сам себя, даже если он является самовытесняющимся
*флаг = жр.ЗаписатьРасчетНа Выполняет те же действия, что и метод ВвестиРасчетНаОсОсновании(док, новании, за тем исключением, что расчет не вытесняет сам объект, ВР, датаНач, себя, даже если он является самовытесняющимся датаКон, [результат]); *жр.Рассчитать();
Вычисляется результат расчета. Результат возвращается пре допределенной процедурой ПровестиРасчет модуля соответ ствующего ВР. Если рассчитываемая запись фиксирована или скорректирована вручную и ее атрибут Исправлена = 1, результат расчета не изменяется. Расчет не выполнятся, если ЖР позиционирован на записи прошлого периода •
*флаг = жр.ВыбратьЗаписи ([датаНач], [датаКон]);
Открывает выборку записей ЖР, период действия которых пересекается с периодом, заданным параметрами типа Дата датаНач и датаКон. Если параметры опущены, то открыва ется выборка всех расчетов ЖР
*флаг = жр.ВыбратьПериод ([дата]);
Открывает выборку записей ЖР, период регистрации кото рых совпадает с периодом, в котором лежит параметр метода дата. Если параметр дата опущен, то открывается выборка расчетов, зарегистрированных в текущем периоде
*флаг = жр.ВыбратьЗаписиПо Объекту(объект, [датаНач], [датаКон]);
Открывает выборку записей ЖР, принадлежащих объекту, заданному параметром объект, и период действия которых пересекается с периодом, заданным параметрами типа Дата датаНач и датаКон. Если параметры датаНач и датаКон опущены, то выбираются все принадлежащие заданному объекту расчеты ЖР, период действия которых пересекается с текущим периодом
Метод *флаг = жр.ВыбратьЗаписиПо Документу(док); *флаг = жр.ВыбратьПериодПо Объекту(объект, [дата]);
Описание Открывает выборку записей ЖР, атрибут Документ которых равен значению параметра док, имеющему тип Документ Открывает выборку записей ЖР, принадлежащих объекту, заданному параметром объект, и период регистрации кото рых совпадает с периодом, в котором лежит параметр дата. Если параметр дата опущен, то открывается выборка расче тов, зарегистрированных в текущем периоде
*флаг = жр.ВыбратьПо Значению (графаОтбора, значОтбора, перНач, перКон);
Открывает выборку записей ЖР по отбору, заданному сим вольным параметром графаОтбора, со значением значОт бора. Период регистрации выбираемых расчетов принадле жит временному интервалу, заданному параметрами перНач и перКон, имеющими тип РасчетныйПериод
жр.Новая();
Создает новую запись ЖР. Добавление записи в ЖР осуществляется методом Записать
жр.3аписать();
Добавляет или изменяет текущую запись ЖР. Добавлению записи предшествует вызов метода Новая. Изменение значе ний реквизитов ЖР выполняется методом УстановитьРеквизит, вызываемым до метода Записать, но после метода Новая (если добавляется новая запись). При добавлении записи в обязательном порядке устанавливаются значения реквизитов Объект, Документ, РодительскийДокумент и ВидРасч. Сле дует помнить, что метод Записать не отрабатывает заданные правила перерасчета и вытеснения ВР
*флаг = жр.УдалитьЗапись();
Удаляет текущую запись ЖР (проставляет DBF-пометку удаления записи)
жр.Исправить(новРез);
Заменяет существующий результат текущей записи ЖР на новРез, устанавливая в атрибут Исправлена значение 1. Исправлен ная запись помечается иконкой СО. Результаты таких записей не изменяются при исполнении метода ВыполнитьРасчет или Рассчитать. Параметр новРез имеет числовой тип
жр.ОтменитьИсправление();
Устанавливает в атрибут Исправлена текущей записи ЖР значение 0 Методы модуля формы ЖР списокВидовТек - ВидыОтбора Устанавливает для интерактивного режима работы доступные виды отбора ЖР. Новые виды отбора задаются строкой список ([списокВидовНов]); ВидовНов, содержащей перечисление имен граф отбора, напри мер "Родитель, Образование". Возвращает строку с именами видов отбороэ, доступных до вызова метода. По умолчанию доступны все заданные при создании ЖР виды отбора флаг = УстановитьОтбор Устанавливает режим отображения записей ЖР по отбору. (графаОтбора, Заданному символьным параметром графаОтбора. В ЖР значениеОтбора); будут отображаться лишь те записи, объекты которых имеют реквизит, заданный параметром графаОтбора, например Образование, равный величине, заданной параметром значе ниеОтбора. Тип параметра значениеОтбора должен совпа дать с типом соответствующего реквизита объекта. Чтобы вновь вернуться в режим вывода всех записей ЖР, следует сделать следующий вызов: УстановитьОтбор("");
Метод
Описание
флаг = ПолучитьОтбор (графаОтбора, значениеОтбора);
Возвращает в символьную переменную графаОтбора теку щий вид отбора, а в переменную значениеОтбора - его зна чение. Вид отбора задается методом УстановитьОтбор. Если отбор не установлен (отображаются все записи), то перемен ная флаг получит значение 0
флаг = ЗакладкиОтбора (графаОтбора, [значениеОтбора]);
Устанавливает режим отображения закладок отбора, задан ного символьным параметром графаОтбора, открывая за кладку со значением значениеОтбора. Если второй параметр опущен, то активизируется первая закладка. Если первый па раметр равен пустой строке, то закладки отбора не отобра жаются
флаг = Установить Представление (режим, [объект]);
Устанавливает режим вывода записей ЖР. Если режим равен: • 1, выводятся все записи ЖР, параметр объект не исполь зуется; • 2, выводятся записи, имеющие атрибут Объект, равный значению параметра объект; • 3, выводятся записи, имеющие атрибут Документ, рав ный значению параметра объект флаг = ПолучитьПредставление Возвращает в параметры режим и объект текущие значения режима представления записей ЖР, заданного методом (режим, объект); УстановитьПредставление. Переменная флаг примет значение 1, если представление установлено, или 0 в противном случае флагТек = РассчитыватьПри Устанавливает, если флагНов = 1, режим автоматического ОтменеИсправления расчета записей при интерактивной отмене ручной правки. ([флагНов]); По умолчанию флагНов = 0 и автоматического расчета запи сей при интерактивной отмене ручной правки не произво дится. Возвращает значение флага расчета, действующее в системе на момент вызова метода Замечания: 1.
Префикс жр, употребленный с методами ЖР и его периодом, может быть произ вольным.
2.
Методы, отмеченные звездочкой (*), применяются только с переменными, опреде ляемыми функций СоздатьОбъект. Прочие методы, если они находятся в модуле формы ЖР и применяются для текущего журнала, употребляются без префикса.
3.
Появляющаяся в таблице переменная флаг равна единице, если метод выполнен удачно, и нулю - в противном случае.
Пример 1 для метода периода ЖР. Выводятся текущий, следующий и предшест вующие периоды журнала Зарплата_2. процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем жз, перТек, пер; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); пер = жз.ТекущийПериод(); Сообщить("Текущий период:" + Символ Табуляции + пер); пер = пер.ПрибавитьПериод(1); Сообщить("Период после:" + СимволТабуляции + пер); пер = пер.ПрибавитьПериод(-2); Сообщить("Период до:" + СимволТабуляции + пер); кОнецПроцедуры // Выполнить Результат: Текущий период: Период после: Период до:
Декабрь 2001 г. Январь 2002 г. Ноябрь 2001 г.
Пример 2 для метода периода ЖР. Выводятся текущий, следующий и предшест вующие периоды регистрации. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем жз, перТек, пер; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); ' жз.ВыбратьЗаписи(); // Позиционируемся на первом расчете ЖЗ пер = жз.ПериодРегистрации; // или жз.ПериодДействия; Сообщить("Период регистрации:" + СимволТабуляции + пер); пер = пер.ПрибавитьПериод(1); Сообщить("Период после:" + СимволТабуляции + пер); пер = пер.ПрибавитьПериод(-2); Сообщить("Период до:" + СимволТабуляции + пер); конецПроцедуры // Выполнить Результат тот же, что и в примере 1. Пример 3. Выводится вид и представление вида ЖЗ Зарплата_2. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем жз; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Идентификатор ЖЗ Сообщить(жз.Вид()); // Зарплата_2 // Синоним ЖЗ Сообщить(жз.ПредставлениеВида()); // Журнал заработной платы конецПроцедуры // Выполнить Пример 4. В окно сообщений выводятся все расчеты ЖЗ, относящиеся к выбран ному сотруднику. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, жз; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Метод Выбрать вызывает диалог для выбора элемента справочника
// Если сотрудник не выбран если сСотр_2.Выбрать("Выберите сотрудника", "ФормаСписка") = 0 тогда Предупреждение("Сотрудник не выбран."); возврат; конецЕсли; // Выбран Добрецов Б. Ю. жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьЗаписиПоОбъекту(сСотр_2.ТекущийЭлемент()); пока жз.ПолучитьЗапись() = 1 цикл Сообщить("" + жз.Объект + СимволТабуляции + жз.ВидРасч + Символ Табуляции + жз.Результат); конецЦикла; // пока конецПроцедуры // Выполнить Результат: Добрецов Борис Юрьевич Добрецов Борис Юрьевич Добрецов Борис Юрьевич Добрецов Борис Юрьевич Добрецов Борис Юрьевич Добрецов Борис Юрьевич Добрецов Борис Юрьевич
Начальное сальдо Премия коэффициентом Премия суммой Перечисление в банк НДФЛ Оклад Премия 1234
0.75 1670 2000 8444 1261.65 2800 3235
Пример 5. В окно сообщений выводятся все расчеты ЖЗ, относящиеся к выбран ному документу. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем док, жз; док = СоздатьОбъект("Документ.Премия"); // Метод Выбрать вызывает диалог для выбора документа // Если документ не выбран если док.Выбрать("Выберите документ") = 0 тогда Предупреждение("Документ не выбран."); возврат; конецЕсли; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьЗаписиПоДокументу(док.ТекущийДокумент()); пока жз.ПолучитьЗапись() = 1 цикл Сообщить("" + жз.Объект + СимволТабуляции + жз.ВидРасч + СимволТабуляции + жз.Результат); конецЦикла; // пока конецПроцедуры // Выполнить Результат: Волосков Михаил Андреевич Волосков Михаил Андреевич Волосков Михаил Андреевич
Премия суммой Премия коэффициентом Премия 1234
500 1600 2550
Пример 6. В окно сообщений выводятся все зарегистрированные в текущем пе риоде расчеты ЖЗ (объект, ВР и результат) сотрудников второго цеха. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, жз, пер, подр; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); если сСотр_2.НайтиПоНаименованию("02 Цех", 0) = 0 тогда Предупреждение("Второй цех не найден."); возврат; конецЕсли; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); пер = жз.ТекущийПериод(); // Подразделение предприятия (найденная выше группа справочника Сотрудники_2) подр = сСотр_2.ТекущийЭлемент(); жз.ВыбратьПоЗначению("Родитель", подр, пер, пер); пока жз.ПолучитьЗапись() = 1 цикл Сообщить("" + жз.Объект + СимволТабуляции + жз.ВидРасч + СимволТабуляции + жз.Результат); конецЦикла; // пока конецПроцедуры // Выполнить Результат: Абрамова Лариса Сергеевна Абрамова Лариса Сергеевна Абрамова Лариса Сергеевна Абрамова Лариса Сергеевна
Начальное сальдо Оклад НДФЛ Перечисление в банк
2.6 2500 325 2177
Пример 7. Удаляются все расчеты ЖЗ, относящиеся к выбранному документу, и сам документ. Записи получают DBF-пометку удаления, а документу проставляется 1С-пометка удаления. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем док, жз; док = СоздатьОбъект("Документ.Премия"); // Метод Выбрать вызывает диалог для выбора документа // Если документ не выбран если док.Выбрать("Выберите документ") = 0 тогда Предупреждение("Документ не выбран."); возврат; конецЕсли; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьЗаписиПоДокументу(док.ТекущийДокумент()); пока жз.ПолучитьЗапись() = 1 цикл жз.УдалитьЗапись(); // Удаляем расчет (ставим DBF-пометку удаления) конецЦикла; // пока док.Удалить(0); // Удаляем документ (ставим 1С-пометку удаления) конецПроцедуры // Выполнить
Пример 8. Обнуляется флаг ручной правки расчетов выбранного сотрудника, кро ме расчетов с ВР ПремияСум_2, для которых, наоборот, вносится ручная правка ре зультата. Новая величина премии равна 1250 руб. Попутно выполняется расчет запи сей, с которых снимется флаг ручной правки. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем сСотр_2, жз, ВР; сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); // Метод Выбрать вызывает диалог для выбора элемента справочника // Если сотрудник не выбран если сСотр_2.Выбрать("Выберите сотрудника", "ФормаДляВыбора") = 0 тогда Предупреждение("Сотрудник не выбран."); возврат; конецЕсли; // Выбран Агальцов Ю. А. жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьПериодП0Объекту(сСотр_2.ТекущийЭлемент()); ВР = ВидРасчета.ПремияСум_2; пока жз.ПолучитьЗапись() = 1 цикл если (жз.Исправлена = 1) и (жз.ВидРасч о ВР) тогда жз.ОтменитьИсправление(); // Снимаем флаг ручной правки результата жз.Рассчитать(); // Рассчитываем запись конецЕсли; если жз.ВидРасч = ВР тогда жз.Исправить( 1250.0); конецЕсли; конецЦикла; // пока Предупреждение("Готово."); конецПроцедуры // Выполнить Для иллюстрации методов модуля формы ЖР войдем в конфигурацию, раскроем ее пункт Журналы расчетов и в ЖЗ Зарплата_2 установим еще один вид (графу) отбо ра - Образование (рис. 7.75).
Рис. 7.75. В ЖЗ Зарплата_2 доступны отборы по графам Родитель и Образование Пример 9. В форме списка ЖЗ Зарплата_2 устанавливается отбор по графе Обра зование. Это выполняется в предопределенной процедуре ПриОткрытии модуля фор мы списка ЖЗ.
процедура ПриОткрытии() // Предопределенная процедура перем обр, флаг, сСотр_2, сОбр_2; // Задаем доступные виды отбора (этот вызов может быть опущен) ВидыОтбора("Родитель, Образование"); // Отображаем закладки отбора по реквизиту Образование ЗакладкиОтбора("Образование"); // Установим представление по одному объекту // Найдем для этого сотрудника, имеющего высшее образование сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сОбр_2 = СоздатьОбъект("Справочник.Образование_2"); // Выбираем вид образования обр флаг = сОбр_2.Выбрать("Выберите вид образования",""); если флаг = 1 тогда обр = сОбр_2.ТекущийЭлемент(); если сСотр_2.НайтиПоРеквизиту("Образование", обр, 1) = 1 тогда // Отображаем записи по одному объекту УстановитьПредставление(2, сСотр_2.ТекущийЭлемент()); конецЕсли; конецЕсли; сСотр_2 = 0; сОбр_2 = 0; // Для иных процедур модуля определим нтп нтп = НачалоТекущегоПериода(); конецПроцедуры Результат после открытия формы ЖЗ см. на рис. 7.76.
Рис. 7.76. Установлены отбор по графе Образование и режим представления по одному объекту Замечание. Чтобы полноценно использовать отбор по графе Образование, нужно внести соответствующие изменения и в диалог формы списка ЖЗ Зарплата 2, и в модуль формы. В частности, флажок на рис. 7.77 переключатель.
придется заменить на приведенный
Рис. 7.77. Переключатель видов отборов
Пример 10. В форме списка ЖЗ Зарплата_2 выводятся только сотрудники с задан ным образованием. процедура ПриОткрытии() // Предопределенная процедура перем обр, флаг, сОбр_2, графаОтбора, значениеОтбора; // Выбираем вид образования обр сОбр_2 = СоздатьОбъект("Справочник.Образование_2"); флаг = сОбр_2.Выбрать("Выберите вид образования",""); если флаг = 1 тогда обр = сОбр_2.ТекущийЭлемент(); // Задаем режим вывода записей о сотрудниках с образованием обр УстановитьОтбор("Образование", обр); конецЕсли; // Контрольный вывод если ПолучитьОтбор(графаОтбора, значениеОтбора) = 1 тогда Сообщить("Установлен отбор по графе " + графаОтбора + " со значением " + значениеОтбора); конецЕсли; УстановитьПредставление(1); // Вывод списка сотрудников // Для иных процедур модуля определим нтп нтп = НачалоТекущегоПериода(); конецПроцедуры // ПриОткрытии Результат: Установлен отбор по графе Образование со значением Среднее Пример 11.В форме списка ЖЗ Зарплата_2 выводятся только записи, относящиеся к документу, выбранному в предопределенной процедуре ПриОткрытии. процедура ПриОткрытии() // Предопределенная процедура перем док; док = СоздатьОбъект("Документ.Премия"); // Метод Выбрать вызывает диалог для выбора документа // Если документ не выбран если док.Выбрать("Выберите документ") = 0 тогда Предупреждение("Документ не выбран."); возврат; конецЕсли; // Вывод расчетов, порожденных выбранным ранее документом УстановитьПредставление(3, док.ТекущийДокумент()); // Для иных процедур модуля определим нтп нтп = НачалоТекущегоПериода(); конецПроцедуры // ПриОткрытии
7.14. П Р Е Д О П Р Е Д Е Л Е Н Н Ы Е П Р О Ц Е Д У Р Ы МОДУЛЯ ФОРМЫ ЖУРНАЛА РАСЧЕТОВ Приведены в табл. 7.17.
Таблица 7.17 Предопределенные процедуры модуля формы ЖР
Пример. Запрещается ручное исправление результатов во всем ЖР. // Процедура входит в состав модуля формы списка ЖР процедура ПриИсправленииРезультата(запись) Предупреждение("Ручная правка невозможна."); СтатусВозврата(0); конецПроцедуры
7.15. ВИДЫ РАСЧЕТОВ И ИХ ГРУППЫ 7.15.1. ВЫВОД СПИСКОВ ВИДОВ РАСЧЕТОВ И ИХ ГРУПП С ВР и группами ВР мы уже имели возможность ознакомиться весьма плотно. Те- | перь наша задача - привести достаточные для активного употребления этих объектов справочные сведения. Приведем, однако, прежде коды, выводящие список ВР и групп ВР конфигурации. // Процедура вывода списка ВР конфигурации // Выводит для каждого ВР его атрибуты: код (идентификатор), // наименование (комментарий), очередность и приоритет, а также его синоним процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем всегоВР; // Число ВР в конфигурации перем ин, код, ВР, наим, очер, приорВыт, синоним; ОчиститьОкноСообщений(); // Очищаем окно сообщений всегоВР = Метаданные.ВидРасчета(); для ин = 1 по всегоВР цикл код = Метаданные.ВидРасчета(ин).Идентификатор; // ВР имеет тип ВидРасчета ВР = ВидРасчета.ПолучитьАтрибут(код); наим = ВР.Наименование; // или наим = Метаданные.ВидРасчета(ин).Комментарий; очер = ВР.Очередность;
приорВыт = ВР.ПриоритетВытеснения; синоним = Метаданные.ВидРасчета(ин).Синоним; Сообщить(код + " - " + наим + " - " + очер + " - " + приорВыт + " - " + синоним); конецЦикла; // для конецПроцедуры // Выполнить Фрагмент результата: Авторские - Другие авторские вознаграждения - 30 - 0 - Др. авторские возн-ия АвторскиеЗаНП - Авторские за создание произведений науки - 30 - 0 - Авт. за науку БанковскиеИздержки - Банковские издержки - 152 - 0 - Банковские издержки // Процедура вывода списка групп ВР конфигурации // Выводит для каждой группы ВР ее атрибуты: код (идентификатор) // и наименование (комментарий), а также синоним процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем всегоГруппВР; // Число групп ВР в конфигурации перем ин, код, наим, синоним; ОчиститьОкноСообщений(); // Очищаем окно сообщений всегоГруппВР = Метаданные.ГруппаРасчетов(); для ин = 1 по всегоГруппВР цикл код = Метаданные.ГруппаРасчетов(ин).Идентификатор; наим = Метаданные.ГруппаРасчетов(ин).Комментарий; синоним = Метаданные.ГруппаРасчетов(ин).Синоним; Сообщить(код + "-." + наим + " - " + синоним); конецЦикла; // для конецПроцедуры // Выполнить Фрагмент результата: ВсеНачисления - Группа, в которую входят начисления - Все начисления ВсеУдержания - Группа, в которую входят все удержания - Все удержания ВсеНачисления_2 - Для сотрудников из справочника Сотрудники_2 - Все начисления
7.15.2. АТРИБУТЫ ВИДОВ РАСЧЕТОВ И ИХ ГРУПП Приводятся в табл. 7.18. Таблица 7.18 Атрибуты ВР и их групп Атрибут
Описание Атрибуты ВР и групп ВР Код Строка, содержащая идентификатор ВР или группы ВР Наименование Строка, содержащая комментарий ВР или группы ВР Атрибуты ВР Число, задающее очередность (приоритет) выполнения ВР (см. разд. 7.3.4) Очередность Число, задающее приоритет вытеснения ВР. Не рекомендован для Приоритет Вытеснения употребления и сохранен для поддержания совместимости с прежними версиями 1С
7.15.3. МЕТОДЫ ВИДОВ РАСЧЕТОВ И ИХ ГРУПП Приводятся в табл. 7.19. Таблица 7.19
Методы ВР и групп ВР Описание
Метод Методы ВР ВР = ВидРасчета. ПолучитьАтрибут (код); флаг = <ВР>Входит ВГругппу(группа);
Возвращает объект типа ВидРасчета, идентификатор которого равен символьному параметру код
флаг = <ВР.> Выбран();
Возвращает 1, если ВР - объект типа ВидРасчета - имеет значение, или О-в противном случае. Употребляется, в частности, для реквизитов типа ВидРасчета объектов 1С, например справочников или документов, с целью узнать, задан ли для реквизита ВР или реквизит имеет пустое значение
флаг = ВР1.Вытесняет ВидРасчета(ВР2);
Возвращает 1, если вид расчета ВР1 вытесняет вид расчета ВР2, или О-в противном случае
флаг = ВР1.Вытесняется ВидомРасчета (ВР2);
Возвращает 1, если вид расчета ВР1 вытесняется видом расчета ВР2, или О-в противном случае
Возвращает 1, если вид расчета ВР входит в группу ВР, задан ную параметром группа типа ГруппаРасчетов
Методы групп ВР флаг = ГВР.Содержит ВидРасчета(ВР); кол = ГВР.Количество(); ВР = ГВР.Получить Расчет(ном);
Вернет 1, если группа видов расчетов ГВР содержит вид расчета ВР, или О-в противном случае Возвращает число ВР, входящих в группу расчетов ГВР Вернет объект типа ВидРасчета, входящий в группу расчетов ГВР под номером ном. Если номер ном больше числа расче тов в группе, то возникнет завершающая ошибка, сопровож даемая сообщением "Выход за границы группы расчетов"
Примеры для методов ВР: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ВР1,ВР2; перем ин, код, наим, синоним; ОчиститьОкноСообщений( ); // Очищаем окно сообщений ВР1 = ВидРасчета.ПремияСум_2; ВР2 = ВидРасчета.ПремияКоэф_2; если ВР1.ВытесняетВидРасчета(ВР2) = 1 тогда Сообщить("ВР Премия суммой вытесняет ВР Премия коэффициентом."); конецЕсли;
// Определим переменные ВР типа ВидРасчета иным способом ВР1 = ВидРасчета.ПолучитьАтрибут("ПремияСум_2"); ВР2 = ВидРасчета.ПолучитьАтрибут("ПремияКоэф_2"); если ВР1.ВытесняетсяВидомРасчета(ВР2) = 1 тогда Сообщить("ВР Премия суммой вытесняется ВР Премия коэффициентом."); иначе Сообщить("ВР Премия коэффициентом вытесняется ВР Премия суммой."); конецЕсли; конецПроцедуры // Выполнить Результат: ВР Премия суммой вытесняет ВР Премия коэффициентом. ВР Премия коэффициентом вытесняется ВР Премия суммой. Примеры для методов групп ВР: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ВР, ГВР, кол, ном; ОчиститьОкноСообщений(); // Очищаем окно сообщений ВР = ВидРасчета.ПремияСум_2; ГВР = ГруппаРасчетов.ВсеНачисления_2; если ГВР.СодержитВидРасчета(ВР) = 1 тогда Сообщить('Труппа ВР " + ГВР.Код + " содержит ВР " + ВР.Код); иначе Сообщить("Группа ВР " + ГВР.Код + " не содержит ВР " + ВР.Код); конецЕсли; кол = ГВР.Количество(); Сообщить("В группу ВР " + ГВР.Код + " входит" + кол + " ВР"); ном = 2; ВР = ГВР.ПолучитьРасчет(ном); Сообщить("В группе ВР " + ГВР.Код + " под номером " + ном + " расположен расчет " + ВР.Код); конецПроцедуры // Выполнить Результат: Группа ВР ВсеНачисления_2 содержит ВР ПремияСум_2 В группу ВР ВсеНачисления_2 входит 4 ВР В группе ВР ВсеНачисления_2 под номером 2 расположен расчет ПремияКоэф_2
7.16. АТРИБУТЫ И МЕТОДЫ ПРАВИЛ ПЕРЕРАСЧЕТА 7.16.1. АТРИБУТЫ ПРАВИЛ ПЕРЕРАСЧЕТА Правило перерасчета, как отмечено в разд. 7.7, при вводе ведущего расчета обну ляет значение атрибута Рассчитана у зависимых расчетов. Каждое правило имеет 4 атрибута: Код, Наименование, Тип и КоличествоПериодов. Их описание дано в табл. 7.20.
Таблица 7.20 Атрибуты правил перерасчета Описание
Атрибут Код
Строка, содержащая идентификатор правила перерасчета (разд. 7.7)
Наименование
Строка, содержащая комментарий правила перерасчета
Тип
Зависимые расчеты должны быть перерассчитаны: • если Тип = 0 - в текущем расчетном периоде ЖР; • если Тип = 1 - в том же расчетном периоде, к которому принадле жит вводимый ведущий расчет; • если Тип = 2 - в нескольких периодах, следующих за периодом, к которому принадлежит вводимый ведущий расчет (разд. 7.7). Число периодов, Охватываемых правилом, заносится в атрибут Коли чествоПериодов правила перерасчета
КоличествоПериодов Число периодов, охватываемых правилом перерасчета. Может быть больше единицы, только когда Тип = 2 Пример. Выводятся атрибуты правила перерасчета КвартальнаяПремия. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ПП; ОчиститьОкноСообщений(); // Очищаем окно сообщений ПП = ПравилоПерерасчета.КвартальнаяПремия; Сообщить("Код (идентификатор): " + ПП.Код); Сообщить("Наименование (комментарий):" + ПП.Наименование); Сообщить("Тип = " + ПравилоПерерасчета.КвартальнаяПремия.Тип); Сообщить("КоЛичествоПериодов = " + ПП.КоличествоПериодов); конецПроцедуры // Выполнить Результат: Код (идентификатор): КвартальнаяПремия Наименование (комментарий): Для квартальной премии Тип = 2 КоличествоПериодов = 3
7.16.2. МЕТОДЫ ПРАВИЛ ПЕРЕРАСЧЕТА Находятся в табл. 7.21. Таблица 7.21 Методы правил перерасчета Метод
Описание
колВед = ПП.КоличествоВедущих();
Возвращает количество ведущих ВР в правиле перерасчета ПП
флаг = ПП.ИмеетВедущий(ВР);
Вернет 1, если вид расчета ВР входит в правило перерасчета ПП как ведущий
ВР = ПП.ПолучитьВедущий(ном);
Возвращает вид расчета ВР, входящий в правило перерасчета ПП как ведущий под номером ном Добавляет в правило перерасчета ПП вид расчета ВР в качестве ведущего
ПП.ДобавитьКакВедущий(ВР);
Метод ПП.УдалитьВсеВедущие();
Описание Удаляет из правила перерасчета ПП все ведущие ВР
колПод = ПП.КоличествоПодчиненных(); Возвращает количество подчиненных (зависимых) ВР в правиле перерасчета ПП флаг = ПП.ИмеетПодчиненный();
Вернет 1, если вид расчета ВР входит в правило перерасчета ПП как подчиненный
ВР = ПП.ПолучитьПодчиненный(ном);
Возвращает вид расчета ВР, входящий в правило перерасчета ПП как подчиненный под номером ном
ПП.ДобавитьКакПодчиненный();
Добавляет в правило перерасчета ПП вид расчета ВР в качестве подчиненного
ПП.УдалитьВсеПодчиненные();
Удаляет из правила перерасчета ПП все зависи мые ВР
флагТек = При вводе ведущих ВР правила перерасчета при ПравилоПерерасчета.Применять ' меняются, если флагНов = 1, и не применяются, ([флагНов]); если флагНов = 0. По умолчанию флагНов = 1. Возвращает текущее, до вызова метода значение флага применения правил перерасчета Замечание. Префикс ПП, употребленный с методами правил перерасчета, может быть произвольным. Пример 1: процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ПП, колВед, колПод, ин; ОчиститьОкноСообщений(); // Очищаем окно сообщений ПП = ПравилоПерерасчета.ВБанк_2; колВед = ПП.КоличествоВедущих(); колПод = ПП.КоличествоПодчиненных(); Сообщить("Правило перерасчета " + ПП.Код); Сообщить("Число ведущих ВР:" + колВед); Сообщить("Число зависимых ВР: " + колПод); Сообщить("Список ведущих ВР:"); для ин = 1 по колВед цикл Сообщить(ПП.ПолучитьВедущий(ин).Код); конецЦикла; // для Сообщить("Список зависимых ВР:"); для ин = 1 по колПод цикл Сообщить(ПП.ПолучитьПодчиненный(ин).Код); конецЦикла; // для конецПроцедуры // Выполнить Результат: Правило перерасчета ВБанк_2 Число ведущих ВР: 6 Число зависимых ВР: 1 Список ведущих ВР:
НачСальдо_2 Оклад_2 ПремияКоэф_2 ПремияСум_2 Премия1234_2 НДФЛ_2 Список зависимых ВР: ВБанк_2 Пример 2. В правиле перерасчета ВБанк_2 первоначально удаляются все ведущие и подчиненные ВР, а затем оно формируется заново. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем ПП, ВРВед[6], ВРПод, ин; ОчиститьОкноСообщений(); // Очищаем окно сообщений ПП = ПравилоПерерасчета.ВБанк_2; ВРВед[1] = ВидРасчета.НачСальдо_2; ВРВед[2] = ВидРасчета.Оклад_2; ВРВед[3] = ВидРасчета.ПремияКоэф_2; ВРВед[4] = ВидРасчета.ПремияСум_2; ВРВед[5] = ВидРасчета.Премия1234_2; ВРВед[6] = ВидРасчета.НДФЛ_2; ВРПод = ВидРасчета.ВБанк_2; ПП.УдалитьВсеВедущие(); // Удаляем ведущие ВР ПП.УдалитьВсеПодчиненные(); // Удаляем подчиненные ВР // Контроль Сообщить("Число ведущих ВР: " + ПП.КоличествоВедущих()); Сообщить("Число подчиненных ВР: " + ПП.КоличествоПод4иненных()); для ин = 1 по 6 цикл ПП.ДобавитьКакВедущий(ВРВед[ин]); конецЦикла; // для ПП.ДобавитьКакПодчиненный(ВРПод); // Контроль Сообщить("Число ведущих ВР: " + ПП.КоличествоВедущих()); Сообщить("Число подчиненных ВР: " + ПП.КоличествоПодчиненных()); конецПроцедуры // Выполнить
7.17. ЗАВЕРШАЕМ КОДИРОВАНИЕ ПРОЦЕДУР МОДУЛЯ ФОРМЫ СПИСКА ЖУРНАЛА ЗАРПЛАТА_2 После ввода расчетов интерфейс ЖЗ предоставляет возможность рассчитать зар плату сотрудников, сформировать их расчетные листки и ведомость о перечислениях в банк. Для этих целей с кнопками диалога (рис. 7.21) Расчет зарплаты, связа ны соответственно процедуры РасчетЗП, ПечатьРЛ и ВедомостьБанк. Это простые процедуры, но для полноты изложения мы напишем и приведем их код. Допол нительно рассмотрим запрос, возвращающий распределение результатов расчетов ЖЗ по хозяйственным операциям.
7.17.1. РАСЧЕТ ЗАРПЛАТЫ Процедура РасчетЗП для выбранного сотрудника или сотрудников заданного под разделения выполняет расчет его начислений и удержаний, включая перечисление п банк. То есть в первом случае она повторяет действия процедуры, запускаемой или соответствующим ей пунктом меню, во втором - эти действия повто иконкой ряются для каждого сотрудника выоранного подразделения. 1аким ооразом, наша за дача - написать процедуру расчета объекта, а затем вызвать ее необходимое число раз. Поскольку расчет с ВР НачСальдо_2 фиксирован, то в создаваемой процедуре за писи с таким ВР не будут рассчитываться вовсе. Также не рассчитываются записи с неотмененной ручной правкой. Кроме того, заложим в процедуру код, проверяющий присутствие у объекта обязательного расчета с ВР Н Д Ф Л 2 . В соответствии с доку ментом Табель наличие одного обязательного расчета, например НДФЛ_2, говорит о том, что в ЖЗ есть и все иные обязательные расчеты объекта, то есть расчеты с ВР Оклад_2 и ВБанк_2. После всего сказанного текст процедур расчета зарплаты может быть таким: // Процедура РасчетОбъекта рассчитывает все записи объекта ЖЗ, // кроме расчета с ВР НачСальдо_2 // Поскольку процедура присутствует в модуле формы списка ЖЗ, // то все методы ЖР вызываются без префикса процедура РасчетОбъекта(сотр) перем флаг, сотр2; // Флаг будет равен единице, если у сотрудника есть расчет с ВР Оклад_2 флаг = 0; сотр2 = сотр; // Запоминаем для вывода сообщения // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл если видРасч = ВидРасчета.НДФЛ_2 тогда флаг = 1; конецЕсли; // Фиксированные и исправленные записи рассчитывать нет смысла если Фиксирована + Исправлена = 0 тогда Рассчитать(); конецЕсли; конецЦикла; // пока если флаг = 0 тогда Сообщить("Оформите табель сотруднику " + сотр2.Наименование); конецЕсли; конецПроцедуры // РасчетОбъекта процедура СоздатьСЗнач(сЗначСотр) далее процедура РасчетЗП( ) перем сотр, сЗначСотр, ин; ОчиститьОкноСообщений(); если кто = 1 тогда // Расчет зарплаты выбранного сотрудника Состояние("Расчет зарплаты сотрудника " + Объект.Наименование); РасчетОбъекта(Объект);
// Расчет заплаты сотрудников выбранного подразделения // Используя метод ЖР ВыбратьПоЗначению, занесем (без повторов) значение // атрибута Объект расчетов выбранного подразделения в список сЗначСотр иначе Состояние("Формируем список сотрудников подразделения " + Объект.Родитель); СоздатьСЗнач(сЗначСотр); // Формируем список сотрудников подразделения для ин = 1 по сЗначСотр.РазмерСписка() цикл сотр = сЗначСотр.ПолучитьЗначение(ин); Состояние("Расчет зарплаты сотрудника " + сотр.Наименование); РасчетОбъекта(сотр); конецЦикла; // для // Поправляем закладку отбора УстановитьОтбор("Родитель", сотр.Родитель); конецЕсли; конецПроцедуры // РасчетЗП процедура СоздатьСЗнач(сЗначСотр) // Формируем список сотрудников подразделения перем сотр, пер; сЗначСотр = СоздатьОбъект("СписокЗначений"); пер = ТекущийПериод(); ВыбратьПоЗначению("Родитель", Объект.Родитель, пер, пер); сотр = Объект; // Первый объект выборки пока ПолучитьЗапись() = 1 цикл // Значения в список сЗначСотр заносятся без повторов если сотр = Объект тогда продолжить; иначе сЗначСотр.ДобавитьЗначение(сотр); сотр = Объект; конецЕсли; конецЦикла; // пока // Добавляем ссылку на последнего сотрудника выбранного подразделения сЗначСотр.ДобавитьЗначение(сотр); конецПроцедуры // СоздатьСЗнач Замечания: 1.
Созданный код работает существенно быстрее, чем аналогичный встроенный в 1С.
2.
Процедуры РасчетОбъекта и РасчетЗП добавляются в модуль формы списка ЖЗ Зарплата_2.
3.
Встроенная процедура Состояние выводит в строку состояний, расположенную в нижней левой части экрана, занесенное в символьный параметр процедуры со общение.
4.
Сотрудников выбранного подразделения можно было бы извлечь из справочника Сотрудники_2 и затем передать их процедуре РасчетОбъекта, употребив, напри мер, следующий код:
процедура РасчетЗЩ ) перем пер, сотр, сСотр_2; ОчиститьОкноСообщений(); если кто = 1 тогда // Расчет зарплаты выбранного сотрудника Состояние("Расчет зарплаты сотрудника " + Объект.Наименование); РасчетОбъекта(Объект); иначе сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ИспользоватьРодителя(Объект.Родитель); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл сотр = сСотр_2.ТекущийЭлемент(); Состояние("Расчет зарплаты сотрудника " + сотр); РасчетОбъекта(сотр); конецЦикла; // пока // Поправляем закладку отбора УстановитьОтбор("Родитель", сотр.Родитель); конецЕсли; конецПроцедуры // РасчетЗП Критерий выбора кода в данном случае - его быстродействие. Вопрос замера вре мени вычислений обсуждается в разд. 2.8.3. 5.
На период отладки вам может понадобиться приводимый ниже код, отменяющий ручную правку и обнуляющий результат расчета с ВР ВБанк_2 (перечисление в банк): // Отменяет ручную правку и заносит 0 в результат расчета с ВР ВБанк_2 процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем жз, перТек, пер; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьПериод(жз.НачалоТекущегоПериода()); пока жз.ПолучитьЗапись() = 1 цикл если жз.Исправлена = 1 тогда жз.ОтменитьИсправление(); // Обнуляем флаг ручной правки конецЕсли; если жз.ВидРасч = ВидРасчета.ВБанк_2 тогда жз.Результат = 0; конецЕсли; конецЦикла; // пока конецПроцедуры // Выполнить
7.17.2. ФОРМИРОВАНИЕ РАСЧЕТНОГО ЛИСТКА Так же как и при расчете зарплаты, мы создадим процедуру ЛистокСотрудника, формирующую расчетный листок (РЛ) для одного сотрудника, и будем использовать ее нужное число раз, когда задача решается для выбранного подразделения. Вывод РЛ будем осуществлять в специально созданную в форме списка ЖЗ табли цу, которой дадим имя РЛ (рис. 7.79).
Рис. 7.79. Образец для расчетного листка Из образца видно, что для вывода нам понадобятся переменные окл, мес, ВР, рез, часы, начУд, нач, уд, вып, вБанк, сальдоНач и сальдоКон. Их смысл разъясняет рис. 7.79. 1. 2. 3. 4. 5.
Алгоритм получения РЛ следующий: Вывести заголовок РЛ и заголовок таблицы начислений. Сформировать списки тЗначНач и тЗначУд с начислениями и удержаниями сотрудника, попутно вычисляя значения переменных нач, уд, вып, вБанк, сальдоНач и сальдоКон. Вывести заголовок таблицы начислений, а затем, используя таблицу тЗначНач, и сами на числения. Вывести заголовок таблицы удержаний, а затем, используя таблицу тЗначУд, и сами удер жания. Вывести итоговую часть РЛ.
Этот алгоритм реализует процедура ЛистокСотрудника, вызываемая из процедуры ПечатьРЛ. После программ на рис. 7.80 приведен пример сформированного РЛ. процедура СоздатьТЗнач(тЗнач) далее // Вспомогательные процедуры формирования РЛ процедура НовСтрокаВТЗнач(тЗнач, мес) далее процедура ВыводНачУд(табл, тЗнач, начУд) далее // Процедура ЛистокСотрудника формирует РЛ одного сотрудника // Поскольку процедура присутствует в модуле формы списка ЖЗ, // то все методы ЖР вызываются без префикса процедура ЛистокСотрудника(Объект, табл, тЗначНач, тЗначУд) перем нач, уд, вып, вБанк, сальдоНач, сальдоКон, мес; // Инициализация переменных нач = 0; уд = 0; вып = 0; вБанк = 0; сальдоНач = 0; сальдоКон = 0; // Помним, что Оклад - это периодический реквизит справочника Сотрудники_2 окл = Объект.Оклад.Получить(КонецТекущегоПериода()); // При выводе применяем заданные по умолчанию параметры таблицы; // для их изменения следует обратиться к методу Опции // Выводим секцию Заголовок табл.ВывестиСекцию("Заголовок");
// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(Объект); пока ПолучитьЗапись( ) = 1 цикл мес = ДатаМесяц(ДатаНачала); если ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1 тогда нач = нач + Результат; // Добавляем в тЗначНач номер месяца, ВидРасч, Результат и ВсегоЧасов НовСтрокаВТЗнач(тЗначНач, мес); иначеЕсли ВидРасч.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда уд = уд + Результат; // Добавляем в тЗначУд номер месяца, ВидРасч, Результат и ВсегоЧасов НовСтрокаВТЗнач(тЗначУд, мес); иначеЕсли ВидРасч = ВидРасчета.НачСальдо_2 тогда сальдоНач = Результат; иначе // Вид расчета ВБанк_2 вБанк = Результат; конецЕсли; конецЦикла; // пока вып = сальдоНач + нач - уд; // Полагается к выплате В сальдоКон = вып - вБанк; // Сальдо на конец месяца ВыводНачУд(табл, тЗначНач, 1); //Вывод начислений ВыводНачУд(табл, тЗначУд, 2); //Вывод удержаний табл.ВывестиСекцию("Пусто"); // Пустая строка в РЛ сальдоНач = Формат(сальдоНач,' "4-10.2"); табл.ВывестиСекцию("сНач"); // Выводим сальдо на начало месяца вып = Формат(вып, "4-10.2"); // Форматируем данные нач = Формат(нач, "4-10.2"); уд = Формат(уд, "4-10.2"); вБанк = Формат(вБанк, "Ч-10.2"); табл.ВывестиСекцию("Итог"); сальдоКон = Формат(сальдоКон, "4-10.2"); табл.ВывестиСекцию("сКон"); // Вывод сальдо на конец месяца // Запрещаем редактирование результирующей таблицы табл.ТолькоПросмотр(1); // В методе Показать задаем пустой заголовок окна с результирующей таблицей табл.Показать(""); конецПроцедуры // ЛистокСотрудника процедура ПечатьРЛ() перем сотр, сЗначСотр, ин, табл, тЗначНач, тЗначУд; // тЗначНач, тЗначУд - таблицы значений для начислений и удержаний сотрудника ОчиститьОкноСообщений(); СоздатьТЗнач(тЗначНач); СоздатьТЗ нач(тЗ начУ д); табл = СоздатьОбъект("Таблица"); // Свяжем переменную табл с таблицей РЛ, содержащей макет расчетного листка табл.ИсходнаяТаблица("РЛ"); если кто = 1 тогда // РЛ выбранного сотрудника Состояние("Формируем расчетный листок сотрудника " + Объект.Наименование); ЛистокСотрудника(Объект, табл, тЗначНач, тЗначУд); // Вывод РЛ сотрудников выбранного подразделения // Используя метод ЖР ВыбратьПоЗначению, занесем (без повторов) значение // атрибута Объект расчетов выбранного подразделения в список сЗначСотр
иначе Состояние("Формируем список сотрудников подразделения " + Обьект.Родитель); // Код процедуры СоздатьСЗнач см. в предшествующем разделе СоздатьСЗнач(сЗначСотр); // Формируем список сотрудников подразделения для ин = 1 по сЗначСотр.РазмерСписка() цикл сотр = сЗначСотр.ПолучитьЗначение(ин); Состояние("Формируем расчетный листок сотрудника " + сотр.Наименование); ЛистокСотрудника(сотр, табл, тЗначНач, тЗначУд); // Подготовка таблиц значений для РЛ следующего сотрудника тЗначНач.УдалитьСтроки(); тЗначУд.УдалитьСтроки(); конецЦикла; // для // Поправляем закладку отбора УстановитьОтбор("Родитель", сотр.Родитель); конецЕсли; конецПроцедуры // ПечатьРЛ // Создает таблицу значений с четырьмя столбцами процедура СоздатьТЗнач(тЗнач) // Таблица значений для начислений (удержаний) сотрудника тЗнач = СоздатьОбъект("ТаблицаЗначений"); // Формируем столбцы таблицы значений. Опуская имена необязательных параметров, // сохраняем разделяющие их запятые тЗнач.НоваяКолонка("Месяц", "Число",,,, 2); тЗнач.НоваяКолонка("ВидРасчета", "ВидРасчета",,,,); тЗнач.НоваяКолонка("Сумма", "Строка",,,, 10); тЗнач.НоваяКолонка("Часы", "Строка",,,, 3); конецПроцедуры // СоздатьТЗнач // Добавляем в таблицу значений номер месяца, ВР, Результат и ВсегоЧасов процедура НовСтрокаВТЗнач(тЗнач, мес) тЗнач.НоваяСтрока(); // Добавляем новую строку // Определяем, используя атрибут идентификатор столбца, ячейки новой строки тЗнач.Месяц = мес; тЗнач.ВидРасчета = ВидРасч; // Форматируем данные тЗнач.Сумма = Формат(Результат, "Ч-10.2"); тЗнач.Часы = Формат(ВсегоЧасов, "Ч-3.0"); конецПроцедуры // НовСтрокаВТЗнач // Выводит в РЛ, если начУд = 1, таблицу с начислениями или с удержаниями, если начУд = 2 процедура ВыводНачУд(табл, тЗнач, начУд) // Выводим секцию табЗаг с заголовком для таблицы начислений (удержаний) табл.ВывестиСекцию("табЗаг"); тЗнач.ВыбратьСтроки( ); пока тЗнач.ПолучитьСтроку() = 1 цикл мес = тЗнач.Месяц; ВР = тЗнач.ВидРасчета; рез = тЗнач.Сумма; часы = тЗнач.Часы; табл.ВывестиСекцию("Расчет"); конецЦикла; // пока конецПроцедуры // ВыводНачУд
Расчетный листок
Предприятие АО ТриТ Подразделение 03 Цех; Безверхний Игорь Петрович Табельный номер 301; Оклад 3100 руб. Начисления Вид начисления Мес. 12 Оклад 12 Премия суммой 12 Премия коэффициентом 1 Премия коэффициентом 12 Премия 1234 Удержания Мес. Видудержания 12 НДФЛ Остаток прошлого месяца Начислено Удержано Полагается к выплате: Перечислено в банк Остаток на конец месяца
Сумма 3100.00 700.00 1650.00 1650.00 3550.00
Часы
Сумма 1384.50
Часы
165 165 165
-
0.20 10650.00 1384.50 9265.70 9265.00 0.70
Рис. 7.80. Расчетный листок Безверхнего И. П. Замечание. Формат "Ч-10.2", употребленный во встроенной функции Формат, обеспечит вывод ненулевого значения на поле длиной в 10 символов с двумя знаками после десятичной точки. Если первый параметр функции Формат равен нулю, то вмес то нуля согласно формату "Ч-10.2" будет выводиться символ -.
7.17.3. ВЕДОМОСТЬ ПЕРЕЧИСЛЕНИЙ В БАНК Формируется в виде текстового файла, который затем можно отправить по элек тронной почте в банк назначения. Содержит для каждого сотрудника его ФИО, та бельный номер, номер счета в банке и перечисленную сумму. При этом мы предпола гаем, что номер счета отличается от табельного номера сотрудника только префиксом Б-. Если это не так, то в справочник Сотрудники_2 придется добавить новое поле для хранения в нем номера счета сотрудника. процедура СтрокаВТекст(сотр, текст, ном, всего) далее // Ведомость перечислений в банк для одного сотрудника или сотрудников // выбранного подразделения. Формируется как текстовый файл // Поскольку процедура присутствует в модуле формы списка ЖЗ, //то все методы ЖР вызываются без префикса процедура ВедомостьБанк() перем текст, всего, сЗначСотр, ин, сотр; перем сообщение; сообщение = "Формируем ведомость перечислений сотрудника "; всего = 0; // Общая сумма перечислений в банк текст = СоздатьОбъект("Текст"); // Направляем вывод в текстовый файл текст.КодоваяСтраница(1); // Используем DOS-кодировку текста текст.ДобавитьСтроку(" Список перечислений во вклады из заработной платы на лицевые счета рабочих и служащих"); текст.ДобавитьСтроку(" " + Объект.Родитель + "," + Константа.НазваниеОрганизации); текст.ДобавитьСтроку(" за " + ПериодРегистрации.ОписательПериода + " в " + Константа.БанкОрганизации);
текст.ДобавитьСтроку("
если кто — 1 тогда // Ведомость для выбранного сотрудника Состояние(сообщение + Объект.Наименование); СтрокаВТекст(Объект, текст, 1, всего); // Вывод РЛ сотрудников выбранного подразделения // Используя метод ЖР ВыбратьПоЗначению, занесем (без повторов) значение // атрибута Объект расчетов выбранного подразделения в список сЗначСотр иначе Состояние("Формируем список сотрудников подразделения " + Объект.Родитель); // Код процедуры СоздатьСЗнач см. в предшествующем разделе СоздатьСЗнач(сЗначСотр); // Формируем список сотрудников подразделения для ин = 1 по сЗначСотр.РазмерСписка() цикл сотр = сЗначСотр.ПолучитьЗначение(ин); Состояние(сообщение + сотр.Наименование); СтрокаВТекст(сотр, текст, ин, всего); конецЦикла; // для // Поправляем закладку отбора УстановитьОтбор("Родитель", сотр.Родитель); конецЕсли; // Завершаем вывод ведомости перечислений в банк текст.ДобавитьСтроку(""); // Пустая строка // Формат "ЧПД" обеспечит вывод итоговой суммы перечислений прописью текст.ДобавитьСтроку(" Итого: " + Формат(всего, "ЧПД")); текст.ДобавитьСтроку(""); // Пустая строка текст.ДобавитьСтроку(" Гл. бухгалтер" + СимволТабуляции + Символ Табуляции + СимволТабуляции + СимволТабуляции + Константа.ГлБухгалтер.Получить(ТекущаяДата())); текст.ТолькоПросмотр(1); // Запрещаем редактирование текста текст.Показать("Ведомость перечислений в банк"); конецПроцедуры // ВедомостьБанк // Добавляет в ведомость строку под номером ном и корректирует значение переменной всего процедура СтрокаВТекст(сотр, текст, ном, всего) // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл если видРасч = ВидРасчета.ВБанк_2 тогда всего = всего + Результат; // Форматируем данные в соответствии с заголовком табличной части ведомости текст.ДобавитьСтроку(" " + Формат(ном, "С5") + ":" + Формат(Объект.Наименование, "С27") + ": " + Формат(Объект.Код, "С9") + ":" + Формат(Результат, "410.2") + " : " + Формат("Б-" + Строка(Объект.Код), "С9")); возврат; конецЕсли; конецЦикла; // пока конецПроцедуры // СтрокаВТекст
Результат для третьего цеха приведен на рис. 7.81.
Рис. 7.81. Ведомость для третьего цеха Замечание. Чтобы получить отображение ведомости перечислений в банк равно мерным шрифтом, была выполнена цепочка Текст - Текст модуля.
7.17.4. СВЕДЕНИЯ ДЛЯ БУХГАЛТЕРСКОГО УЧЕТА ЗАРПЛАТЫ Эти сведения нам предоставит запрос, вычисляющий в заданном периоде для каждой хозяйственной операции суммарные значения результатов относящихся к ней расчетов. Запрос запустим из обработки Проба. Для просмотра результатов запроса выгру зим его в таблицу значений. // Формирует и выполняет запрос по хозяйственным операциям, // суммируя относящиеся к ним результаты // Параметр зХозОп типа Запрос является входным/выходным функция ЗапрХозОп(зХозОп) перем текстЗапХозОп; // Содержание запроса перем жз, нтп, ктп; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Начало и конец текущего периода ЖЗ нтп = жз.НачалоТекущегоПериода(); ктп = жз.КонецТекущегоПериода(); текстЗапХозОп = " | период с нтп по ктп; // Период запроса // Переменные запроса | хозОп = журналРасчетов.Зарплата_2.ХозОп; | рез = журналРасчетов.Зарплата_2.Результат; // Функция запроса | функция сумХозОп = сумма(рез); // Задаем порядок выборки данных | группировка хозОп упорядочить по хозОп.Наименование;"; // Выполняем запрос и возвращаем 1 в случае удачи, или 0, если есть проблемы возврат зХозОп.Выполнить(текстЗапХозОп); конецфункции // ЗапрХозОп
// Запускает запрос зХозОп о хозяйственных операциях и выгружает // его результат для последующего просмотра в таблицу значений тЗнач процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем зХозОп, тЗнач; // Создаем объекты зХозОп и тЗнач зХозОп = СоздатьОбъект("Запрос"); если ЗапрХозОп(зХозОп) = 0 тогда возврат; // Запрос не выполнен конецЕсли; // Создаем объект тЗнач для промежуточной демонстрации выборки запроса тЗнач = СоздатьОбъект("ТаблицаЗначений"); // Выгружаем все переменные запроса в таблицу значений тЗнач // для его предварительного просмотра зХозОп.Выгрузить(тЗнач, 1); // Просмотр .таблицы значений тЗнач.ВыбратьСтроку(, "Запрос о хозяйственных операциях в таблице значений"); конецПроцедуры // Выполнить Результат приведен на рис. 7.82.
Рис. 7.82. Распределение расчетов по хозяйственным операциям Имеющаяся в тексте запроса строка-функция | функция сумХозОп = сумма(рез); задает в результирующей таблице поле сумХозОп, в которое для каждой группы, а группировка выполняется по переменной запроса хозОп, заносится сумма результа тов всех расчетов, отнесенных на текущую хозяйственную операцию. Код, повторяющий действия функции сумХозОп, может быть таким: // Возвращает в заданном периоде для заданной хозяйственной операции хозОп // сумму результатов относящихся к ней расчетов функция сумХозОп2(хозОп, жз, нтп) жз.ВыбратьПериод(нтп); схо = 0; // Искомый результат пока жз.ПолучитьЗапись() = 1 цикл если жз.ХозОп = хозОп тогда схо = схо + жз.Результат; конецЕсли; конецЦикла; // пока возврат схо; конецФункции // сумХозОп2
Запустим функцию сумХозОп2 для всех хозяйственных операций текущего рас четного периода из обработки Проба. Перечень хозяйственных операций нам даст зап рос зХозОп. // Формирует список использованных в текущем периоде ЖЗ хозяйственных операций // Вызывает затем функцию сумХозОп2 для каждой хозяйственной операции // и печатает возвращенный функцией результат процедура Выполнить( ) // Связана с кнопкой Пуск обработки Проба перем зХозОп, текстЗапХ; // Содержание запроса перем жз, нтп, ктп, сумРез; ОчиститьОкноСообщений(); // Создаем объекты зХозОп и жз зХозОп = СоздатьОбъект("Запрос"); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Начало и конец текущего периода ЖЗ нтп = жз.НачалоТекущегоПериода(); ктп - жз.КонецТекущегоПериода(); текстЗапХ = " | период с нтп по ктп; // Период запроса // Переменная запроса [ хозОп = журналРасчетов.Зарплата_2.ХозОп; | группировка хозОп упорядочить по хозОп.Наименование;"; // Выполняем запрос и возвращаем 1 в случае удачи, или 0, если есть проблемы если зХозОп.Выполнить(текстЗапХ) = 0 тогда возврат; // Запрос не выполнен конецЕсли; всего = 0; пока зХозОп.Группировка("хозОп") = 1 цикл сумРез = сумХозОп2(зХозОп.хозОп, жз, нтп); всего = всего + сумРез; Сообщить("Сумма результатов по операции " + СокрП(зХозОп.хозОп) + " равна " + символТабуляции + сумРез); конецЦикла; // пока Сообщить("Всего по всем операциям: " + всего); конецПроцедуры // Выполнить Результат: Сумма результатов по операции 2013000 равна Сумма результатов по операции 2013001 равна Сумма результатов по операции 2017002 равна Сумма результатов по операции 2300100 равна Сумма результатов по операции 2300111 равна Сумма результатов по операции 2300112 равна Всего по всем операциям: 209892.1
5.5 61793 13642.6 91301 33550 9600
Если нужно просмотреть отдельные составляющие каждой группы с указанием объекта, текст запроса нужно сформировать следующим образом: текстЗапХозОп =" | период с нтп по ктп; // Период запроса // Переменные запроса; добавляем переменную сотр | сотр = журналРасчетов.Зарплата_2.Объект; | хозОп = журналРасчетов.Зарплата_2.ХозОп;
| рез = журналРасчетов.Зарплата_2.Результат; // Функция запроса | функция сумХозОп = сумма(рез); // Задаем порядок выборки данных | группировка хозОп упорядочить по хозОп.Наименование; | группировка рез;"; // Добавленная строка Тогда в таблице результатов мы будем наблюдать приведенные на рис. 7.83 данные.
Рис. 7.83. Фрагмент результата запроса для группы хозОп =20130000
7.18. ПЕРВЫЙ РАСЧЕТНЫЙ ПЕРИОД НОВОГО ГОДА Теперь, когда выполнены расчеты и выпущены все необходимые документы, можно перейти к следующему расчетному периоду. Это можно сделать интерактивно, выбрав на панели инструментов диалога формы списка ЖЗ Зарплата 2 иконку . В этом случае будут отработаны сопровождающие смену системные действия, предопределенная проце дура глобального модуля ПриСменеРасчетногоПериода и, следовательно, добавленная нами в глобальный модуль процедура ФиксироватьСменуРП (разд. 7.3.6.3). В результате ее исполнения в документе Начало периода № 1 появится новая запись, напоминающая о произошедшем событии (рис. 7.84).
Рис. 7.84. Новый расчетный период Вспомним, что архивные записи ЖЗ (кроме фиксированных) имеют иной цвет фо на сопровождающих их иконок - голубой. Изменяется и фон иконок в журнале доку ментов: иконки, сопровождающие документы, соответствующие архивным расчетам, располагаются на темно-синем фоне. В новом периоде нет пока что ни одного расчета. Но не следует торопиться с их вводом, поскольку период открывает новый год и скорее всего для него не определены используемые справочником Сотрудники_2 календари и праздники. Поэтому предва рим ввод расчетов заданием праздников, а вслед выполним автозаполнение календа рей. Все необходимые средства для выполнения этих действий у нас есть, доступ к ним осуществляется посредством пунктов Календари и Праздники, расположенных в колонке Календари меню интерфейса Ученик.
7.19. ВЫВОДЫ 1. ЖР состоит из записей, называемых расчетами; они попадают в журнал в резуль тате проведения соответствующих документов. 2. Расчеты могут быть обязательными, дополнительными, вытесняющими, длинны ми, перерасчетами, зависимыми и обычными. Полезно контролировать, имеют ли объекты ЖЗ все обязательные расчеты. 3. Длинные расчеты могут существовать только в документах. В ЖЗ длинный расчет разбивается на соответствующее число обычных расчетов. 4. В описание группы ВР следует помещать информацию о программах, в которых группа используется. 5. Перенос данных в 1С из других программ можно выполнить, употребляя объекты типа XBase. 6. Основное назначение объектов типа Календарь и Праздники - предоставлять дан ные для вычисления числа рабочих часов и дней в заданном временном интервале. 7. Сложные документы, журналы и обработки 1С, учитывающие многие случаи жиз ни, целесообразно заменять на аналогичные по назначению, отвечающие потреб ностям предприятия более простые и, следовательно, эффективные программы. 8. В окне задания свойств документа, вводящего расчеты, нужно активизировать флажок Расчет. 9. Методы ВвестиРасчет и ЗаписатьРасчет могут быть вызваны только в модулях до кументов, вводящих расчеты. Вызвать эти методы в модулях иных объектов, на пример в модуле формы списка ЖЗ или в модуле отчета (обработки), нельзя. 10. Расчеты одного объекта располагаются в ЖЗ в порядке очередности их исполнения; 11. Команда проведения документа при выполнении перепроведения отслеживает все возможные произошедшие в документе изменения. 12. Для обновления самовытесняющего расчета при его повторном вводе другим до кументом вместо метода ЗаписатьРасчет используется метод ВвестиРасчет. 13. Правила перерасчета обнуляют значение атрибута Рассчитана у зависимых расчетов. 14. Правила перерасчета не срабатывают при расчете записи в результате выбора пункта меню Рассчитать запись. 15. Разбивая длинный расчет на части, 1С сохраняет вычисленный в соответствии с документом, порождающим расчет, результат для каждой из частей разбиения. 16. Если в результате вытеснения расчет разбивается на к частей, то каждая из этих частей наследует общий результат, то есть общий результат исходного расчета увеличивается в к раз, что входит в противоречие с документом; создавшим расчет. 17. Предопределенные процедуры модуля формы ЖР позволяют эффективно управ лять правами доступа пользователей.
8. ПОВЫШЕНИЕ ЭФФЕКТИВНОСТИ ФУНКЦИОНИРОВАНИЯ СИСТЕМЫ 8.1. КРИТЕРИИ ЭФФЕКТИВНОСТИ Эффективность функционирования системы будем оценивать быстродействием программ, временным промежутком, необходимым для ввода, редактирования дан ных, поиска ошибок и получения результата, качеством пользовательского интерфей са, надежностью вычислений и степенью защищенности данных. На первый взгляд мы не можем повлиять на эти характеристики, поскольку они зало жены в систему ее разработчиками. Это действительно так, если эксплуатировать систему как она есть. Но теперь, когда мы понимаем ее узкие звенья, знаем, как их преобразовать, и умеем делать это, некоторые параметры эффективности можно улучшить.
8.2. УВЕЛИЧЕНИЕ БЫСТРОДЕЙСТВИЯ Частично эта задача решена. Так, программы расчета зарплаты, формирования расчетных листков и ведомостей перечислений в банк, которые мы разместили в мо дуле формы списка ЖЗ Зарплата_2 и привели разд. 7.17.1-7.17.3, работают существен но быстрее, чем аналогичные встроенные в 1С процедуры. Это достигнуто за счет ут раты универсальности, но нам она в нашей задаче и не нужна. В этом направлении заинтересованные пользователи могут сделать и иные про дуктивные шаги. Иной способ повышения быстродействия - удаление из конфигурации ненужных вам объектов. Сразу это может не получиться. Так, если вы хотите удалить ВР ОплатаБЛ_Северн (больничный для северных регионов), то система выдаст предупрежде ние "Объект не может быть удален", а в окне сообщений выведет следующие текст: Данный объект использован в: ПравилоПерерасчета.ОсновныеУдержания ПравилоПерерасчета.БольничныеЛисты ПравилоПерерасчета.НДФЛ_по_13 Однако после устранения объекта из этих правил удаление ВР ОплатаБЛСеверн станет возможным.
8.3. УЛУЧШЕНИЕ КАЧЕСТВА ИНТЕРФЕЙСА В формах 1С или в созданных взамен их своих формах вы можете разместить об легчающие обработку данных элементы управления. Что мы, собственно, и сделали, например, в форме списка ЖЗ Зарплата_2. Элементы диалога этой формы позволяют ввести все записи, провести их расчеты и выпустить необходимые сопровождающие расчеты документы, не покидая формы списка ЖЗ.
Кроме размещения дополнительных элементов управления, можно произвести ре визию существующих и удалить перегружающие интерфейс элементы. Однако это не всегда просто сделать. на панели инструментов ЖЗ, предлагающую соз Возьмем, к примеру, иконку дать новый документ. В нашем случае ее желательно убрать, поскольку список пред лагаемых для выбора документов содержит много лишних позиций (рис. 8.1).
Рис. 8.1. Список, открываемый иконкой на панели инструментов ЖЗ Мы заменили эту иконку кнопкой Ввод расчета, показывающей для выбора список документов (см. рис. 7.25), вводящих расчеты в ЖЗ Зарплата_2. Их перечень, напом ним, мы внесли в перечисление ВР 2 (разд. 7.3.3). из панели инструментов можно, только убрав всю па Однако удалить иконку нель инструментов (Сервис - Панели инструментов - Дополнительные - Инструмен тальные панели окон - <Отключены>). Но даже после этого аналогичный пункт оста нется в колонке Действия меню системы. В большей мере нежелательно присутствие на панели инструментов ЖЗ иконки , предлагающей удалить документ. Для этого есть по меньшей мере три причины. Во-первых, пользователь, наблюдая ЖЗ, не видит документа, который ему предла гают удалить, - перед ним лишь часть расчетов, порожденных документом (а эти рас четы могут быть рассыпаны по всему ЖЗ), а чтобы принять решение, нужно иметь пе ред глазами объект, а не его производные. Во-вторых, пользователь, находясь в ЖЗ, наблюдает удаление не документа, а за писей ЖЗ. Такая рассогласованность намерений и визуального результата несколько обескураживает. В-третьих, расчеты, порожденные удаляемым документом, могут иметь неотмененную ручную правку. И если ошибочное удаление документа, расчеты которого не имеют ручной правки, - дело поправимое (для преодоления ошибки нужно снять по метку удаления документа в соответствующем журнале документов и провести доку мент заново), то расчеты с ручной правкой после восстановления ошибочно удаленно го документа придется произвести заново.
Ситуацию можно поправить, если в предопределенную процедуру ПриУдаленииДокумента, которую нужно расположить в глобальном модуле, добавить вызов ниже приводимой функции ЕстьЛиРучнаяПравка, которая как раз-то и фиксирует наличие расчета с ручной правкой и запрещает при наличии такового удаление породившего его документа. Функцию придется разместить в глобальном модуле, поскольку оттуда осуществляется ее вызов. // Вернет 0, если хотя бы один расчет удаляемого документа имеет ручную правку функция ЕстьЛиРучнаяПравка(док) перем жз; жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); жз.ВыбратьЗаписиПоДокументу(док); // Просматриваем все расчеты, порожденные документом док пока жз.ПолучитьЗапись() = 1 цикл если жз.Исправлена = 1 тогда возврат 0; // Есть ручная правка конецЕсли; конецЦикла; // пока возврат 1; // Нет расчетов с ручной правкой конецФункции // ЕстьЛиРучнаяПравка Саму же предопределенную процедуру ПриУдаленииДокумента можно записать следующим образом: // Предопределенная процедура глобального модуля // Интерфейс процедуры задан в системе и содержит два входных параметра: док и режим // док - удаляемый документ // Параметр режим равен единице, если проставляется DBF-пометка удаления, // и равен нулю, если документ получает 1С-пометку удаления // В нашей проблеме этот параметр неинтересен процедура ПриУдаленииДокумента(док, режим) если док.ПометкаУдаления() = 1 тогда возврат; // Если снимается пометка удаления, конецЕсли; // то никаких проверок не выполняем // Исследуем документы, порождающие расчеты // Иные документы в этом исследовании не участвуют если (док.Вид() = "Табель") или (док.Вид() = "Премия") или (док.Вид() = "НачПериода_2") тогда // Статус возврата будет равен нулю, если расчеты документа док имеют ручную // правку. При таком статусе возврата документ удален не будет если ЕстьЛиРучнаяПравка(док) = 0 тогда СтатусВозврата(0); Предупреждение("Нельзя удалить документ с ручной правкой."); конецЕсли; конецЕсли; конецПроцедуры // ПриУдаленииДокумента Подобного рода модификации заинтересованный пользователь может осуществить в великом множестве.
8.4. Н А Д Е Ж Н О С Т Ь П Р О Г Р А М М Мы рассмотрим один аспект надежности - вероятность получения ожидаемого ре зультата. Ниже мы обсудим ряд мероприятий, позволяющих повысить эту вероятность и, следовательно, надежность вычислений.
8.4.1. ПРОВЕРКА ДАННЫХ В любой задаче мы планируем получить некоторый результат. Так, нажав на икон , размещенную в диалоге формы списка ЖЗ Зарплата_2, мы рассчитываем полу ку чить ведомость перечислении в банк для выбранного сотрудника или подразделения. Видимых препятствий для получения такого результата нет, и если нет ошибок в расчетах и коде, формирующем ведомость, то будет верен и документ. В то же время в программу, выводящую ведомость, можно добавить функцию, проверяющую, точно ли подсчитан размер перечисляемой в банк суммы. Ошибка, как мы уже отмечали (разд. 7.10.1), может возникнуть, когда после отмены ручной правки какой-либо запи си, имеющей зависимые ВР, выполнен ее перерасчет (иконка панели инструментов ЖЗ), при этом атрибут Рассчитана зависимых ВР остается равным единице, а пользо ватель забывает выполнить их перерасчет. (Напомним, что ВР ВБанк_2 зависит от всех иных ВР.) Код такой проверочной функции прост: // Выполняет сравнение размеров перечислений в банк. Возвращает нуль, // если перечисления верны, и отличное от нуля число - в противном случае // При ошибке функция выдает соответствующее предупреждение функция СовпадениеПеречислений(сотр, перечислено) // перечислено = Целая часть(Начальное сальдо + Все начисления - Все удержания) перем ВР, банк; банк = перечислено; // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл ВР - ВидРасч; если (ВР.ВходитВГруппу(ГруппаРасчетов.ВсеНачисления_2) = 1) или (ВР = ВидРасчета.НачСальдо_2) тогда банк = банк - результат; иначеЕсли ВР.ВходитВГруппу(ГруппаРасчетов.ВсеУдержания_2) = 1 тогда банк = банк + результат; конецЕсли; конецЦикла; // пока возврат Цел(банк); // Берем целую часть результата конецФункции // СовпадениеПеречислений Тогда приведенная в разд. 7.17.3 процедура СтрокаВТекст, добавляющая строку в ведомость перечислений в банк, примет следующий вид: // Добавляет в ведомость строку под номером ном и корректирует значение переменной всего процедура СтрокаВТекст(сотр, текст, ном, всего)
// Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись( ) = 1 цикл если видРасч = ВидРасчета.ВБанк_2 тогда всего = всего + Результат; // Форматируем данные в соответствии с заголовком табличной части ведомости текст.ДобавитьСтроку(Формат(ном, "С5") + ":" + Формат(Объект.Наименование, "С27") + ":" + Формат(Объект.Код, "С9") + ":" + Формат(Результат, "410.2") + " : " + Формат(Объект.Код, "С9")); прервать; конецЕсли; конецЦикла; // пока // Добавляем вызов проверяющей функции СовпадениеПеречислений если СовпадениеПеречислений(сотр, Результат) о 0 тогда Сообщить("Ошибка в размере перечислений сотрудника " + сотр.Наименование); Предупреждение("Ошибка в размере перечислений сотрудника " + РазделительСтрок + сотр.Наименование); конецЕсли; конецПроцедуры // СтрокаВТекст Такой подход, правда, тоже имеет недостаток. Ведь если изменится алгоритм рас чета перечислений в банк, то код придется менять в двух местах: в ВР ВБанк_2 и в вышеприведенной функции СовпадениеПеречислений. Дублирования кода, впрочем, можно избежать, если разместить его в глобальном модуле.
8.4.2. ПРЕОДОЛЕНИЕ КОНФЛИКТОВ До сих пор мы предполагали, что с программой работает один пользователь. В этом случае обработка данных выполняется без конфликтов. Они возникают в мно гопользовательском режиме, когда несколько пользователей одновременно пытаются обновить записи одной и той же DBF-таблицы информационной базы данных. При этом оказывается, что DBF-таблица доступна только пользователю, первому получив шему к ней доступ. В этом случае говорят, что произошел захват таблицы. При еди ничных записях время захвата таблицы непродолжительно, и если программы других пользователей могут ждать освобождения таблицы, то конфликт через некоторое вре мя будет исчерпан. Если же программы не содержат кода, обеспечивающего режим ожидания, то при попытке обновления захваченной таблицы произойдет завершающая исполнение ошибка, сопровождаемая, например, таким сообщением: жз.3аписать(); {D:\ПPOБA.ERT(20)}: Объект заблокирован: Журнал расчетов Журнал заработной платы Чтобы промоделировать такую ситуацию, напишем следующий код: процедура Выполнить( ) // перем сСотр_2, сотр, жз, рез, флаг; ОчиститьОкноСообщений( );
Связана с кнопкой Пуск обработки Проба
сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2");
// Ищем во всем справочнике сотрудника с кодом 301 сСотр_2.НайтиПоКоду(301,0); сотр = сСотр_2.ТекущийЭлемент( ); жз = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде жз.ВыбратьПериодПоОбъекту(сотр); пока жз.ПолучитьЗапись( ) = 1 цикл // Пропускаем фиксированные и исправленные расчеты если жз.Фиксирована + жз.Исправлена о 0 тогда продолжить; конецЕсли; рез = жз.Результат; // Начинаем бесконечный цикл // Чтобы цикл прервать, нужно нажать клавишу Esc // В этом цикле время от времени захватывается ЖЗ флаг= 1; пока флаг = 1 цикл // Значение реквизита Результат не меняется жз.УстановитьРеквизит("Результат", рез); жз.3аписать(); конецЦикла; // пока флаг = 1 конецЦикла; // пока жз.ПолучитьЗапись() = 1 конецПроцедуры // Выполнить
Эта программа никаких изменений в ЖЗ не производит, но время от времени, вы полняя оператор жз.3аписать(); захватывает журнал расчетов Зарплата_2. Пусть 1С:Предприятие откроют два пользователя (в этом эксперименте число пользователей при желании можно сделать и большим). Первый запустит приведен ную программу, а второй - ту же программу, но вместо оператора // Ищем во всем справочнике сотрудника с кодом 301 сСотр_2.НайтиПоКоду(301,0); имеющую оператор // Ищем во всем справочнике сотрудника с кодом 302 сСотр_2.НайтиПоКоду(302, 0); То есть в ЖЗ мы будем обновлять результат другой записи, отвечающей объекту с ко дом 302. Тогда если сотрудники с кодами 301 и 302 в справочнике Сотрудники_2 есть и если в текущем периоде ЖЗ каждый из этих сотрудников имеет нефиксирован ный и неисправленный расчет, то неизбежно в работе одного из пользователей возникнет завершающая ошибка исполнения и приведенное выше сообщение об этой ошибке.
Если же в этом эксперименте вместо операторов жз.УстановитьРеквизит("Результат", рез); жз.3аписать(); записать оператор жз.Результат = рез; то ошибка возникнет сразу у двух пользователей (поскольку имеем дело с бесконеч ным циклом). Сообщение, которое они смогут наблюдать, будет таким: жз.Результат = рез; {D:\ПPOБA.ERT(21)}: Запись заблокирована! Причем ошибка будет не завершающей, а информационной и сообщение о ней бу дет воспроизводиться вплоть до прерывания обработки, выполняемого клавишей Esc. После прерывания одной из обработок блокировка будет снята и оставшаяся обработ ка продолжит вычисления. В чистом виде на практике такая ситуация может возникнуть лишь при наличии в программе ошибки, приводящей к бесконечному циклу. Но описанные пользова тельские конфликты будут происходить и в правильно написанной (с позиции одного пользователя) программе при интенсивных модификациях таблиц базы данных. Чтобы продемонстрировать метод устранения такой ошибки, продолжим начатый пример, модифицировав в нем бесконечный цикл Пока флаг = 1, введя в него управ ляющую конструкцию Попытка. // Начинаем бесконечный цикл // Чтобы цикл прервать, нужно нажать клавишу Esc // В этом цикле время от времени захватывается ЖЗ флаг = 1 ; пока флаг = 1 цикл // Этот цикл подлежит модификации // Значение реквизита Результат не меняется жз.УстановитьРеквизит("Результат", рез); // Начало добавляемого кода // Вводим в цикле Пока флагПопытки - 1 управляющую конструкцию Попытка // Цикл прервется после удачного исполнения метода Записать флагПопытки = 1; пока флагПопытки = 1 цикл попытка // Управляющая конструкция Попытка жз.3аписать(); // Этот оператор из старого кода флагПопытки = 0; Сообщить("Запись выполнена."); исключение Сообщить("Ожидаю освобождения таблицы."); конецПопытки; конецЦикла; // пока флагПопытки = 1 // Конец добавляемого кода конецЦикла; // пока флаг = 1
Внесем соответствующие изменения в обработки двух наших пользователей и за пустим их процедуры Выполнить. Внешний цикл по-прежнему будет бесконечным, но благодаря управляющей конструкции Попытка завершающих ошибок по причине блокировки DBF-таблицы происходить не будет и соответствующие ее записи будут обновляться. Добавленные вызовы встроенной процедуры Сообщить привнесут в окно сообщений (в многопользовательском режиме) следующий код: Запись выполнена. Запись выполнена. Ожидаю освобождения таблицы. Ожидаю освобождения таблицы. Запись выполнена. Продемонстрированный метод преодоления конфликтов при помощи управляю щей конструкции Попытка нужно применять каждый раз, когда есть угроза пользова тельских конфликтов на почве захвата DBF-таблиц. В частности, при работе такая уг роза существует при употреблении методов, выполняющих модификацию ЖЗ, а имен но: ВыполнитьРасчет, Рассчитать, ВвестиРасчет, ЗаписатьРасчет, ВвестиПерерасчет, Записать, ФиксироватьЗапись, ОсвободитьЗапись, Исправить, ОтменитьИсправление и УдалитьЗапись. С учетом сказанного процедура РасчетОбъекта, рассчитывающая записи сотруд ника, вызываемая из процедуры РасчетЗП (разд. 7.17.1), должна быть усовершенство вана следующим образом: // Обновленная процедура РасчетОбъекта // Рассчитывает все записи объекта ЖЗ кроме расчета с ВР НачСальдо_2 // Поскольку процедура присутствует в модуле формы списка ЖЗ, // то все методы ЖР вызываются без префикса процедура РасчетОбъекта(сотр) перем флаг, сотр2, флагПопытки; // Флаг будет равен единице, если у сотрудника есть расчет с ВР Оклад_2 флаг = 0; сотр2 = сотр; // Запоминаем для вывода сообщения // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде ВыбратьПериодПоОбъекту(сотр); пока ПолучитьЗапись() = 1 цикл если видРасч = ВидРасчета.НДФЛ_2 тогда флаг = 1; конецЕсли; // Фиксированные и исправленные записи рассчитывать нет смысла если Фиксирована + Исправлена = 0 тогда // Начало добавляемого кода // Вводим в цикле Пока флагПопытки = 1 управляющую конструкцию Попытка // Цикл прервется после удачного исполнения метода Рассчитать флагПопытки = 1; пока флагПопытки = 1 цикл
попытка // Управляющая конструкция Попытка Рассчитать(); // Этот оператор из старого кода флагПопытки = 0; Состояние ("Выполнен расчет сотрудника " + сотр.Наименование); исключение Состояние("Ожидаю освобождения таблицы."); конецПопытки; конецЦикла; // пока флагПопытки = 1 // Конец добавляемого кода конецЕсли; конецЦикла; // пока если флаг = 0 тогда Сообщить("Оформите табель сотруднику " + сотр2.Наименование); конецЕсли; конецПроцедуры // РасчетОбъекта Аналогичные изменения должны быть внесены и в другие процедуры модуля формы списка ЖЗ Зарплата_2, а также в модули форм созданных нами документов и справочни ков, хотя степень их конфликтности по сравнению с ЖЗ существенно ниже. Вопрос надежности исполнения предопределенных процедур модуля документа ОбработкаПроведения и ОбработкаУдаленияПроведения обсуждается в следующем разделе. Здесь же заметим, что в периоды расчета зарплаты, продолжительность кото рых в силу организационных причин невелика (чуть более недели), эти процедуры (особенно ОбработкаПроведения), вызываемые в создающих расчеты ЖЗ документах, интенсивно модифицируют базу данных и, следовательно, обладают повышенной конфликтностью.
8.4.3. ТРАНЗАКЦИИ Транзакция - это механизм обновления таблиц базы данных, обеспечивающий при отсутствии внешних воздействий получение ожидаемого результата. В случае невоз можности обновления таблиц в соответствии с установленными правилами транзак цию можно отменить, не производя каких-либо модификаций данных. (Под внешними воздействиями понимаются факторы, возникающие не в результате исполнения про граммы, а по иным причинам, например при сбоях в энергоснабжении, вирусной атаке и др.) Если речь идет о единичном обновлении базы данных, например в результате вы полнения метода Записать, то гарантии получения ожидаемого результата нам дает управляющая конструкция Попытка. При пакетном обновлении базы данных, напри мер в результате проведения документа, порождающего многочисленные расчеты ЖЗ, нужно применять транзакцию. Надо сказать, что в модуле документа в предопределенных процедурах ОбработкаПроведения и ОбработкаУдаленияПроведения 1С выполняет предусмотренные в этих процедурах действия, употребляя транзакцию. Поэтому добавлять в них свои транзак ции нет необходимости. Известно, что транзакции блокируют используемые в них DBF-файлы, поэтому необходимо максимально содействовать их успешному завершению. В частности,
предопределенные процедуры ОбработкаПроведения и ОбработкаУдаленияПроведения не следует включать приостанавливающие до отклика пользователя процедуры Вопрос, Предупреждение, функции, вызывающие диалог ввода данных, например ВвестиЗначение, и другие открывающие диалоги команды. В решаемых нами задачах, кроме упомянутых процессов проведения документов, транзакцию можно было бы применить при расчете зарплаты объекта, записав код процедуры РасчетОбъекта следующим образом: // Еще один вариант процедуры РасчетОбъекта, на этот раз использующий транзакцию процедура РасчетОбъекта(сотр) перем флаг, сотр2; // Флаг будет равен единице, если у сотрудника есть расчет с ВР Оклад_2 флаг = 0; сотр2 = сотр; // Запоминаем для вывода сообщения // Открываем выборку расчетов объекта, зарегистрированных в текущем периоде если ВыбратьПериодПоОбъекту(сотр) = 0 тогда возврат; конецЕсли; НачатьТранзакцию();
//
Первый добавленный оператор..
пока ПолучитьЗапись() = 1 цикл если видРасч = ВидРасчета.НДФЛ_2 тогда флаг = 1; конецЕсли; // Фиксированные и исправленные записи рассчитывать нет смысла если Фиксирована + Исправлена = 0 тогда Рассчитать( ); конецЕсли; конецЦикла; // пока если флаг = 1 тогда // Транзакция фиксируется, если проведен Табель ЗафиксироватьТранзакцию(); // Второй добавленный оператор иначе Отменить
// Транзакцию();
флаг = 0 //
Третий добавленный оператор
Сообщить("Оформите табель сотруднику " + сотр2.Наименование); конецЕсли; конецПроцедуры // РасчетОбъекта В этом обновленном коде 3 новые (для нас) встроенные в 1С процедуры: НачатьТранзакцию, ЗафиксироватьТранзакцию и ОтменитьТранзакцию. Назначение проце дур понятно из их названий. Остановимся на особенностях их исполнения. Во-первых, пользователь, начавший транзакцию, захватывает одну или не сколько DBF-таблиц, участвующих в транзакции, и тем самым Не дает работать другим участникам процесса. При длительном захвате таблицы ждущая процедура может завершиться аварийно, о чем пользователь получит соответствующее сооб щение, например такое:
пока ПолучитьЗапись( ) = 1 цикл {ЖурналРасчетов.Зарплата_2.Форма.ФормаСписка.Форма.Модуль(250)}:Таблица: CJ4287 Ошибка обращения к данным при транзакции, выполняемой другим пользователем Если захват непродолжительный, то после завершения транзакции ждущий поль зователь благополучно продолжит вычисления. Во-вторых, обновление захваченных таблиц во время транзакции не производится, но необходимые для обновления сведения накапливаются в надлежащем виде для последующего использования. Если в результате транзакции получены правильные результаты, то следует вы звать процедуру ЗафиксироватьТранзакцию, которая, собственно, и выполняет обнов ление базы данных и завершает транзакцию. Если обнаружены ошибки, то вызов про цедуры ОтменитьТранзакцию завершит транзакцию, но никаких модификаций данных произведено не будет. Об обнаружении ошибок должен позаботиться программист, встроив в процедуру с транзакцией соответствующий код. В нашем примере осуществлены вызовы всех трех процедур, что несколько изме нило характер получаемого результата. Так, если ранее процедура РасчетОбъекта мо дифицировала результаты расчетов и при отсутствии расчета с ВР Оклад_2, то теперь в такой ситуации данные не меняются, поскольку вызывается .процедура Отменить Транзакцию. Если сравнивать две модификации процедуры РасчетОбъекта (первая с управ ляющей конструкцией Попытка, а вторая с транзакцией), то предпочтение следует, ви димо, отдать первому варианту. На этот счет есть по меньшей мере 3 причины. Первая в том, что нет необходимости рассматривать расчет объекта как единое целое и упот реблять для него транзакцию; вполне приемлемо, если расчеты будут выполняться по следовательно в соответствии с установленной очередностью, и нет трагедии, если между двумя вашими расчетами выполнится один-другой расчет иного пользователя. Во-вторых, как мы убедились, при длительных транзакциях ждущие процедуры могут завершаться аварийно. И наконец, транзакция может потребовать дополнительных ре сурсов (пространственных, например в виде области памяти на диске, и временных), снижающих эффективность исполнения программы.
8.5. ЗАЩИТА ДАННЫХ 8.5.1. ПОСТАНОВКА ЗАДАЧИ Защита данных - это также многогранная задача, включающая такие подзадачи, как физическое сохранение данных, обеспечение их целостности и непротиворечиво сти, верификация данных, ограничение прав доступа и др. В целом в 1С достаточно средств для защиты данных. Однако в ряде случаев для упрочнения защиты требуется вмешательство программиста. Остановимся на проблеме ограничения прав доступа. (Иные проблемы либо час тично затрагивались по мере изложения материала, либо находятся за пределами на стоящего пособия.) Изначально пользователю можно присвоить имя, назначить интерфейс, пароль и снабдить определенными правами доступа (разд. 1.2, 1.3).
Наборы прав задаются на закладке Права окна Конфигурация (рис. 8.2).
Рис. 8.2. Задание набора прав выполняется на этой закладке Для заданного набора прав, если по строке с именем набора дважды ударить мы шью, откроется редактор прав (рис. 8.3).
Рис. 8.3. Редактор пользовательских прав В этом редакторе для выбранного набора прав (в нашем случае - Ученик) можно определить права Ученика в отношении каждого класса объектов в целом (рис. 8.4) и отдельных представителей классов (рис. 8.5).
Рис. 8.4. Задание прав доступа Ученика ко всем справочникам системы
Рис. 8.5. Детализация прав Ученика в отношении справочника Сотрудники_2 Напоследок надо не забыть задать права по управлению конфигурацией системы (рис. 8.6).
Рис. 8.6. Задание прав по управлению системой Далее встает проблема ограничения прав доступа в пределах справочника Сотрудники_2, журнала Зарплата_2 и других журналов. Суть проблемы в том, что начисление зарплаты осуществляется несколькими расчетчиками, которые, разумеются, используют один и тот же справочник сотрудников, журнал расчетов и одинаковые журналы документов. И в отношении к имеющимся в этих объектах данным расчетчики обладают равными правами. Более того, они, как правило, пользуются единым по своему содержанию интерфейсом. Положим, что на нашем предприятии зарплатой занимаются 3 расчетчика, обслу живающие соответственно цехи 1, 2 и 3. Тогда полезно ввести дополнительные огра ничения, запретив расчетчику цеха 1 доступ к данным расчетчиков цехов 2 и 3. Анало гичные ограничения распространим и на других расчетчиков. Реализация этих ограничений проста. Создается справочник ПраваРасчетчика; в нем для каждого расчетчика указываются подразделения, к которым он имеет доступ. Далее эта информация используется при работе со справочниками и журналами 1С. Используется следующим образом. В модуль формы списка справочника Сотрудники_2 мы добавим две предопределенные процедуры - ПриВыбореРодителя и ПриСменеИерархии. В первой мы разрешим каждому расчетчику открывать только дос тупные ему в соответствии со справочником ПраваРасчетчика группы (подразделе-
ния), а во второй запретим всем расчетчикам переходить в режим просмотра всего справочника. При открытии справочника мы зададим режим отображения данных справочника с учетом имеющейся иерархии (см. рис. 5.1). В модуль формы списка журнала Зарплата_2 добавляется предопределенная про цедура ПриУстановкеОтбора, позволяющую расчетчику работать только с подразде лениями, назначенными ему справочником ПраваРасчетчика. В модуле формы списка журнала Зарплата_2 придется также модифицировать процедуру ПриОткрытии, которая теперь, если ЖЗ открывает расчетчик, будет подби рать для работы соответствующие ему подразделения. Замечание. Рассматриваемые мероприятия по ограничению доступа к данным не являются исчерпывающими, но раскрывают механизм решения этой проблемы.
8.5.2. СПРАВОЧНИК ПраваРасчетчика Реквизиты и свойства справочника задаются в соответствии с рис. 8.7, а описание реквизитов см. в табл. 8.1.
Рис. 8.7. Свойства и реквизиты справочника ПраваРасчетчика Таблица 8.1 Реквизиты справочника ПраваРасчетчика Реквизит Описание имяПользователя Имя, с которым расчетчик входит в систему. Задается в конфигурации подразделение Подразделение, с которым может работать расчетчик. Вводится как группа справочника Сотрудники_2
Примечание Строка длиной в 15 символов Имеет разновидность типа Справочник.Сотрудники_2
Правами ввода и редактирования данных справочника обладает только админист ратор системы. Редактирование данных осуществляется в форме списка, и после заполнения спра вочник будет содержать отображенные на рис.'8.8 сведения.
Рис. 8.8. Имена пользователей предприятия и обслуживаемые ими подразделения Чтобы в справочнике Сотрудники_2 можно было выбирать группы (подразделе ния), в модуль формы ФормаДляВыбора мы добавили следующий код: // Предопределенная процедура ПриОткрытии процедура ПриОткрытии() ВыборГруппы(1); конецПроцедуры // ПриОткрытииСами же имена пользователей определим в конфигурации в соответствии с рис. 8.9, задав им одинаковые права, интерфейс и рабочий каталог.
Рис. 8.9. Задание имен пользователей и их прав в конфигурации системы
8.5.3. МЕНЮ И ПРАВА ПОЛЬЗОВАТЕЛЕЙ В меню пользователя Ученик добавим возможность вызова вновь созданного справочника ПраваРасчетчика, оставив иные пункты меню без изменений. Для работ ника, отвечающего за кадры, создадим интерфейс Кадры, меню которого построим в соответствии с рис. 8.10.
Рис. 8.10. Меню для лица, регистрирующего кадровые изменения
Также работнику отдела кадров доступен справочник Дети, подчиненный спра вочнику Сотрудники_2. В отношении указанных в меню объектов пользователь под именем Кадры имеет полные права. Расчетчик сможет работать с объектами, показанными на рис. 8.11.
Рис. 8.11. Меню расчетчика Причем справочник Сотрудники_2 доступен расчетчику только в режиме чтения. Замечание. Колонка Операции меню системы отключается в обоих создаваемых пользовательских меню. Порядок ее отключения иллюстрирует рис. 1.4. Иные введенные нами операции, такие, как удаление помеченных объектов, мо дификация констант, редактирование справочников ПраваРасчетчика, хозяйственных операций и ХозОпДляВР, управление календарями и праздниками, возложим на лицо, выполняющее функции администрирования системы. Для этих целей у нас имеется интерфейс Ученик. В то же время необходимо ограничить права этого лица по отно шению к данным, создаваемым в отделе кадров и расчетном отделе. Эти данные должны быть доступны ему только в режиме просмотра.
8.5.4. МОДИФИКАЦИЯ МОДУЛЯ ФОРМЫ СПИСКА ЖУРНАЛА ЗАРПЛАТА_2 Суть вносимых изменений отражена в разд. 8.5.1. Однако, прежде чем заняться кодированием, несколько слов придется сказать об изменениях интерфейса ЖЗ. Вопервых, если работает расчетчик, то нужно отключить возможность вывода ЖЗ с при менением закладок отбора 01/1,01/2, 01/3, 02 Цех и 03 Цех (см. рис. 7.26). Дело втом, что ограничить доступ к закладке отбора встроенными средствами 1С нельзя. Это выясняется, когда пытаешься применить предопределенную процедуру модуля формы ПриВыбореЗакладки: она работает лишь с атрибутом формы Закладки, имею щим тип Список значений. Метод ЗакладкиОтбора этот атрибут не изменяет. Так, по сле применения метода ЗакладкиОтбора("Родитель"); формирующего в случае ЖЗ вышеперечисленные закладки отбора, процедура Сообщить(ТипЗначения(Форма. Закладки)); напечатает в окне сообщений 0 - число, выдаваемое встроенной функцией ТипЗначения для неопределенного типа данных. Во-вторых, нужно позаботиться о том, чтобы не изменились функции радиокно пок с идентификатором кто группы Режим расчета. Таким образом, переменная закл при входе расчетчика должна быть равна едини це, но закладки отбора отображаться не должны и, конечно же, должны быть в соот ветствии со справочником ПраваРасчетчика введены ограничения на рабочее про-
странство расчетчика. Если в систему входит пользователь с правами Ученика, то ее функционирование должно отвечать привычной, изложенной в разд. 7.4.3 схеме. Озвученные изменения поддерживаются следующим кодом: // Определяем сПраваРасч, расч, значениеОтбора и флагРасч как переменные модуля перем сПраваРасч, расч, значениеОтбора, флагРасч; // Следует уже имеющийся в модуле код (разд. 7.4.4.1) // Предопределенная процедура модуля формы ЖР // Эта процедура добавляется в модуль формы списка ЖЗ Зарплата_2 // Интерактивно отбор управляется иконками // лежащими на панели управления ЖЗ процедура ПриУстановкеОтбора(графаОтбора, значениеОтбора) если флагРасч = 0 тогда // Если работает не расчетчик, то возврат; // контроль прав не осуществляется конецЕсли; // Простым перебором устанавливаем, можно ли расчетчику расч работать // с интерактивно выбираемым им подразделением сПраваРасч.ВыбратьЭлементы(); пока сПраваРасч.ПолучитьЭлемент() = 1 цикл если (расч = СокрЛП(сПраваРасч.ИмяПользователя)) и (сПраваРасч.Подразделение = значениеОтбора) тогда СтатусВозврата(1); // Работать можно возврат; // Выход из процедуры конецЕсли; конецЦикла; // пока СтатусВозврата(0); // Работать нельзя Предупреждение("Это не Ваше подразделение."); конецПроцедуры // ПриУстановкеОтбора процедура При6ткрытии() // Предопределенная процедура перем реж, значРеж; // Восстанавливаем с диска значения переменных диалога закл и кто // для текущего сеанса работы закл = ВосстановитьЗначение("ЗакладкиВЗарплате"); если ТипЗначения(закл) = 0 тогда // Если значение переменной закл не восстановлено закл = 1; кто = 1; иначе кто = ВосстановитьЗначение("КтоВЗарплате"); конецЕсли; ктоСтар = кто; ПоЦехам( ); если флагРасч = 0 тогда // Добавленная строка кода если закл = 1 тогда значОтбора = ВосстановитьЗначение("ЗначениеОтбора"); ЗакладкиОтбора("Родитель", значОтбора); конецЕсли;
иначе // Еще 5 добавленных строк кода Форма.Закл.Видимость(0); // Элемент диалога закл расчетчику не нужен // Установим разрешенный для расчетчика расч отбор УстановитьОтбор("Родитель", значениеОтбора); конецЕсли; реж = ВосстановитьЗначение("РежимПредставления"); значРеж = ВосстановитьЗначение("ЗначениеПредставления"); если реж = 1 тогда УстановитьПредставление(1); иначе УстановитьПредставление(реж, значРеж); конецЕсли; нтп = НачалоТекущегоПериода(); конецПроцедуры // ПриОткрытии // Включает/отключает режим вывода по цехам. Если закл = 0, то устанавливает // кто = 1 и делает недоступными радиокнопки группы Режим расчета процедура ПоЦехам() // Эти 3 строчки добавляются в прежнюю процедуру ПоЦехам (разд. 7.4.4.1) если флагРасч = 1 тогда // Начало добавляемого кода возврат; конецЕсли; // Конец добавляемого кода если закл = 1 тогда // ЗакладкиОтбора("Родитель"); кто = ктоСтар; Форма.Кто.Доступность(1); Форма.Кто2.Доступность(1); иначе ЗакладкиОтбора(""); кто= 1; Форма.Кто.Доступность(0); Форма.Кто2.Доступность(0); конецЕсли; конецПроцедуры // ПоЦехам()
Если используются закладки отбора
// А это оператор основной программы модуля формы списка ЖЗ // Переменная модуля флагРасч получит значение 1, если в системе расчетчик, . // или О-в противном случае. Функция ЕстьЛиРасчетчик включена в глобальный модуль флагРасч = ЕстьЛиРасчетчик(расч, сПраваРасч, значениеОтбора); Замечания: 1.
Функция ЕстьЛиРасчетчик включается в глобальный модуль. Это целесообразно, потому что проверка наличия расчетчика в системе выполняется неоднократно. Можно было бы пойти дальше - включить в глобальный модуль переменную фла гРасч, дать ей атрибут Экспорт и вычислять ее значение при загрузке системы, вы зывая функцию ЕстьЛиРасчетчик в предопределенной процедуре глобального мо дуля ПриНачалеРаботыСистемы, но мы остановимся на первом варианте. Функция ЕстьЛиРасчетчик имеет следующий код:
// А это функция, которую мы включаем в глобальный модуль // Следует после всех его программных компонентов функция ЕстьЛиРасчетчик(расч, сПраваРасч, значениеОтбора) экспорт // Этот пользователь работает в системе и открывает форму списка ЖЗ Зарплата 2 расч = ИмяПользователя(); сПраваРасч = СоздатьОбъект("Справочник.ПраваРасчетчика"); // Смотрим, является ли этот пользователем расчетчиком. Если да, то флагРасч получит // значение 1, или 0 - в противном случае. Проверку осуществляем простым перебором сПраваРасч.ВыбратьЭлементы(); флагРасч = 0; // Первоначальное предположение пока сПраваРасч.ПолучитьЭлемент() = 1 цикл если расч = СокрЛП(сПраваРасч.имяПользователя) тогда флагРасч = 1 ; // В системе расчетчик значениеОтбора = сПраваРасч.Подразделение; прервать; // Прерываем цикл конецЕсли; конецЦикла; // пока возврат флагРасч; конецФункции // ЕстьЛиРасчетчик 2.
Если бы мы хотели избежать переборных операций при поиске пользователя или под разделения в справочнике ПраваРасчетчика, то нам пришлось бы задать свойство Сор тировка соответствующим реквизитам справочника. Однако используемый перебор вследствие ничтожного объема просматриваемых значений не приведет к замедлению вычислений, поэтому нужды в задании обозначенного свойства нет.
3.
Напомним, что возможные отборы отображаются и становятся доступными после в приведенном на рис. 8.12 фрагменте диалога Отбор записей. выбора иконки
Рис. 8.12. Фрагмент диалога Отбор записей: возможные отборы ЖЗ Зарплата_2 Подробнее об управлении отборами см. в разд. 5.9.1.
8.5.5. МОДИФИКАЦИЯ МОДУЛЯ ФОРМЫ СПИСКА СПРАВОЧНИКА СОТРУДНИКИ_2 Аналогичные дополнения следует сделать в модулях форм списка и для выбора справочника Сотрудники_2. Наша задача в том, чтобы запретить расчетчику переход в режим просмотра списка всех сотрудников, а также не допускать проникновения в запрещенные для него подразделения. В отличие от ЖЗ используемый в справочнике Сотрудники_2 отбор по реквизиту Образование отключать не надо, поскольку отбор осуществляется в пределах выбранного родителя. (Напомним, что расчетчику справочник Сотрудники_2 доступен только в режиме чтения.)
I// Определяем сПраваРасч, расч, родитель и флагРасч как переменные модуля перем сПраваРасч, расч, родитель, флагРасч; // Следует уже имеющийся в модуле код (разд. 5.12.6) // Предопределенная процедура модуля формы справочника // Эта процедура добавляется в модули форм списка // (основной и для выбора) справочника Сотрудники_2 процедура ПриВыбореРодителя(родитель) если флагРасч = 0 тогда // Если работает не расчетчик, то возврат; // контроль прав не осуществляется конецЕсли; // Простым перебором устанавливаем, можно ли расчетчику расч работать // с интерактивно выбираемым им подразделением сПраваРасч.ВыбратьЭлементы(); пока сПраваРасч.ПолучитьЭлемент() = 1 цикл если (расч = СокрЛП(сПраваРасч.ИмяПользователя)) и (сПраваРасч.Подразделение = родитель) тогда СтатусВозврата(1); // Можно менять родителя возврат; //Выход из процедуры конецЕсли; конецЦикла; // пока СтатусВозврата(0); // Родителя менять нельзя Предупреждение("Это не Ваше подразделение."); конецПроцедуры // ПриВыбореРодителя // Предопределенная процедура модуля формы справочника // Эта процедура добавляется в модули форм списка // (основной и для выбора) справочника Сотрудники_2 процедура ПриСменеИерархии(иерарх) если флагРасч = 0 тогда // Если работает не расчетчик, то возврат; // контроль прав не осуществляется конецЕсли; если иерарх = 1 тогда // Разрешим устанавливать режим возврат; // иерархического доступа к записям конецЕсли; // справочника Сотрудники_2 СтатусВозврата(0); // Нельзя просматривать весь список Предупреждение("Доступ к списку всех сотрудников невозможен."); конецПроцедуры // ПриСменеИерархии процедура ПриОткрытии() // Предопределенная процедура // Этот оператор нужен в форме списка для выбора (разд. 8.5.2); // в основной форме списка его нужно опустить ВыборГруппы(1); // Если работает расчетчик, то отображаем справочник Сотрудники_2 // в виде иерархического списка если флагРасч = 1 тогда ИерархическийСписок(1);
// Позиционируемся в первой разрешенной для расчетчика/?асч группе ИспользоватьРодителя(родитель); конецЕсли; конецПроцедуры // ПриОткрытии // А это оператор основной программы модуля формы списка справочника // Переменная модуля флагРасч получит значение 1, если в системе расчетчик, // или О-в противном случае. Функция ЕстьЛиРасчетчик включена в глобальный модуль флагРасч = ЕстьЛиРасчетчик(расч, сПраваРасч, родитель); Замечание. Процедуру ПриСменеИерархии лучше изъять из кода, а в предопреде ленной процедуре ПриОткрытии метод ИерархическийСписок вызвать следующим образом: если флагРасч = 1 тогда // Нельзя менять режим отображения ИерархическийСписок(1, 0); //справочника Сотрудники_2 // Позиционируемся в первой разрешенной для расчетчика расч группе ИспользоватьРодителя(родитель); иначе ИерархическийСписок(1, 1); // Можно менять режим отображения конецЕсли; Когда второй параметр метода ИерархическийСписок равен нулю, смена режима отобра жения справочника (в нашем случае переход к неиерархическому списку) невозможна.
8.6. СОПРОВОЖДЕНИЕ СИСТЕМЫ. ОБНОВЛЕНИЕ КОНФИГУРАЦИИ Задачами сопровождения системы являются поддержание ее работоспособности и повышение эффективности ее функционирования. В комплексе мероприятий по сопровождению можно выделить два вида работ: регламентные и инициативные.
8.6.1. РЕГЛАМЕНТНЫЕ РАБОТЫ Регламентные работы состоят в администрировании данных и системы. Цель ад министрирования данных - обеспечение сохранности данных, их проверка и в случае обнаружения ошибок принятие необходимых для их устранения мер. Это большая ра бота, требующая высокой квалификации. При администрировании системы решаются задачи организации рабочих мест и обновления системы по мере поступления новых версий. Перечень названных и иных работ, направленных на поддержание работоспособ ности системы, определяется, как известно, особым документом - регламентом. В нем же содержатся указания о периодичности работ и о порядке их регистрации, а также упоминание об инстанции, контролирующей выполнение регламента. , В число регламентных работ входит регистрация произведенных изменений кон фигурации и внешних отчетов и обработок. Сведения об изменениях разместим в табл. 8.2.
Таблица 8.2
Изменения, произведенные в системе Описание
Изменение Создана внешняя обработка Проба
Внешние отчеты и обработки Файл Ilpo6a.ert. Предназначена для запуска произвольных обработок, например процедур удаления записей ЖЗ, мягкой смены расчетного периода (разд. 7.4.5), отчета по константам (разд. 1.9 и 6.1), по справочнику Сотрудники_2 (разд. 5.11), начальному сальдо (разд. 7.4.7), программной обработки удаляемых записей (разд. 5.14), загрузки начального сальдо из DBF-файла (разд. 7.4.6) и т. д. Изменения конфигурации системы Константы (файл 1SC0NST.DBF)
Добавлена периодическая константа к5
Коэффициент, используемый в ВР Премия_1234, равной (Оклад_2 + ПремияКоэф_2 + ПремияСум_2) * к5
Добавлена периодическая константа СтавкаНалога
Используется при вычислении результата ВР НДФЛ_2 Обработки
Удалено две обработки
РасчетЗарплаты и РасчетЗарплаты! Отчеты
Удален один отчет
ПеречисленияВБанки
Виды расчетов (хранятся в конфигурации, файл ICV7.MD) Добавлено 7 ВР
НачСальдо_2, Оклад_2, ПремияКоэф_2, ПремияСум_2, Премия 1234_2, НДФЛ_2, ВБанк_2. Описание ВР см. в разд. 7.3.2 (табл. 7.2) и 7.11 (табл. 7.15)
Удалено два ВР
АвторскиеЗаСк и ОплатаБЛ_Северн. Порядок удаления ВР см. в разд. 8.2
Правила перерасчетов (хранятся в конфигурации, файл 1CV7.MD) Добавлено 3 правила перерасчета
Премия 1234_2, НДФЛ_2, ВБанк_2. Описание правил см. в разд. 7.7 (табл. 7.12)
Группы расчетов (хранятся в конфигурации, файл 1CV7.MD) Добавлено две группы расчетов
ВсеУдержания 2 (разд. 7.3.6.2) и ВсеНачисления 2 (разд. 7.8.1)
Добавлено 5 справочников
Сотрудники_2 (разд. 5.1 и 5.2, файл SC4194.DBF), Образование 2 (разд. 5.7, файл SC4214.DBF), Дети (разд. 5.10.1, файл SC4233.DBF), ХозОпДляВР (разд. 7.3.7, файл SC4326.DBF), ПраваРасчетчика (разд. 8.5.2, файл SC4355.DBF)
Добавлено 5 документов. Для документа, если он имеет и заголовок и табличную часть, создаются 2 DBF-файла, или 1 - в противном случае
ПриказОПриеме (разд. 5.8.2, файл DH4216.DBF), ИзменениеОклада (разд. 5.8.4, файлы DH4226.DBF и DT4226.DBF), Табель (разд. 7.6, файлы DH4244.DBF и DT4244.DBF), Премия (разд. 7.9, файлы DH4293.DBF и DT4293.DBF), НачПериода 2 (разд. 7.3.6.3, файл DT4321.DBF)
Справочники
Документы
Описание
Изменение Удален один документ
ВводОтработанногоВремени (файлы DH3061.DBF HDT3061.DBF) Журналы документов
Добавлено два журнала документов
Табель (разд. 7.6.5), Расчеты (разд. 7.9.5). Все журналы документов хранятся в файле 1 SJOURN.DBF Журнал расчетов
Добавлен один ЖЗ
Зарплата_2 (разд. 7.4.2, файл CJ4287.DBF)
Удален один ЖЗ
Зарплата (файл CJ209.DBF) Календари
Добавлено два календаря
Служащие_2, Рабочие_2 (разд. 7.5.1, файл CL.DBF)
Добавлено два перечисления
Скидки (разд. 4.2), ВР_2 (разд. 7.3.3). Хранятся в конфигура ции, файл 1CV7.MD
Перечисления
Замечания: 1. Подобную таблицу следует иметь каждому лицу, сопровождающему систему. 2.
При попытке удалении документа (рис. 8.13)
1С может
возникнуть предупреждение
Рис. 8.13. Предупреждение при попытке удаления документа ВводОтработанногоВремени. и поясняющее его сообщение Данный объект использован в: ГрафаОтбора.Сотрудник Чтобы это препятствие преодолеть, следует в конфигурации открыть указан ные на рис. 8.14 пункты меню, а затем дважды ударить мышью по подпункту Сотрудник.
Рис. 8.14. Графы отбора журнала документов В появившемся диалоге (рис. 8.15) перенести вправо элемент левого списка Документ.ВводОтработанногоВремени.
Рис. 8.15. Устраняем препятствие для удаления документа ВводОтработанногоВремени Теперь документ ВводОтработанногоВремени может быть удален. 3.
При удалении документа система удаляет CDX- и DBF-файлы, созданные ей для документа.
4.
На самом деле с целью ускорения работы системы из конфигурации удалены все, кроме созданных нами и используемых в работе, ВР, правила перерасчето в, груп пы ВР, перечисления, документы, справочники, перечисления, отчеты и обработ ки. В результате таких манипуляций файл конфигурации 1CV7.MD уменьшился почти в 6 раз до 1,3 Мбайт.
5.
Получить список введенных в конфигурацию изменений можно, загрузив прежде копию неизмененной конфигурации, а затем, имитируя попытку объединения ее с модифицированной (разд. 8.6.4), выпустить отчет об изменениях. В нашем слу чае он будет содержать следующий текст: Задача Библиотека Картинок - Объект изменен Глобальный модуль - Объект изменен Справочник.Сотрудники_2 - Объект добавлен Справочник.Образование_2 - Объект добавлен Справочник.Дети - Объект добавлен Справочник.ХозОпДляВР - Объект добавлен Справочник.ПраваРасчетчика - Объект добавлен Документ.ПриказОПриеме - Объект добавлен Документ.ИзменениеОклада - Объект добавлен Документ.Табель - Объект добавлен Документ.Премия - Объект добавлен Документ.НачПериода_2 - Объект добавлен Перечисление.Скидки - Объект добавлен
Перечисление.ВР_2 ГрафаОтбора.Сотрудник ГрафаОтбора.Подразделение Журнал.ПриказыКадровые Журнал.Табель Журнал.Расчеты ЖурналРасчетов.Зарплата_2 ПравилоПерерасчета.НДФЛ_2 ПравилоПерерасчета.ВБанк_2 ПравилоПерерасчета.Премия1234_2 ВидРасчета.НачСальдо_2 ВидРасчета.Оклад_2 ВидРасчета.ПремияКоэф_2 ВидРасчета.ПремияСум_2 ВидРасчета.Премия1234_2 ВидРасчета.ВБанк_2 ВидРасчета.НДФЛ_2 ГруппаРасчетов.ВсеУдержания_2 ГруппаРасчетов.ВсеНачисления_2 Календарь.Служащие_2 Календарь.Рабочие_2 Права ПолныеПрава РасчетЗП Кадры Ученик Интерфейсы РасчетЗП Кадры Ученик
- Объект добавлен - Объект изменен - Объект изменен - Объект изменен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект изменен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен - Объект добавлен
8.6.2. ИНИЦИАТИВНЫЕ РАБОТЫ Инициативные работы направлены на повышение качества системы, то есть на оп тимизацию системы по перечисленным в разд. 8.1 критериям. Часть оптимизирующих мероприятий мы уже упомянули. Здесь же остановимся на еще одной стороне вопроса - скорости получения результата, которым в нашей задаче является расчет зарплаты всем сотрудникам и выпуск сопровождающих этот рас чет документов. В этом плане лицо, сопровождающее систему, будучи осведомленным об особенностях ее работы, может дать полезные рекомендации и при необходимости произвести необходимые модификации программы с целью практической реализации принятых рекомендаций.
К примеру, полезно минимизировать число вводимых документов. Это повысит быстродействие системы и снизит время ввода и редактирования данных. Эта задача, с одной стороны, решается организационными мероприятиями, например по каждому ВР для цеха оформляется один документ. С другой стороны, некоторые меры по сни жению числа документов может предпринять и программист. Так, после незначительных преобразований мы научились вводить начало расчет ного периода на основе одного документа для всего предприятия. Причем каждый расчетный период вводится одним и тем же документом НачПериода_2 под номе ром 1, в который при начале нового периода добавляется одна строка табличной части (см. рис. 7.82). Этот документ удалять нельзя. Защиту от удаления мы поставим в предопределен ной процедуре глобального модуля ПриУдаленииДокументд, добавив в нее следую щий код: если док.Вид() = "НачПериода_2" тогда СтатусВозврата(0); Предупреждение("Нельзя удалять документ Начало расчетного периода."); возврат; конецЕсли; Еще один шаг в направлении снижения числа документов мы сделали, создав д окументы Табель и Премия, вводящие по несколько ВР каждый. Таким образом, чтобы ввести все расчеты с приведенными в табл. 7.2 ВР предприятия (разд. 7.3.2), мы можем обойтись всего двумя документами на цех (Табель и Премия). Схожие решения могут быть приняты и для других ВР, даже таких, как оплата больничных или расчет отпус ка. Программная реализация таких подходов, как мы убедились, не представляет ос обых затруднений. Другая оптимизационная ниша - это исключение повторных операций по вводу данных и их обработке. Известно, что на предприятиях многие виды оплат начисляют ся в подразделениях, например премиальные, простои, всевозможные доплаты (за р аботу в ночь, в праздник и др.). Там же ведется учет отработанного времени. Поэтому логично разработать и поставить в подразделения облегченные конфигурации 1С, поз воляющие выполнять присущие им расчеты. Тогда задачи расчетного отдела предпр иятия существенно упростятся и будут состоять в импорте подготовленных в подразде лениях документов, выполнении сложных расчетов, например отпускных, бухгалтер ском учете заработной платы и выпуске необходимых отчетов, в том числе и для внешних организаций, налоговых, пенсионных и др. Третья сфера инициативных работ - это выработка предложений по оптимизация производственных процессов и согласование их с моделями, реализуемыми средства ми 1С. Словом, пространство для инициативных работ велико. Будут ли выполняться эти работы или нет, от 1С не зависит, и поэтому этот вопрос здесь не обсуждается. В то же время все необходимые для реализации подобных мероприятий средства в 1С имеют ся, в чем читатель мог убедиться, просматривая предшествующие разделы пособия.
8.6.3. ОПТИМИЗАЦИЯ КОНФИГУРАЦИИ И ИНФОРМАЦИОННОЙ БАЗЫ Оптимизация достигается за счет удаления из конфигурации и базы данных неис пользуемых объектов и файлов. Для этого прежде составляется свод применяемых объектов (табл. 8.3). Таблица 8.3 Используемые объекты конфигурации Описание
Объект Константы НазваниеОрганизации
Используется в расчетном листке и в ведомости перечислений в банк (процедура ВедомостьБанк формы списка ЖЗ Зарплата_2)
БанкОрганизации
Используется в ведомости перечислений в банк
ГлБухгалтер
"
СтавкаНалога
Используется в ВР НДФЛ_2
к5
Используется в ВР Премия_1234 Справочники
Банки
Используется в объекте Константа.БанкОрганизации
Счета
Используется в объекте Справочник.ХозяйственнаяОперация
ХозяйственнаяОперация
Используется в объектах Справочник.ХозОпДляВР, ЖурналРасчетов.Зарплата_2
ВидыАналитики
Используется в объекте Справочник.Счета
Сотрудники_2
Используется в объектах Константа.ГлБухгалтер, Справочник.Дети, Справочник.ПраваРасчетчика, Документ.ПриказОПриеме, Документ.ИзменениеОклада, Документ.Табель, Документ.Премия, ЖурналРасчетов.Зарплата_2
Образование_2
Используется в объектах Справочник.Сотрудники_2, Документ. ПриказОПриеме
Дети
Хранит сведения о детях сотрудников
ХозОпДляВР
Хранит связанные с ВР хозяйственные операции
ПраваРасчетчика
Содержит перечень доступных расчетчику подразделений
ПриказОПриеме
Используется в объекте Журнал.ПриказыКадровые
ИзменениеОклада
Используется в объекте Документ.ПриказОПриеме
Табель
Создает ВР Оклад_2, НДФЛ_2, ВБанк_2
Премия
Создает ВР ПремияКоэф_2, ПремияСум_2, Премия_1234
НачПериода_2
Фиксирует смену расчетного периода
ПриказыКадровые Расчеты Табель
Содержит документы ПриказОПриеме и ИзменениеОклада
Документы
Журналы документов Содержит документы НачПериода_2 и Премия Содержит документ Табель
Описание
Объект Перечисления ДаНет
Используется в объекте Документ.Премия.Премия1234
ВР_2
Используется в модуле формы списка ЖЗ Зарплата_2 Журналы расчетов
Зарплата_2
Содержит расчеты зарплаты сотрудников из справочника Сотрудники_2 Правила перерасчета
НДФЛ_2
При вводе расчетов с ВР Оклад_2, ПремияКоэф_2, ПремияСум_2 и Премия_1234 нужно пересчитать расчеты с ВР НДФЛ_2
ВБанк_2
При вводе расчетов с ВР НачСальдо_2, Оклад_2, ПремияКоэф_2, ПремияСум_2, Премия_1234 и НДФЛ_2 нужно пересчитать расчеты с ВР ВБанк_2
Премия_1234
При вводе расчетов с ВР Оклад_2, ПремияКоэф_2 и ПремияСум_2 нужно пересчитать расчеты с ВР Премия_1234 Виды расчетов (см. табл. 7.2 и 7.11) Группы видов расчетов
ВсеУдержания_2
Включает ВР НДФЛ_2
ВсеНачисления_2
Включает ВР Оклад_2, ПремияКоэф_2, ПремияСум_2 и Премйя_1234 Календари
Служащие_2
Календарь для служащих
Рабочие_2
Календарь для рабочих
ПриНачале РаботыСистемы
Предопределенная процедура глобального модуля. Выполняет инициализацию переменных и проверку, можно ли создать ЖР Зарплата_2
глДействия
Вызывается в документе Табель
ПриСмене РасчетногоПериода
Предопределенная процедура глобального модуля. Вызывает процедуру ФиксироватьСменуРП
Фиксировать СменуРП
Заносит данные о смене расчетного периода в документ НачПериода_2
ЕстьЛиРучная Правка
Функция вернет 0, если хотя бы один расчет удаляемого документа имеет ручную правку
ПриУдалении Документа
Предопределенная процедура глобального модуля. Вызывает функцию ЕстьЛиРучнаяПравка и не позволяет удалять документ с ручной правкой. Не позволяет также удалять документ НачПериода_2
ЕстьЛиРасчетчик
Функция вернет 1, если в систему вошел расчетчик, или 0 в противном случае. Используется в ЖЗ Зарплата_2 и в справочнике Сотрудники_2 для ограничения прав доступа
Процедуры и функции глобального модуля
Все остальные объекты из конфигурации можно удалить, сохранив предваритель но копию исходного файла. А в глобальном модуле облегченного варианта системы, предназначенного, скажем, для подразделений предприятия, следует оставить ограни ченное число процедур и функций, например приведенных на рис. 8.16.
Рис. 8.16. Сохраняемые процедуры и функции глобального модуля В оставшемся коде глобального модуля нужно не забыть убрать ссылки на уда ленные из конфигурации объекты. Удаление ненужных объектов, таких, как справочники, документы и журналы, по влечет удаление соответствующих им DBF- и CDX-файлов. Тогда, запуская облегчен ную систему, мы снизим и число открываемых файлов (система открывает все файлы в момент ее загрузки), и число операций на поиск нужных объектов, например групп ВР или перечислений. Чтобы сохранить работоспособность модифицированной конфигурации без жур нала Зарплата, в предопределенной процедуре глобального модуля ПриНачалеРаботыСистемы в операторе ЖЗ = СоздатьОбъект("ЖурналРасчетов.Зарплата"); объект Зарплата нужно заменить на объект Зарплата_2: ЖЗ = СоздатьОбъект("ЖурналРасчетов.Зарплата_2"); Аналогичные изменения следует сделать и в других оставляемых процедурах гло бального модуля. Впрочем, можно от имени ЖЗ Зарплата_2 вернуться к имени Зар плата. Перед удалением документов из конфигурации можно, находясь в 1С:Предприятии, употреблять следующую процедуру (на примере документа НачалоМесяца): процедура Выполнить() док = СоздатьОбъект("Документ.НачалоМесяца"); док.ВыбратьДокументы( ); пока док.ПолучитьДокумент( ) = 1 цикл
// Проставляем 1 С-пометку удаления // Затем придется выполнить удаление помеченных объектов док.Удалить(0); конецЦикла; // пока ОткрытьФорму("Журнал.Общий"); // Контрольный просмотр результата конецПроцедуры // Выполнить
Удаленные из конфигурации объекты всегда можно добавить из ее сохраненной копии (файл 1 CV7.MD), если воспользоваться режимом Объединение конфигураций.
8.6.4. ОБНОВЛЕНИЕ КОНФИГУРАЦИИ Время от времени 1С поставляет новые версии конфигураций, содержащие свежие на работки фирмы. Ваша задача - добавить из обновленной конфигурации в рабочую нужные фрагменты и при этом сохранить все привнесенные модификации (см. табл. 8.2). Задача решается следующим образом. Находясь в конфигураторе, выберем пункт Объединение конфигураций колонки Конфигурация. Далее откроем файл с новой конфигурацией. Его имя скорее всего будет совпадать с именем файла текущей кон фигурации (1CV7.MD), а расположение, естественно, - нет. После сравнения текущей и новой конфигураций 1С откроет диалог, представлен ный на рис. 8.17.
Рис. 8.17. Результаты сравнения конфигураций
В диалоге выберем в группе Приоритет конфигурации переключатель Текущая конфигурация и нажмем на кнопку Выкл. все. Последнее действие погасит все флажки в столбце Объект. То есть, если нажать ОК, никаких изменений произведено не будет. Выбор метода объединения зависит от объединяемых объектов. Если он не может быть задан одинаковым для всех объектов, то объединение конфигураций придется . выполнить дважды. Впрочем, число производимых объединений может быть и боль шим, если использовать разные приоритеты конфигураций.
Теперь, употребляя элементы диалога Открыть, Сравнить и Отчет, следует при нять решение в отношении каждого объекта, после чего нажать ОК. Процесс изменения будет сопровождаться сообщениями следующего вида: Начало процесса объединения конфигураций - Режим замещения ведущих объектов - Текущая конфигурация является приоритетной - Добавление Объекта: "Константа.НашБанк" Окончание процесса объединения конфигураций После объединения выполняется сохранение полученной конфигурации. Замечание. Обновляя конфигурацию, не забываете вносить соответствующие из менения в глобальный модуль, добавляя в него вновь появившиеся переменные и программные компоненты, удаляя или модифицируя устаревшие.
8.7. ВЫВОДЫ 1. Настройку 1С следует выполнять на основе комплексной программы, ориентиро ванной на повышение эффективности функционирования системы. 2. Критериями эффективности являются время ввода и редактирования данных, вре мя вычислений, качество пользовательского интерфейса, надежность вычислений и степень защищенности данных. 3. При многопользовательском доступе возможные конфликты преодолеваются при помощи конструкции Попытка или процедур для транзакций. Там, где возможно, транзакции нужно замещать конструкцией Попытка. 4. По умолчанию предопределенные процедуры модуля документа ОбработкаПроведения и ОбработкаУдаленияПроведения выполняются с применением транзакции. 5. После введения справочника ПраваРасчетчика ограничение доступа данных ста новится легко решаемой задачей. 6. Специалист, сопровождающий систему, должен выполнять не только регламент ные, но и инициативные работы, направленные на оптимизацию программы и про цессов, модели которых программа реализует. 7. Быстродействие системы повысится, а задачи ее сопровождения существенно уп ростятся, если перейти от полного к облегченному варианту системы, удалив из нее неиспользуемые объекты. 8. Удалению объектов должно предшествовать детальное описание используемых в работе объектов, процедур и функций. 9. Для подразделений предприятия, выполняющих частичный расчет зарплаты своих сотрудников, создаются максимально легкие версии программы. 10. Удаленные объекты всегда можно добавить в текущую конфигурацию из ранее сохраненной копии.
9. О Б М Е Н Д А Н Н Ы М И С другими системами 1С обменивается данными преимущественно посредст вом текстовых и DBF-файлов. В этой главе мы рассмотрим вторую возможность, предварительно сообщив базовые сведения об алгоритмах сортировки и поиска данных.
9.1. МЕТОДЫ СОРТИРОВКИ ДАННЫХ Основное назначение сортировки - обеспечить быстрый поиск данных. Помимо этого в отсортированном файле или массиве гораздо быстрее выполнять многие вы числения. Например, существенно быстрее подсчитывается число элементов, равных заданному значению. Также во многих случаях отсортированный файл более удобен для просмотра и визуального анализа данных.
9.1.1. ВНЕШНЯЯ И ВНУТРЕННЯЯ СОРТИРОВКА Данные в файле можно отсортировать, применяя следующий порядок действий: а) ввести данные в массив; б) выполнить его сортировку; в) переместиться в начало файла; г) перенести данные из отсортированного массива в файл. Такая сортировка файла называется внутренней - все данные файла одновременно находятся в выделен ной под процесс памяти. Сортировка, при которой часть данных находится в принадлежащей программе памяти, а часть во внешней памяти, называется внешней. Крайним проявлением внеш ней сортировки является непосредственная сортировка DBF-файла, в котором, как из вестно, можно редактировать отдельные записи.
9.1.2. ПОНЯТИЕ КЛЮЧА Пусть файл данных содержит некоторую последовательность из п элементов: r1, r2,..., rn. Каждый элемент файла будем называть записью. Как правило, записи фай ла состоят из одинакового числа компонентов, называемых полями записи. В общем случае компоненты записи могут быть разного типа. Пример. В табл. 9.1 приводится фрагмент DBF-файла, содержащего данные о сотрудниках предприятия; строка таблиц является записью, а отдельная ячейка ее полем. Таблица 9.1 Фрагмевт файла SC4194.DBF Группа СсылкаНа Группу
Код
Наименование
Флаг Папки
6
2
201
Абрамова Лариса Сергеевна
2
7
2
202
Куприкова Людмила Сергеевна
2
8
2
203
Митина Ольга Владимировна
2
Другие поля
Наименование
Флаг Папки
Группа
СсылкаНа Группу
Код
9
3
111
Агальцов Юрий Алексеевич
2
10
3
112
Добрецов Борис Юрьевич
2
11
4
121
Волосков Михаил Андреевич
2
12
4
122
Кузьмина Раиса Николаевна
2
13
5
131
Васильева Елена Ивановна
2
15
5
132
Смирнова Нина Федоровна
2
16
5
133
Хохлов Евгений Николаевич
2
Другие поля
Свяжем с. каждой записью файлов ri ключ ki , понимая под ключом одно из полей записи. Правда, такое понятие ключа является узким: в общем случае ключом записи rt является некоторое выражение, среди операндов которого присутствует одно или не сколько полей записи. Так, в приведенной таблице данные упорядочены по выраже нию СсылкаНаГруппу + Наименование (знак сложения в выражении уместен, по скольку поле СсылкаНаГруппу имеет символьный тип). Выбор ключа диктуется практическими целями поиска и представления данных. Так, в приведенной таблице естественно для просмотра упорядочить данные по номе ру цеха (группы), а в пределах каждого цеха разместить фамилии сотрудников в алфа витном порядке. Файл отсортирован по ключу, если для любых ki < kj запись ri всегда предшествует rj, где i и j - номера записей в файле до выполнения сортировки. (Первая запись файла имеет номер 1.) Вполне возможно, что две записи имеют в некотором файле одинаковый ключ. Метод сортировки называется устойчивым, если для всех записей ri, и rj, таких, что кi = kj, выполняется условие: в отсортированном файле ri, предшествует rj , если ri,предшествует ri, в первоначальном файле.
9.1.3. СОРТИРОВКА ТАБЛИЦЫ УКАЗАТЕЛЕЙ Сортировать можно или сами записи файла, или указатели некоторой вспомога тельной таблицы. Пример первого случая приведен на рис. 9.1. Номер записи
Ключ
Другие поля
Ключ
1
8
4
2
20
8
3
15
10
4
4
15
5
10
20 а
Другие поля
б
Рис. 9.1. Сортировка записей: а - исходный файл; б - отсортированный файл
Если объем данных в каждой записи файла велик, то сортировка самих записей является нецелесообразной, в частности по причине больших временных затрат на пе ремещение записей в процессе сортировки. Кроме того, часто файл имеет не один, а несколько ключей. В этом случае можно ввести вспомогательную таблицу указате лей или несколько таких таблиц и перемещать при сортировке не записи, а указатели на записи (рис. 9.2). Такая сортировка называется сортировкой таблицы адресов (ука зателей). В базах данных таблица указателей может размещаться в отдельном индекс ном файле. Таблица указателей может содержать два поля: ключ и номер соответствующей записи исходного файла.
Рис. 9.2. Отсортированная таблица указателей При работе с таблицами указателей задача поиска записи решается в два этапа: 1) в таблице указателей ищется ключ; 2) в файле данных ищется запись (по ее номеру), с которой связан найденный в таблице указателей ключ. Рассмотрим теперь алгоритмы внутренней пузырьковой и быстрой сортировки ключей. Расширение этих методов для сортировки записей файла или таблицы указа телей представляется очевидным.
9.1.4. СОРТИРОВКА МАССИВА МЕТОДОМ ПУЗЫРЬКА Сортировка методом пузырька наиболее проста для реализации, но имеет незначи тельную вычислительную эффективность. Не теряя общности, будем для простоты изложения в дальнейшем рассматривать задачу сортировки массива х целых чисел, в котором первые п чисел должны быть от сортированы так, чтобы для Идея сортировки методом пузырька в том, чтобы просмотреть массив последова тельно несколько раз. Один просмотр состоит из сравнения каждого элемента массива со следующим за ним элементом (xi сравнивается с xi+1) и обмена этих двух элементов, если они располагаются не в нужном порядке (если xi> хi+1). Рассмотрим массив: 25 57 48 37 12 92 86 33. Результат сортировки: 12 25 33 37 48 57 86 92. Проанализируем процесс сортировки.
Н а п е р в о м п р о с м о т р е делаются сравнения: x1 c x2 (25 с 57) Нет обмена х2 с х3 (57 с 48) Обмен х3 с х4 (57 с 37) Обмен х4 с х5 (57 с 12) Обмен х5 с х6 (57 с 92) Нет обмена х6 с х7 (92 с 86) Обмен х7 с х8 (92 с 33) Обмен После первого просмотра в результате обменов элементы массива расположатся в таком порядке: 25 48 37 12 57 86 33 92 Отметим, что наибольший элемент (в данном случае 92) находится после первого просмотра в нужной позиции. В общем случае элемент xn-pass+1 результирующего массива будет находиться в нужной позиции после итерации pass. Отсюда и идет на звание метода: каждое число медленно "всплывает", как пузырек, вверх на свою ко нечную позицию. После второго просмотра в результате обменов элементы массива расположатся так: 25 37 12 48 57 33 86 92 Как и ожидалось, в нужной позиции оказалось второе по величине число, 86. По скольку каждая итерация помещает в нужную позицию очередной элемент, для сорти ровки массива из и чисел потребуется не более п - 1 итераций. Полный набор итераций при сортировке методом пузырька таков: Массив до начала сортировки 25 57 48 37 12 92 86 33 Итерация 1 25 48 37 12 57 86 33 92 Итерация 2 25 37 12 48 57 33 86 92 Итерация 3 25 12 37 48 33 57 86 92 Итерация 4 12 25 37 33 48 57 86 92 Итерация 5 12 25 33 37 48 57 86 92 Итерация 6 12 25 33 37 48 57 86 92 Итерация 7 12 25 33 37 48 57 86 92 В каждой строчке приведенного списка итераций подчеркнуты элементы массива, находящиеся в нужной позиции. Анализ полного набора итераций подсказывает два очевидных улучшения метода. Во-первых, поскольку все элементы в позициях к> п - pass + 1 уже находятся по сле итерации pass на нужных местах, их рассмотрение на последующих итерациях из быточно. Следовательно, на каждой итерации число необходимых сравнений умень шается на единицу. Так, при первом просмотре нужно сделать п - 1 сравнений, на вто ром - и - 2, на просмотре с номером pass - только п - pass. To есть данный процесс ускоряется с каждым просмотром. Во-вторых, не всегда следует выполнять все и - 1 просмотров. В частности, приве денный массив был отсортирован после пяти итераций (вместо семи). Для исключения холостых проходов нужно иметь возможность обнаружения факта завершения сорти ровки массива и прекращать итерации при его обнаружении. Признаком того, что мас сив отсортирован, является отсутствие перестановок на очередном проходе. Примени-
тельно к нашему примеру это означает, что факт завершения сортировки будет уста новлен только на шестом проходе. Проанализируем вычислительную эффективность метода. Всего алгоритм (без усо вершенствований) предусматривает выполнение п -1 просмотров и и -1 сравнений на каждом просмотре. Таким образом, общее число сравнений на всех возможных прохо дах равно (n - 1)*(n -1) = п2 - 2n + 1, что составляет 0(п2). Введенные усовершенствования метода хотя и сокращают общее число сравнений, но не изменяют порядка вычислительной сложности. В самом деле, число сравнений на итерации pass равно n - pass. При наличии к итераций общее число сравнений равно (n -1) + (n - 2) + ... + (n - k) - (2k*п - k2 - k)/2. Можно показать, что среднее число ите раций k представляет собой 0(п), так что общая формула имеет по-прежнему порядок 0{п2), хотя постоянный сомножитель теперь меньше, чем ранее. Если массив полностью отсортирован, то вычислительная сложность метода со ставляет 0(п) - необходимо л - 1 сравнений, чтобы в этом убедиться. Для улучшения метода имеются и иные способы. Один из них таков: сортировка методом пузырька может быть ускорена при помощи выполнения следующих один за другим просмотров в противоположных направлениях, так что небольшие по вели чине элементы быстро перемещаются в начало массива таким же способом, как боль шие элементы перемещаются в его конец. Это приводит к уменьшению необходимого числа просмотров.
9.1.5. БЫСТРАЯ СОРТИРОВКА Современные процедуры сортировки используют одну из модификаций алгоритма быстрой сортировки, к изложению которого мы переходим. Рассмотрим массив х: 25 37 12 33 48 57 92 86 В нем число 48 характеризуется тем, что, во-первых, все расположенные левее не го числа меньше 48 и, во-вторых, числа, расположенные правее него, больше 48. Назо вем такое число разделителем массива. Нетрудно понять, что теперь мы можем от дельно решать задачу сортировки для чисел до разделителя и для чисел после него. Кроме того, сам разделитель находится в нужной позиции, то есть в дальнейшей сор тировке он уже не рассматривается. Рассмотрим теперь массив, в котором нет разделителя: 37 25 57 48 12 92 86 33 Чтобы воспользоваться только что приведенной идеей уменьшения размерности задачи сортировки, нам надо научиться выполнять такие перестановки в массиве, по сле которых один из его элементов станет разделителем. Выберем для будущего раз делителя первый элемент массива, то есть число 37. Разделитель окажется в нужной позиции, если разместить числа 25, 12 и 33 слева от 37. Выполним это так: будем про сматривать последовательно элементы массива начиная с его первой позиции до тех пор, пока не встретим число, большее разделителя. Присвоим этой позиции имя L1. Далее просмотрим элементы массива начиная с последней позиции, останавливаясь при обнаружении числа, меньшего 37. Дадим такой позиции имя R1. В нашем случае после выполнения этих двух просмотров возникнет следующая ситуация: 37 25 57 48 12 92 86 33 L1 R1
Нетрудно предугадать следующий шаг алгоритма - числа 57 и 33 должны быть пе реставлены местами, тогда получится массив 37 25 33 48 12 92 86 57 L1 R1 Продолжим теперь поиск элемента, большего 37, перемещаясь вправо, начиная с позиции L1, и элемента, меньшего 37, перемещаясь влево, начиная с позиции R1. Та кой поиск даст следующий результат: 37 25 33 48 12 92 86 57 L1 R1 Выполним и перестановку: 37 25 33 12 48 92 86 57 L1 R1 Еще одна пара перемещений не изменит картины, но даст нам основание для пре кращения левых и правых перемещений вдоль массива (критерий прекращения пере мещений - истинность выражения L1 >= R1) и перестановки разделителя (числа 37) в его окончательную позицию. После выполнения перемещений, предшествующих пе рестановке разделителя, имена R1 и L1 расположатся так: 37 25 33 12 48 92 86 57 Rl L1 Сам же разделитель должен быть размещен в позиции R1, что выполняется в ре зультате обмена элементов х[1] и x [ R 1 ] , то есть чисел 12 и 37. Приведем и результат: 12 25 33 37 48 92 86 57 Итак, массив разбит на две части (до и после разделителя), которые можно сорти ровать отдельно, применяя к каждой из частей только что приведенную схему. Завер шим же сортировку, когда длина каждой из полученных в результате разбиения частей будет равна единице. Замечание. Выбор в качестве будущего разделителя первого элемента массива (или последующего фрагмента) необязателен и в ряде случаев неудовлетворителен. Так, в случае почти отсортированного массива будущий разделитель лучше выбирать ближе к середине рассматриваемого фрагмента. Вычислительная сложность быстрой сортировки оценивается как 0(nlog2n). Так, если сортируется массив из 1024 элементов, то пузырьковая сортировка будет в сред нем выполняться в
раза медленнее.
9.2. ПОИСК ДАННЫХ Поиск данных в векторе и файле выполняется по ключу. Как и при сортировке, выделяют два метода поиска: внутренний и внешний. В первом случае весь файл нахо дится в отведенной под программу памяти ЭВМ. В случае внешнего поиска большая часть данных находится во внешней памяти, например на жестком диске.
Рассмотрим внутренний поиск применительно к одномерному массиву. Предположим, что х - это вектор, содержащий п ключей. Пусть key является аргументом поиска, то есть искомым значением ключа. Сформулируем задачу поиска: установить в переменную search наименьшее целое число i такое, что x[i] = key, если такое i существует, и нуль - в противном случае. (При такой постановке задачи нижняя граница массива х должна быть больше нуля.)
9.2.1. ПОСЛЕДОВАТЕЛЬНЫЙ ПОИСК Последовательный поиск является простейшей формой поиска. В представленной формулировке запись фрагмента поиска тривиальна: перем х[20], нашел, ключ, ин, п; <здание значений вектора х и переменной ключ> n = 20; нашел = 0; ин= 1; пока (ин <= п) и (нашел = 0) цикл если х[ин] = ключ тогда нашел = ин; конецЕсли; ин = ин+1; конецЦикла // пока; если нашел = 0 тогда Сообщить("Ключ не найден."); иначе Сообщить("Искомый элемент имеет в векторе x номер " + Строка(нашел)); конецЕсли; Эффективность последовательного поиска оценим из предположения, что аргу мент поиска может равновероятно располагаться в любой позиции массива. Подсчита ем среднее число сравнений вида х[ин] = ключ, необходимых для завершения поиска. Если аргумент является первым элементом массива, то необходимо одно сравнение; если последним, то необходимо и сравнений. То есть среднее число сравнений для ус пешного поиска составит (n + 1)/2. А в случае неуспешного - п. В любом случае вы числительная сложность алгоритма последовательного поиска равна 0(п). Область применения последовательного поиска - поиск в неупорядоченных масси вах (файлах). Если же данные упорядочены, то следует применять бинарный поиск, имеющий и иное название - дихотомия.
9.2.2. БИНАРНЫЙ ПОИСК Методы поиска в отсортированных таблицах указателей основаны на алгоритме бинарного поиска. Пусть упорядоченный массив х из п элементов содержит значения 5 7 11 18 26 32 44 57 81 90 94 97 107 116 129 147 179 и пусть задан аргумент поиска ключ, равный, например, 129.
Идея алгоритма бинарного поиска такова: сравнить аргумент поиска ключ со значением среднего элемента х[сред] массива х, где сред = [п/2], а [с] - целая часть числа с; • если они равны, то поиск завершен, иначе, если ключ < х[сред], выполнить аналогич ным образом поиск в позициях массива х, предшествующих позиции сред, в противном случае, если ключ > х[сред], выполнить аналогичным образом поиск в позициях массива х, следующих за позицией сред. Исключить из дальнейшего рассмотрения часть массива позволяет тот факт, что массив упорядочен. Проиллюстрируем процесс бинарного поиска. Число элементов массива п = 17, тогда [п/2] = 8. Поэтому первоначально выполняется сравнение сред с x 8 = 5 7 . Так как сред > x8, то зона поиска на следующем шаге ограничивается участком от 9-го элемента до 17-го. Этот участок состоит из девяти элементов и его середи ной является элемент х13 = 107 ([(9 + 17)/2] = 13). Поскольку сред > x 1 3 , то зона по иска ограничивается участком от 14-го до 17-го элемента. Его серединой является элемент x15. На этом процесс поиска завершен, так как х15 = сред. Отобразим на рис. 9.3 процесс поиска элемента ключ = 129, выделяя посредством подчеркива ния на каждом шаге зоны поиска. •
Итерация 0 Итерация 0 Итерация 1 Итерация 3
5 7 11 18 26 32 44 57 81 90 94 97 107 116 129 147 179 5 7 11 18 26 32 44 57 81 90 94 97 107 116 129 147 179 5 7 11 18 26 32 44 57 81 90 94 97 107 116 129 147 179 5 7 11 18 26 32 44 57 81 90 94 97 107 116 129 147 179 Рис. 9.3. Пример дихотомии
Каждое сравнение уменьшает число возможных кандидатов в 2 раза. Максималь ное число шагов поиска будет в том случае, когда аргумент поиска находится в начале или в конце массива. В этом случае потребуется log2n + 1 итераций. Действительно, т если число элементов в массиве равно п = 2 , ключ будет найден, когда нерассмотрен ным останется только один элемент, то есть через т шагов. В свою очередь, при за данном п имеем т = log2n. После анализа последнего элемента получаем общее число итераций log2n + 1. Поэтому вычислительная сложность бинарного поиска составляет O(log2n). Однако приведенный алгоритм не позволяет в общем случае точно решить задачу поиска, когда файл или массив содержат повторяющиеся значения ключей. Рассмот рим, например, массив 5 7 11 18 26 32 44 57 81 90 94 97 107 129 129 147 179 в котором элемент (ключ) 129 содержится 2 раза. Тогда, если аргумент поиска будет равен 129, алгоритм, как и прежде, укажет, что ключ находится в 15-й позиции, то есть будет найдено не первое, а второе значение ключа 129 (первое значение ключа распо ложено в позиции 14). В ряде случаев эта ошибка принципиальна, тогда она устраня ется в результате очевидной модификации алгоритма бинарного поиска.
9.2.3. СРАВНЕНИЕ ПОСЛЕДОВАТЕЛЬНОГО И БИНАРНОГО ПОИСКА Пусть файл, в котором выполняется поиск, отсортирован и содержит 1024 (210) элемента. В случае последовательного поиска наибольшее число итераций будет равно 1024, а бинарного - 11. То есть разница в два порядка. Сравним теперь временные затраты на поиск в случае неотсортированного файла. При последовательном поиске максимальное число итераций, разумеется, сохраняется. Бинарный поиск неприменим. Выполним, однако, быструю сортировку файла. В сред нем для этого потребуется 1024*log21024 = 10240 итераций. Далее выполним бинар ный поиск, на который будет затрачено не более 11 итераций. Приведенные цифры позволяют сделать вывод: если файл неотсортирован и в про цессе вычислений задача поиска в файле возникает сравнительно редко (в нашем при мере не более 10 раз), то можно применять последовательный поиск, в противном слу чае более целесообразно прежде отсортировать файл или таблицу указателей и при вычислениях применять бинарный поиск. Как правило, именно такой подход исполь зуется на практике.
9.3. ПЕРЕДАЧА ДАННЫХ ИЗ 1С В DBF-ФАЙЛЫ 9.3.1. ЭКСПОРТ ДАННЫХ ИЗ СПРАВОЧНИКА СОТРУДНИКИ_2 9.3.1.1. ПОСТАНОВКА ЗАДАЧИ В разд. 7.4.6 мы загрузили в 1С, используя объект типа XBase, DBF-файл с данными о начальном сальдо. Объекты того же типа применяются и для выгрузки данных из 1С. Рассмотрим задачу переноса данных из справочника Сотрудники_2 в файл еmployee.dbf. Задача осложняется тем, что реквизит Оклад справочника Сотрудники_2 является пе риодическим. Перенос значений этого реквизита мы выполним в отдельный файл salary.dbf, то есть поступим так же, как и 1С, которая хранит данные о периодических рек визитах в файле 1SCONST.DBF. Структуру файла salary.dbf воспроизводит табл. 9.2, в заголовках столбцов которой после символа / указано имя соответствующего поля DBFфайла. Таблица 9.2 Структура файла salary.dbf на примере истории окладов Горюновой У. В. Код\Id 2010 2010 2010
Дата/Date 20.11.2001 22.11.2001 30.11.2001
Имя документа /DocNarne ПриказОПриеме ИзменениеОклада -
Номер документа /Docld
2 2 -
Оклад/Salary 2500 2700 3200
Характеристики полей файла salary.dbf опишем в табл. 9.3, в которой формат чи словых полей определяется заданными в конфигурации 1С свойствами Длина и Точ ность соответствующих реквизитов.
Таблица 9.3 Характеристики полей файла salary.dbf Свойства поля
Имя поля Id, Docld
Числовые поля формата 5.0
Date
Поле типа Дата
DocName
Строка из 20 символов
Salary
Числовое поле формата 10.2
Вторым осложняющим обстоятельством является то, что реквизиты Образование, ПриказОклад, ПриказПрием и Календарь имеют типы, которые в чистом виде нельзя вос произвести в DBF-файле. (Средствами 1С в DBF-файле можно определить числовые, сим вольные, логические поля и поля типа Дата.) Поэтому нам придется некоторым образом заменить значения этих реквизитов на значения стандартных типов, но таким образом, чтобы иметь возможность восстановить при обратном преобразовании исходные величи ны. Замены будем выполнять в соответствии с правилами, изложенными в табл. 9.4. Таблица 9.4 Правила преобразования значений объектов 1С Объект 1С
На что заменяется в DBF-файле
Элемент справочника
Идентификатор справочника и код элемента
Документ
Идентификатор документа и номер документа
Календарь
Идентификатор календаря
И наконец, третий осложняющий фактор в том, что, кроме данных о сотрудниках, справочник содержит группы, отражающие имена подразделений предприятия, а так же описывает иерархию групп и сотрудников (см. табл. 5.1). В создаваемом файле эти сведения также надо сохранить. Таким образом, файл employee.dbf будет иметь отраженные в табл. 9.5 поля. Таблица 9.5 Поля файла employee.dbf Имя DBFполя
Имя 1Cреквизита
Id
Код
Parentld
-
IsGroup
Свойства DBF-поля
Описание
Числовое поле формата 5.0 Код сотрудника или группы " " 5.0 Ссылка на группу, в которую вхо дит текущий элемент Логическое поле
-
Имеет значение истина, если эле мент является группой, или ложь в противном случае
Name
Наименование Строка из 30 символов
ФИО сотрудника или имя группы
Ed
Образование
Строка из 13 символов
Имеет значение Образование_2
EdId
"
Числовое поле формата 3.0 Код соответствующей записи в справочнике Образование_2
Имя DBFполя OrderEng OrderEngld OrderSal OrderSalld Calendar
Свойства DBF-поля Имя 1Cреквизита ПриказПрием Строка из 15 символов " Числовое поле формата 5.0 ПриказОклад Строка из 13 символов " Числовое поле формата 5.0 Календарь Строка из 9 символов
Описание Имеет значение ПриказОПриеме Номер приказа о приеме на работу Имеет значение ИзменениеОклада Номер приказа об изменении оклада Идентификатор календаря
Очевидно, что, имея файлы employee.dbf и salary.dbf, можно полностью восстано вить справочник Сотрудники_2. Правда, восстановленный справочник не будет точ ной копией оригинала. 9.3.1.2.
ПРОЦЕДУРЫ ПЕРЕНОСА
ДАННЫХ
Во-первых, нужно по известной структуре справочника Сотрудники_2 и по изло женным правилам преобразования данных создать файлы employee.dbf и salary.dbf. Предварительно, однако, мы в процедуре АтрСвойства сформируем таблицу значений, содержащую преобразованное в соответствии с табл. 9.5 описание атрибутов (рекви зитов) справочника Сотрудники_2. Для этого нам придется воспользоваться объектом Метаданные. Запустим процедуру АтрСвойства из обработки Проба и просмотрим п олученный результат. процедура АтрСвойства(тЗнач) далее процедура ЕщеОднаСтрока(тЗнач, видСпр, типРек, ин, кон) далее процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем тЗнач; ОчиститьОкноСообщений(); // Формируем таблицу с описанием реквизитов справочника Сотрудники_2 АтрСвойства(тЗнач); // Просмотр таблицы значений. Результат см. на рис. 9.3 тЗнач.ВыбратьСтроку(, "Преобразованные реквизиты справочника Сотрудники_2"); конецПроцедуры // Выполнить // Возвращает таблицу значений, содержащую преобразованное описание // атрибутов справочника Сотрудники_2 процедура АтрСвойства(тЗнач) перем видСпр; тЗнач = СоздатьОбъект("ТаблицаЗначений"); видСпр = "Сотрудники_2"; // Вид справочника // Формируем столбцы таблицы значений тЗнач.НоваяКолонка("Идентификатор", "Строка"); тЗнач.НоваяКолонка("Тип", "Строка"); тЗнач.НоваяКолонка("Длина", "Число"); тЗнач.НоваяКолонка("Точность", "Число"); тЗнач.НоваяКолонка("Периодический"," Число"); // Запишем в таблицу значений идентификатор, тип, длину и точность реквизитов // справочника Сотрудники_2. Длина записывается для символьных и числовых // реквизитов, а точность - только для числовых // Два первых реквизита - это атрибуты Код и Наименование справочника
тЗнач.НоваяСтрока(); // Добавляем новую строку в таблицу значений // Атрибут Код тЗнач.Идентификатор = "Код"; тЗнач.Тип = Метаданные.Справочник(видСпр).ТипКода; тЗнач.Длина = Метаданные.Справочник(видСпр).ДлинаКода; тЗнач.Точность = 0; тЗнач.Периодический = 0; тЗнач.НоваяСтрока(); // Еще одна новая строка // Атрибут Наименование тЗнач.Идентификатор = "Наименование"; тЗнач.Тип = "Строка"; тЗнач.Длина = Метаданные.Справочник(видСпр).ДлинаНаименования; тЗнач.Точность = 0; тЗнач.Периодический = 0; // Реквизиты справочника Сотрудники_2 // Напоминаем, что реквизиты типа Справочник и Документ порождают 2 DBF-поля, // поэтому для них в тЗнач выделяем две строки для ин = 1 по Метаданные.Справочник(видСпр).Реквизит() цикл // Тип реквизита типРек = Метаданные.Справочник(видСпр).Реквизит(ин).Тип; // Очередная новая строка ЕщеОднаСтрока(тЗнач, видСпр, типРек, ин, ""); если (типРек = "Справочник") или (типРек = "Документ") тогда // Очередная новая строка ЕщеОднаСтрока(тЗнач, видСпр, типРек, ин, "2"); конецЕсли; конецЦикла; конецПроцедуры // АтрСвойства // Добавляет новую строку в таблицу значений тЗнач процедура ЕщеОднаСтрока(тЗнач, видСпр, типРек, ин, кон) перем обВид; // Разновидность типа объекта // кон = "", если добавляется первая строка для справочника или документа // кон = "2", если добавляется вторая строка для справочника или документа тЗнач.НоваяСтрока(); если (типРек = "Число") или (типРек = "Строка") или (типРек = "Дата") тогда тЗнач.Идентификатор = Метаданные.Справочник(видСпр).Реквизит(ин).Идентификатор; тЗнач.Тип = типРек; тЗнач.Длина = Метаданные.Справочник(видСпр).Реквизит(ин).Длина; иначе // Справочник, Документ или Календарь если типРек = "Календарь" тогда тЗнач.Идентификатор = Метаданные.Справочник(видСпр).Реквизит(ин).Идентификатор; иначе обВид = Метаданные.Справочник(видСпр).Реквизит(ин).Вид; тЗнач.Идентификатор = обВид + кон; конецЕсли;
если кон = "" тогда тЗнач.Тип - "Строка"; тЗнач.Длина = СтрДлина(тЗнач.Идентификатор); иначе // кон = 2 тЗнач.Тип = "Число"; если типРек = "Справочник" тогда тЗнач.Длина = Метаданные.Справочник(обВид).ДлинаКода; иначе // типРек = "Документ" тЗнач.Длина = Метаданные.Документ(обВид).ДлинаНомера; конецЕсли; конецЕсли; конецЕсли; тЗнач.Точность = Метаданные.Справочник(видСпр).Реквизит(ин).Точность; тЗнач.Периодический = Метаданные.Справочник(видСпр).Реквизит(ин).Периодический; конецПроцедуры // ЕщеОднаСтрока
Рис. 9.3. Результат работы процедуры АтрСвойства Замечания: 1.
Процедуры АтрСвойства и ЕщеОднаСтрока показывают, что объект Метаданные обеспечивает доступ к значению любого компонента структуры объекта агрегат ного типа.
2.
Примеры структур метаданных системы приведены в прил. 1.
Теперь таблица тЗнач содержит практически все необходимые данные для созда ния файла employee.dbf. Осталось только заменить значения столбца Идентификатор на соответствующие имена полей файла employee.dbf. Выполним эти замены в проце дуре ДляДБФ. В процедуре СоздатьПоляДБФ подготовим, пользуясь данными табли цы значений тЗнач, поля для файла employee.dbf, а в процедуре СоздатьСотр сформи руем файл employee.dbf и его индексы. Перенос данных в файл employee.dbf выполнит процедура ПеренестиСотр, обраб атывающая справочник Сотрудники_2 по следующему алгоритму: 1. 2. 3. 4. 5.
Начало. Выполнить выборку элементов справочника Сотрудники_2. Для каждого элемента найти переносимые в DBF-поля значения. Создать новую запись в файле employee.dbf и перенести в нее найденные в п. 3 значения. Конец.
Для выбранного сотрудника пп. 3 и 4 алгоритма реализует следующий код: дбф.Добавить(); дбф.Id = сСотр_2.Код; дбф.ParentId= сСотр_2.Родитель.Код; дбф.IsGroup = сСотр_2.ЭтоГруппа(); дбф.Name = сСотр_2.Наименование; дбф.Ed = сСотр_2.0бразование.Вид(); дбф.EdId = сСотр_2.Образование.Код; дбф.QrderEng = сСотр_2.ПриказПрием.Вид(); дбф.OrderEngId = сСотр_2.ПриказПрием.НомерДок; дбф.OrderSal = сСотр_2.ПриказОклад.Вид(); дбф.OrderSalId = сСотр_2.ПриказОклад.НомерДок; дбф.Calendar = сСотр_2.Календарь.Вид(); дбф.3аписать(); Создание файла salary.dbf осуществит процедура СоздатьОклад, принимающая объект дбф2, содержащий поля файла, сформированные в процедуре СоздатьПоляДБФ. Предварительно, однако, мы, как и в случае файла employee.dbf, сформируем в процедуре ДляДБФ2 таблицу значений тЗнач, на основании которой процедурой СоздатьПоляДБФ будут созданы поля файла salary.dbf. Заполнение файла salary.dbf выполним в процедуре ПеренестиОклад по схеме, реализованной в примере 3 разд. 6.1. Процедура ПеренестиОклад вызывается для вы бранного сотрудника в процедуре ПеренестиСотр. Итоговый код переноса будет таким: процедура АтрСвойства(тЗнач) далее процедура ЕщеОднаСтрока(тЗнач, видСпр, типРек, ин, кон) далее процедура ДляДБФ(тЗнач) далее процедура СоздатьПоляДБФ(тЗнач, дбф, пРекв) далее процедура СоздатьСотр(дбф) далее процедура ДляДБФ2(тЗнач) далее процедура СоздатьОклад(дбф) далее процедура ПеренестиСотр(дбф, дбф2) далее процедура ПеренестиОклад(сотр, оп, дбф) далее процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем тЗнач, дбф, дбф2; ОчиститьОкноСообщений(); // Формируем таблицу с описанием реквизитов справочника Сотрудники_2 АтрСвойства(тЗнач); // Готовим таблицу значений тЗнач для процедуры создания файла employee.dbf ДляДБФ(тЗнач); // Просмотр таблицы значений. Результат см. на рис. 9.4 тЗнач.ВыбратьСтроку(, "Свойства полей файла employee.dbf); СоздатьПоляДБФ(тЗнач, дбф, 0); // Создаем поля файла employee.dbf СоздатьСотр(дбф); // Создаем файл employee.dbf ДляДБФ2(тЗнач); // Просмотр таблицы значений. Результат см. на рис. 9.5 тЗнач.ВыбратьСтроку(, "Свойства полей файла salary.dbf); СоздатьПоляДБФ(тЗнач, дбф2, 1); // Создаем поля
файла
salary.dbf
// Готовим таблицу значений тЗнач для процедуры создания файла salary.dbf СоздатьОклад(дбф2); // Создаем файл salary.dbf // Перенос данных в файлы employee.dbf и salary.dbf. // Вызывает процедуру ПеренестиОклад. Результаты см. на рис. 9.6 и 9.7 ПеренестиСотр(дбф, дбф2); Предупреждение("Готово."); конецПроцедуры // Выполнить // Код процедур АтрСвойства и ЕщеОднаСтрока см. выше // Заменяет в таблице значений тЗнач данные столбца Идентификатор, // на соответствующие имена поле файла employee.dbf. Вставляет также две // строки для полей Parentld и IsGroup файла employee.dbf процедура ДляДБФ(тЗнач) перем сЗнач, ин, поз, имяПоля, номСтроки, колСтрок; // Список значений сЗнач содержит перечень соответствий между именами // DBF-полей файла employee.dbf и значениями столбца Идентификатор таблицы тЗнач сЗнач = СоздатьОбъект("СписокЗначений"); сЗнач.ДобавитьЗначение("Код", "Id"); сЗнач.ДобавитьЗначение("Наименование", "Name"); сЗнач.ДобавитьЗначение("Оклад", "Salary"); сЗнач.ДобавитьЗначение("Образование_2", "Ed"); сЗнач.ДобавитьЗначение("Образование_22"; "Edld"); сЗнач.ДобавитьЗначение("ПриказОПриеме", "OrderEng"); сЗнач.ДобавитьЗначение("ПриказОПриеме2", "OrderEngld"); сЗнач.ДобавитьЗначение("ИзменениеОклада", "OrderSal"); сЗнач.ДобавитьЗначение("ИзменениеОклада2", "OrderSalld"); сЗнач.ДобавитьЗначение("Календарь", "Calendar"); тЗнач.ВыбратьСтроки(); // Заносим вместо имеющегося в столбце Идентификатор значения имя DBF-поля пока тЗнач.ПолучитьСтроку() = 1 цикл поз = сЗнач.НайтиЗначение(тЗнач.Идентификатор); если поз > 0 тогда сЗнач.ПолучитьЗначение(поз, имяПоля); тЗнач.Идентификатор = имяПоля; конецЕсли; конецЦикла; // пока // Добавляем данные для двух полей файла employee.dbf- ParentID и IsGroup, // а затем располагаем добавленные строки вслед за первой строкой таблицы тЗнач тЗнач.НоваяСтрока(); тЗнач.Идентификатор = "ParentID"; тЗнач.Тип = "Число"; // Длина поля ParentID равна длине поля ID тЗнач.НайтиЗначение("Id", номСтроки, 1); тЗнач.Длина = тЗнач.ПолучитьЗначение(номСтроки, 3); тЗнач.Точность = 0; тЗнач.Периодический = 0; тЗнач.НоваяСтрока(); тЗнач.Идентификатор = "IsGroup";
тЗнач.Тип = "Логический"; тЗнач.Длина= 1; тЗнач.Точность - 0; тЗнач.Периодический = 0; // Сдвигаем новые строки вверх колСтрок = тЗнач.КоличествоСтрок(); тЗнач.СдвинутьСтроку(2 - колСтрок, колСтрок); тЗнач.СдвинутьСтроку(2 - колСтрок, колСтрок); конецПроцедурЫ // ДляДБФ // Создает на основе таблицы значений тЗнач поля DBF-файл процедура СоздатьПоляДБФ(тЗнач, дбф, пРекв) // Формальный параметр пРекв = 1, если создается поле для периодического реквизита, // или пРекв = 0 - в противном случае перем имяПоля, типПоля; дбф = СоздатьОбъект("ХВаse"); тЗнач.ВыбратьСтроки(); // Каждая строка таблицы значений тЗнач, отвечающая непериодическому реквизиту // справочника Сотрудники2, порождает одно DBF-поле пока тЗнач.ПолучитьСтроку() = 1 цикл // Значения периодических реквизитов размещаются в отдельной таблице // (в нашем случае речь идет о периодическом реквизите Оклад) если (тЗнач.Периодический = 1) и (пРекв = 0) тогда продолжить; // Пропускаем периодический реквизит конецЕсли; имяПоля = тЗнач.Идентификатор; если (тЗнач.Тип = "Число") или (тЗнач.Тип = "Числовой") тогда типПоля = "N"; иначеЕсли тЗнач.Тип = "Строка" тогда типПоля = "S"; иначеЕсли тЗнач.Тип = "Дата" тогда типПоля = "D"; иначеЕсли тЗнач.Тип = "Логический" тогда типПоля = "L"; конецЕсли; дбф.ДобавитьПоле(имяПоля, типПоля, тЗнач.Длина, тЗнач.Точность); конецЦикла; // пока конецПроцедуры // СоздатьПоляДБФ процедура СоздатьСотр(дбф) // Создает файл employee.dbf // Создаем два индекса // Индекс Name обеспечит сортировку по значению поля Name дбф.ДобавитьИндекс("Name", "Name", 0, 0,""); // Индекс Depart обеспечить сортировку по группам (поле Parentld), // а в пределах групп по полю Name дбф.ДобавитьИндекс("Depart", "trim(str(ParentId)) + Name", 0, 0, ""); дбф.СоздатьФайл("employee.dbf, "employee.cdx"); конецПроцедуры // СоздатьСотр
// Готовит таблицу значений тЗнач для создания файл salary.dbf процедура ДляДБФ2(тЗнач) перем сЗнач, ин, значен, предст, номСтроки, тЗнач2, колСтрок; // Модифицируем таблицу значений тЗнач в соответствии с табл. 9.2 //Для этого создадим список значений сЗнач, содержащий соответствия между // старыми и новыми значениями поля Идентификатор таблицы тЗнач // Строки таблицы значений тЗнач, не отраженные в списке сЗнач, удаляются сЗнач = СоздатьОбъект("СписокЗначений"); сЗнач.ДобавитьЗначение("Id", "Id"); сЗнач.ДобавитьЗначение("OrderSal", "DocName"); сЗнач.ДобавитьЗначение("OrderSalId", "DocId"); сЗнач.ДобавитьЗначение("Salary", "Salary"); // Перемещаем строки, отраженные в списке сЗнач, в верх таблицы значений тЗнач // и делаем предписанные списком сЗнач замены имен для ин = 1 по сЗнач.РазмерСписка() цикл значен = сЗнач.ПолучитьЗначение(ин, предст); номСтроки = 0; тЗнач.НайтиЗначение(значен, номСтроки, 1); // Изменения значения поля Идентификатор в соответствии со списком сЗнач тЗнач.УстановитьЗначение(номСтроки, 1, предст); тЗнач.СдвинутьСтроку(ин - номСтроки, номСтроки); конецЦикла; // для // Выгружаем строки, отраженные в списке сЗнач, в таблицу тЗнач2 тЗнач2 = СоздатьОбъект("ТаблицаЗначений"); тЗнач.Выгрузить(тЗнач2, 1, сЗнач.РазмерСписка()); // Переносим таблицу тЗнач2 в таблицу тЗнач т3нач.3агрузить(т3нач2); тЗнач2 = 0; // Более таблица значений тЗнач2 не нужна // Добавляем в тЗнач строку для даты и перемещаем ее на вторую позицию таблицы тЗнач.НоваяСтрока(); // Добавляем новую строку в таблицу значений тЗнач.Идентификатор = "Date"; тЗнач.Тип = "Дата"; тЗнач.Длина = 0; тЗнач.Точность = 0; тЗнач.Периодический = 0; // Ставим строку с датой на вторую позицию колСтрок = тЗнач.КоличествоСтрок(); тЗнач.СдвинутьСтроку(2 - колСтрок, колСтрок); конецПроцедуры // ДляДБФ2 процедура СоздатьОклад(дбф) // Создает файл salary.dbf // Создаем один индекс // Индекс IdDate обеспечит сортировку по дате в пределах заданного кода сотрудника дбф.ДобавитьИндекс("IdDate", "str(Id) + dtos(Date)", 0, 0,""); дбф.СоздатьФайл("salary.dbf, "salary.cdx"); конецПроцедуры // СоздатьОклад // Заполняет файл employee.dbf и вызывает процедуру ПеренестиОклад, которая добавляет //данные в файл salary.dbf для выбранного сотрудника процедура ПеренестиСотр(дбф, дбф2) перем сСотр_2, оп;
// Создаем объект типа Периодический для работы с файлом оп = СоздатьОбъект("Периодический"); сСотр_2 = СоздатьОбъект("Справочник.Сотрудники_2"); сСотр_2.ВыбратьЭлементы(); пока сСотр_2.ПолучитьЭлемент() = 1 цикл дбф.Добавить(); // Новая запись в файле employee.dbf дбф.Id = сСотр_2.Код; дбф.ParentId = сСотр_2.Родитель.Код; дбф.IsGroup = сСотр_2.ЭтоГруппа(); дбф.Name = сСотр_2.Наименование; дбф.Ed = сСотр_2.0бразование.Вид(); дбф.EdId = сСотр_2.0бразование.Код; дбф.OrderEng = сСотр_2.ПриказПрием.Вид(); дбф.OrderEngId = сСотр_2.ПриказПрием.НомерДок; дбф.OrderSal = сСотр_2.ПриказОклад.Вид(); дбф.OrderSalId = сСотр_2.ПриказОклад.НомерДок; дбф.Calendar = сСотр_2.Календарь.Вид(); дбф.3аписать(); // Сохраняем данные // Если выбран сотрудник, а не группа если сСотр_2.ЭтоГруппа() = 0 тогда // Добавляем данные об окладе текущего сотрудника в файл salary.dbf ПеренестиОклад(сСотр_2, оп, дбф2); конецЕсли; конецЦикла // пока конецПроцедуры // ПеренестиСотр // Добавляет для сотрудника сотр записи в файл salary.dbf процедура ПеренестиОклад(сотр, оп, дбф) перем док; // Прикрепляем ОП к периодическому реквизиту Оклад найденного сотрудника оп.ИспользоватьОбъект("Оклад", сотр.ТекущийЭлемент()); // Позиционируемся перед первой записью истории окладов сотрудника оп.ВыбратьЗначения(); // Метод ПолучитьЗначение позиционирует ОП на следующей записи // периодического реквизита Оклад пока оп.ПолучитьЗначение() = 1 цикл дбф.Добавить(); // Новая запись в файле salary.dbf дбф.Id = сотр.Код; // Код сотрудника дбф.Salary = оп.Значение; // Значение и ДатаЗнач- атрибуты ОП дбф.Date = оп.ДатаЗнач; // Документ, вызвавший изменение оклада док = оп.ТекущийДокумент(); если док.Выбран() = 1 тогда дбф.DocName = док.Вид(); дбф.DocId = док.НомерДок; иначе дбф.DocName = ""; дбф.DocId = 0; конецЕсли; дбф.3аписать( ); // Сохраняем данные конецЦикла; // пока конецПроцедуры // ПеренестиОклад
Рис. 9.4. Таблица тЗнач, преобразованная в процедуре ДляДБФ
Рис. 9.5. Таблица тЗнач, преобразованная в процедуре ДляДБФ2
Рис. 9.6. Фрагмент файла employee.dbf
Рис. 9.7. Фрагмент файла salary.dbf
Замечания: 1. Поскольку путь к файлам не указан, то они будут созданы в каталоге пользовате ля, из которого запускается обработка Проба. Имя каталога возвращается встроен ной функцией КаталогПользователя. 2. С файлом, на который ссылается объект XBase, может одновременно работать только один пользователь. Поэтому если есть не равная нулю вероятность одно временного доступа к одному и тому же DBF-файлу более одного пользователя, то с объектом XBase нужно соответствующим образом использовать конструкцию Попытка.
9.3.2. АТРИБУТЫ И МЕТОДЫ ОБЪЕКТА XBASE 9.3.2.1. АТРИБУТЫ ОБЪЕКТА XBASE Объект XBase имеет два вида атрибутов: поля DBF-файла и его ключи. Атрибут Поле позволяет по имени поля читать и изменять значение поля текущей записи DBF-файла. Число таких атрибутов равно числу полей файла, на который ссы лается объект XBase. Пример. В окне сообщений выводится значение поля Name 3-й записи файла е т ployee.dbf. В соответствии с рис. 9.6 в этом поле хранится строка Агальцов Юрий Алексеевич. Затем меняется значение 6-й записи файла employee.dbf. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф, ин; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВазе"); дбф.ОткрытьФайл("emplоуее.dbf"); если дбф.Открыта( ) = 0 тогда Предупреждение("Файла employee.dbf в каталоге пользователя нет."); возврат; конецЕсли; // Перемещаемся на третью запись файла employee.dbf // После того как файл открыт, он позиционируется на своей первой записи дбф.Перейти(3); , Сообщить(дбф.Name); // Напечатает Агальцов Юрий Алексеевич // Перемещаемся на шестую запись файла employee.dbf дбф.Перейти(6); дбф-Name = "Бараненков Иван Несторович"; дбф.3аписать( ); // Не забываем записать изменения в файл Предупреждение("Готово."); конецПроцедуры // Выполнить Замечание. Файл employee.dbf изменялся при закрытом индексном файле етployee.cdx, поэтому индексы этого файла, в которые входит в качестве выражения имя поля Name, не отвечают в полной мере файлу employee.dbf. Следовательно, для поиска по индексу файл employee.cdx необходимо предварительно переиндексировать. Атрибут Ключ является объектом агрегатного типа. Число компонентов объекта равно числу полей DBF-файла, на который ссылается объект XBase. Имя компонента атрибута Ключ составляется по следующей схеме: дбф.Ключ.<имя поля DBF-файла> Атрибут применяется для задания значений, употребляемых при вычислении ин дексного выражения. Полученное значение индексного выражения используется мето дом НайтиПоКлючу в качестве ключа поиска. Этот метод осуществляет поиск по те кущему индексу, применяя в качестве ключа выражение, составленное из значений компонентов атрибута Ключ. Например, с файлом employee.dbf используется индекс
Depart, вычисляемый по выражению TRIM(STR(ParentId)) + Name. Чтобы задать это выражение в программе 1С, необходимо определить два компонента ключа: К л ю ч . P a r e n t I d и Ключ.Name. Пример 1. Задаются ключи, обеспечивающие получение значения индексного вы ражения индекса Depart, равное 12Бараненков. дбф.Ключ.ParentId = 12; // Значение для TRIM(STR(ParentId)) дбф.Ключ.Name = "Бараненков"; // Значение для Name // Индексное выражение TRIM(STR(12)) + "Бараненков" вернет 12Бараненков Пример 2. В файле employee.dbf ищется по индексу Name, индексное выражение которого равно Name (см. процедуру СоздатьСотр в разд. 9.3.1.2), ключ, равный Агальцов Юрий Алексеевич. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перемдбф; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаве"); дбф.ОткрытьФайл("етр1оуее.dbf", "employee.cdx"); если дбф.Открыта() = 0 тогда Предупреждение("Файла employee.dbf и/или файла employee.cdx | в каталоге пользователя нет."); возврат; конецЕсли; дбф.ТекущийИндекс("Name"); дбф.Ключ.Name = "Агальцов Юрий Алексеевич"; дбф.НайтиПоКлючу(1); Сообщить(дбф.Name); // Напечатает Агальцов Юрий Алексеевич конецПроцедуры // Выполнить Замечание. При таком использовании метода НайтиПоКлючу можно задать не полное значение ключа, например дбф.Ключ.Name = "Ага"; дбф.НайтиПоКлючу(1); Сообщить(дбф.Name);
// Напечатает Агальцов Юрий Алексеевич
Пример 3. В файле employee.dbf ищется по индексу Depart, индексное выражение ко торого равно TRIM(STR(ParentId)) + Name (см. процедуру СоздатьСотр в разд. 9.3.1.2), Ключ, равный TRIM((STR(12)) + "Бараненков". Процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВasе"); дбф.ОткрытьФайл("етр1оуее.dbf, "employee.cdx"); если дбф.Открыта() = 0 тогда Предупреждение("Файла employee.dbf и/или файла employee.cdx | в каталоге пользователя нет."); возврат;
дбф.ТекущийИндекс("Depart"); дбф.Ключ.ParentId = 12; //Значение для TRIM(STR(ParentId)) дбф.Ключ.Name = "Бараненков"; // Значение для Name дбф.НайтиПоКлючу(1); // Ключ равен 12Бараненков Сообщить(дбф.Name); // Напечатает Бараненков Иван Несторович конецПроцедуры // Выполнить
9.3.2.2. МЕТОДЫ ОБЪЕКТА XBASE Приводятся в табл. 9.6. Таблица 9.6 Методы объекта XBase Метод
Описание
дбф.СоздатьФайл (DBFфайл, [СDХфайл]);
Создает файл базы данных (DBF-файл) и, если задан второй параметр, индексный файл (CDX-файл). Имена файлов задаются символьными параметрами DBFфайл и СDХфайл. Если имя не содержит пути, то файлы будут созданы в каталоге пользователя. Если файлы с указанными именами на диске имеются, то они будут заменены на новые
дбф.ОткрытьФайл (DBFфайл, [СDХфайл], [флагЧтения]);
Открывает DBF-файл и, если задан второй параметр, индексный файл. Тип параметров DBFфайл и СDХфайл символьный. Файлы открываются только для чтения, если флагЧтения = 1, и доступны для редактирования, если флагЧтения = 0. По умолчанию флагЧтения = 0
флаг = дбф.Открыта();
Вернет 1, если DBF-файл открыт, или 0-в противном случае Закрывает ранее открытый или созданный DBF-файл. Игнорируется, если DBF-файл не был открыт или создан до употребления метода. DBF-файл при нормальном завершении программы будет закрыт и без вызова метода
дбф.ЗакрытьФайл();
дбф.ОчиститьФайл();
Выполняет физическое удаление всех записей DBF-файла. После выполнения метода удаленные записи восстановить нельзя
дбф.Сжать( );
Удаляет физически записи DBF-файла, имеющие пометку удаления Выполняет переиндексацию индексного файла. Применяется для восстановления порядка следования записей в индексном файле, который может быть нарушен при аварийном завершении программы или при модификации DBF-файла при закрытом индексном файле Устанавливает режим доступа к записям, имеющим пометку удаления. Если режим = 1, то такие записи доступны, и недоступны, если режим = 0. По умолчанию режим = 0. Возвращает текущее значение режима
дбф.Переиндексировать();
режимТек = дбф.Показывать Удаленные ([режим]);
Метод флаг = дбф.Первая();
флаг = дбф.Последняя();
флаг = дбф.Следующая();
флаг = дбф.Предыдущая();
нЗап = дбф.НомерЗаписи(); дбф.Перейти(нЗап);
флаг = дбф.ВКонце(); флаг = дбф.ВНачале(); имяИндТек =дбф.Текущий Индекс([ИмяИнд]); флаг = дбф.Найти (ключ, [режим]);
Описание Перемещает DBF-файл на первую запись. Если задан текущий индекс, то первой является запись, отвечающая при возрастающем индексе его наименьшему значению или наибольшему - при убывающем индексе. Вернет 1 в случае успеха или 0 при неудаче Перемещает DBF-файл на последнюю запись. Если задан текущий индекс, то последней является запись, отвечающая при возрастающем индексе его наибольшему значению или наименьшему - при убывающем индексе. Вернет 1 в случае успеха или 0 при неудаче Перемещает DBF-файл на следующую запись. Если задан текущий индекс, то порядок записей определяется этим индексом. Вернет 1 в случае успеха или 0 при неудаче Перемещает DBF-файл на предшествующую запись. Если задан текущий индекс, то порядок записей определяется этим индексом. Вернет 1 в случае успеха или 0 при неудаче Возвращает номер записи DBF-файла. Вернет 0, если файл позиционирован перед первой или вслед за последней записью Перемещает DBF-файл на запись, имеющую номер нЗап. Методу всегда доступны записи, имеющие пометку удаления. Если нЗап < 0 или нЗап > дбф.КоличествоЗаписей(), то возникнет информационная ошибка Вернет 1, если DBF-файл находится вслед за своей последней записью, или О-в противном случае Вернет 1, если DBF-файл находится перед своей первой записью, или О-в противном случае Устанавливает, если задан параметр имяИнд, текущий индекс. Возвращает значение текущего индекса до вызова метода. Параметр имяИнд и результат имеют символьный тип Ищет ключ, используя текущий индекс. Вернет 1 в случае успеха или 0 при неудаче. При успешном поиске позиционирует файл на найденной записи. Если числовой параметр режим есть: • 0, то ищется запись, индексное выражение которой точно равно ключу; • 1, то ищется запись, индексное выражение которой больше или равно ключу; • 2, то ищется запись, индексное выражение которой больше ключа; • -1, то ищется запись, индексное выражение которой меньше или равно ключу; • -2, то ищется запись, индексное выражение которой меньше ключа. По умолчанию режим = 0. Используется для простых индексов
Метод
Описание
флаг = дбф.НайтиПоКлючу ([режим]);
Ищет, используя текущий индекс, ключ, заданный компонентами атрибута Ключ. Описание метода такое же, как и метода Найти. Используется для любых индексов
значение = дбф.Получить ЗначениеПоля (имяПоля| номерПоля);
Возвращает значение текущей записи в поле, заданным параметром имяПоля|номерПоля. Параметр имяПоля - это строка с именем поля. Параметр номерПоля - это номер поля. Возникнет информационная ошибка, если имяПоля (номерПоля) содержит несуществующее имя (несуществующий номер) или если файл находится перед первой или за последней записью
дбф.УстановитьЗначение Поля(имяПоля| номерПоля, значение);
Устанавливает значение текущей записи в поле, заданным параметром имяПоля|номерПоля. Дальнейшую информацию о методе см. в описании метода ПолучитьЗначениеПоля. Для сохранения изменений, если не задан режим автосохранения, применяется метод Записать
дбф.Добавить();
Добавляет пустую запись. Для ее сохранения в DBF-файле применяется, если не задан режим автосохранения, метод Записать
дбф.Скопировать();
Добавляет запись, которая является копией текущей записи. Для сохранения добавленной записи в DBF-файле применяется, если не задан режим автосохранения, метод Записать
режимТек = дбф.АвтоСохранение ([режим]);
Задает, если режим = 1, автоматическое сохранение (АС) измененных записей базы данных. В этом случае применение метода Записать избыточно. Для отмены изменений применяется метод Отменить. Если режим = 0, АС измененных записей не выполняется; изменения сохраняются методом Записать. Возвращает существовавший до вызова метода режим АС
дбф.3аписать( );
Заносит изменения текущей записи в базу данных. Если метод не применен и не задано АС, смена позиции файла или его закрытие приведет к потере введенных данных
дбф.Отменить();
Отменяет запись в базу данных изменений, выполненных в режиме автосохранения. Не отменяет действие метода Записать
дбф.Удалить();
Проставляет пометку удаления текущей записи
флаг = дбф.ЗаписьУдалена();
Возвращает 1, если запись имеет пометку удаления, или 0 в противном случае
дбф.Восстановить();
Снимает пометку удаления текущей записи
дбф.Очистить();
Очищает текущую запись, то есть в символьное поле заносится строка нулевой длины, в числовое - 0, в логичес кое - .F., а в поле типа Дата - пустая дата. Изменения сохраняются, если задан режим автосохранения или если вслед за методом Очистить применен метод Записать
числоЗап = дбф.КоличествоЗаписей();
Возвращает число записей в DBF-файле
Метод числоПолей = дбф.Количество Полей(); числоИнд = дбф.Количество Индексов();
Описание Возвращает число полей DBF-файла Возвращает число индексов открытого индексного файла
дбф.ОписаниеПоля Возвращает для поля, имеющего номер, равный параметру (номерПоля, номерПоля, имя, тип, длину иточность поля в соответст имяПоля, тип, длина, вующие параметры метода точндсть); дбф.ОписаниеИндекса (номерИнд, имяИнд, выражение, уник, убыв, фильтр);
Возвращает для индекса, имеющего номер, равный параметру номерИнд, имя, индексное выражение, флаги уникальности и убывания, а также фильтр в соответствующие параметры метода. Параметр уник равен единице, если индекс уникальный, или нулю - в противном случае. Параметр убыв равен единице, если индекс сортируется по убыванию значений индексного выражения, и равен нулю, если по возрастанию
номПоля = дбф.НомерПоля (имяПоля);
Возвращает номер поля по его имени
дбф.ДобавитьПоле(ИмяПоля, тип, длина, точность);
Добавляет в структуру DBF-файла поле, имя которого определено символьным параметром имяПоля, а тип, длина и точность задаются соответствующими параметрами метода. Параметр точность имеет смысл только для числового поля. Параметр тип равен: •
1 или "N", если добавляется числовое поле;
•
2 или "S", если добавляется символьное поле;
•
3 или "D", если добавляется поле типа Дата;
•
4 или "L", если добавляется логическое поле;
• 5 или "F", если добавляется числовое поле. После добавления всех полей употребляется метод СоздатьФайл дбф.ДобавитьИндекс Добавляет в структуру индексного файла индекс, имя (имяИнд, выражение, которого определено символьным параметром имяИнд, уник, убыв, фильтр); а индексное выражение, флаги уникальности, убывания и фильтр индекса задаются соответствующими параметрами метода. Параметры выражение и фильтр имеют символьный тип. Смысл параметров уник и убыв см. в методе ОписаниеИндекса. После добавления всех индексов употребляется метод СоздатьФайл или СоздатьИндексныйФайл дбф.СоздатьИндексныйФайл (имяФайла);
Создает индексный файл имяФайла, содержащий все индексы, сконструированные до вызова метода СоздатьИндексныйФайл методом ДобавитьИндекс. Тип параметра имяФайла - символьный. Также индексный файл может быть создан и методом СоздатьФайл
Метод
Описание
Устанавливает, если кодСтр = 0, Windows-кодовую страницу кодСтрТек - дбф.Кодовая Страница([кодСтр]); или DOS-кодовую страницу, если кодСтр = 1. Возвращает существовавшую до вызова метода кодовую страницу. По умолчанию кодСтр = 1 код = дбф.КодОшибки();
Возвращает код последней ошибки, возникшей при исполнении метода объекта XBase
Замечания: 1. Префикс дбф, употребленный с методами объекта XBase, может быть произвольным. 2.
Фильтр индекса является логическим выражением, составленным по правилам, изложенным в разд. 9.3.1.3. В индексном файле, если фильтр задан, есть ссылка только на те записи DBF-файла, для которых выражение фильтра истинно. Тогда, если индекс с фильтром является текущим, записи, на которых нет ссылок в и ндексном файле, пользователю недоступны. Отсутствие фильтра равнозначно фильтру, возвращающему для всех записей .Т. (истина).
Пример 1. Выводятся коды (табельные номера) всех сотрудников, имеющих оклад, равный 2900 руб. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаsе"); дбф.ОткрытьФайл("Salary.dbf"); если дбф.Открыта() = 0 тогда Предупреждение("Файла salary.dbf в каталоге пользователя нет."); возврат; конецЕсли; // Переход в начало файла salary.dbf; в данном примере метод может быть опущен, // поскольку после открытия файл и так находится на своей первой записи дбф.Первая(); пока дбф.ВКонце() = 0 цикл если дбф.Salary = 2900 тогда Сообщить(дбф.Id); конецЕсли; дбф.Следующая(); конецЦикла; // пока конецПроцедуры // Выполнить Результат: 111 209 Пример 2. Выводятся ФИО всех сотрудников, фамилия которых начинается с буквы Б. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаse"); дбф.ОткрытьФайл("emploуее.dbf", "employee.cdx");
если дбф.Открыта( ) = 0 тогда Предупреждение("Файла employee.dbf и/или файла employee.cdx | в каталоге пользователя нет."); возврат; конецЕсли; дбф.ТекущийИндекс("Name"); // Ищем запись, равную или большую символа "Б" если дбф.Найти("Б", 1) = 1 тогда пока Лев(дбф.Name, 1) = "Б" цикл Сообщить(дбф.Name); дбф.Следующая(); конецЦикла; // пока иначе Предупреждение ("Сотрудников, фамилия которых начинается на букву Б, в файле нет."); конецЕсли; конецПроцедуры // Выполнить Результат: Бараненков Иван Несторович Безверхний Игорь Петрович Пример 3. Оклад сотрудника, табельный номер (код) которого равен 209, увеличи вается на 500 руб. Для нового оклада в файл salary.dbf добавляется запись. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф, окл; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаse"); дбф.ОткрытьФайл("salary.dbf"," salary.cdx"); если дбф.Открыта() = 0 тогда Предупреждение("Файла salary.dbf и/или файла salary.cdx | в каталоге пользователя нет."); возврат; конецЕсли; дбф.ТекущийИндекс("IdDate"); дбф.Ключ.Id = 209; // Ищем запись, большую str(209) // Помним, что индексное выражение - это STR(Id) + DTOS(Date) если дбф.НайтиПоКлючу(2) = 1 тогда // Перемещаемся на последнее значение оклада сотрудника с кодом 209 пока дбф.Id = 209 цикл дбф.Следующая(); конецЦикла; // пока дбф.Предыдущая(); // Шаг назад окл = дбф.ПолучитьЗначениеПоля("Salary"); // или, используя атрибут: окл = дбф.Salary; дбф.Добавить(); дбф.УстановитьЗначениеПоля("Id", 209); дбф.УстановитьЗначениеПоля("Date", ТекущаяДата()); дбф.УстановитьЗначениеПоля("Salary", окл + 500);
// или, используя атрибуты: // дбф.Id= 209; дбф.Date = ТекущаяДата(); дбф.Salary = окл + 500; дбф.3аписать(); Предупреждение("Готово."); иначе Предупреждение("Сотрудника с кодом 209 в файле нет."); конецЕсли; конецПроцедуры // Выполнить Пример 4. Копируется и записывается в файл salary.dbf его последняя запись. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаse"); дбф.ОткрытьФайл("salary.dbf"); если дбф.Открыта() = 0 тогда Предупреждение("Файла salary.dbf в каталоге пользователя нет."); возврат; конецЕсли; дбф.АвтоСохранение(1); // Будем обходиться без метода Записать дбф.Последняя(); . // Переход на последнюю запись дбф.Скопировать( ); // Добавляем копию последней записи Предупреждение("Готово."); конецПроцедуры // Выполнить Пример 5. Выводятся описания всех полей и индексов файла salary.dbf. процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф, ин, имяПоля, тип, длина, точность; перем имяИнд, выражение, уник, убыв, фильтр; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаse"); дбф.ОткрытьФайл("salary.dbf," salary.cdx"); если дбф.Открыта( ) = 0 тогда Предупреждение("Файла salary.dbf и/или файла salary.cdx | в каталоге пользователя нет."); возврат; конецЕсли; Сообщить("Описание полей файла salary.dbf:"); для ин = 1 по дбф.КоличествоПолей() цикл дбф.ОписаниеПоля(ин, имяПоля, тип, длина, точность); Сообщить(имяПоля + " " + тип + " " + длина + "" + точность); конецЦикла; // для Сообщить("Описание индексов файла salary.dbf:"); для ин = 1 по дбф.КоличествоИндексов( ) цикл дбф.ОписаниеИндекса(ин, имяИнд, выражение, уник, убыв, фильтр); Сообщить(имяИнд + " " + выражение + " " + уник + " " + убыв + " " + фильтр); конецЦикла; // для конецПроцедуры // Выполнить
Результат: Описание полей файла salary.dbf: ID 1 50 DATE 3 8 0 DOCNAME 2 15 0 DOCID 1 5 0 SALARY 1 10 2 Описание индексов файла salary.dbf: IDDATE STR(id)+DTOS(date) 0 0 Пример 6. Содержимое произвольного DBF-файла выводится в таблицу значений. Диалог формы, содержащей таблицу значений, оформим в соответствии с рис. 9.8.
Рис. 9.8. Таблица значений для записей DBF-файла В заголовке таблицы значений будем выводить значение выражения "Состав файла " + имяФайла которое зададим в диалоге как формулу элемента диалога Текст. // Вводим переменную модуля для ее использования в диалоге перем имяФайла; процедура ПоказатьДБФ(дбф, имяФайла) далее процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф, флаг, папка; ОчиститьОкноСообщений(); // Открываем диалог для выбора DBF-файла // В качестве примера выберем файл employee.dbf флаг = ФС.ВыбратьФайл(0, имяФайла, папка, "Выберите файл"," | *.DBF"); если флаг = 0 тогда возврат; конецЕсли; дбф = СоздатьОбъект("ХВаsе"); дбф.ОткрытьФайл(папка + имяФайла); если дбф.Открыта() = 0 тогда Предупреждение("Не удалось открыть файл " + имяФайла); возврат; конецЕсли; // Отобразим DBF-файл имяФайла в таблице значений // Результат приведен на рис. 9.9 ПоказатьДБФ(дбф, имяФайла); конецПроцедуры // Выполнить
// Выводит записи DBF-файла имяФайла в таблицу значений процедура ПоказатьДБФ(дбф, имяФайла) перем ин, имяПоля, тип, длина, точность, номСтроки; // Формируем столбцы таблицы значений и устанавливаем их параметры для ин = 1 по дбф.КоличествоПолей() цикл дбф.ОписаниеПоля(ин, имяПоля, тип, длина, точность); тЗнач.НоваяКолонка(имяПоля, тип); тЗнач.УстановитьПараметрыКолонки(ин, тип, длина, точность,, мин(10, длина)); конецЦикла; // для // Заголовок таблицы значений диалога загТЗнач = "Состав файла " + имяФайла; // Заполняем таблицу значений тЗнач данными из DBF-файла дбф.Первая(); // Переходим на первую запись файла номСтроки = 0; пока дбф.ВКонце() = 0 цикл // Пока не достигнут конец файла номСтроки = номСтроки + 1; тЗнач.НоваяСтрока(номСтроки); для ин = 1 по дбф.КоличествоПолей() цикл тЗнач.УстановитьЗначение(номСтроки, ин, дбф.ПолучитьЗначениеПоля(ин)); конецЦикла; // для дбф.Следующая(); конецЦикла; // пока конецПроцедуры // ПоказатьДБФ
Рис. 9.9. Файл employee.dbf в таблице значений Замечание. Подобного рода процедуру всегда удобно иметь под рукой для опера тивного просмотра используемых DBF-файлов. Напомним, что таблицу значений так же можно отобразить, применив метод ВыбратьСтроку. Пример 7. Выводятся коды сотрудников, оклад которых больше 3000 руб. Для по лучения результата в дополнение к существующему создается индекс с выражением Salary и фильтром Salary > 3000. перем имяФайла; процедура Выполнить() // Связана с кнопкой Пуск обработки Проба перем дбф, флаг, папка; ОчиститьОкноСообщений(); дбф = СоздатьОбъект("ХВаsе"); дбф.ОткрытьФайл("salary.dbf"); если дбф.Открыта( ) = 0 тогда Предупреждение("Файла salary.dbf в каталоге пользователя нет."); возврат; конецЕсли;
// Индекс IdDate обеспечит сортировку по дате в пределах заданного кода сотрудника дбф.ДобавитьИндекс("IdDate", "str(Id) + dtos(Date)", 0, 0, ""); // Индекс Salary обеспечит сортировку по окладу дбф.ДобавитьИндекс("Salary", "Salary", 0, 0, "Salary > 3000"); дбф.СоздатьИндексныйФайл("Salary.cdx"); дбф.ТекущийИндекс("Salary"); дбф.Первая(); // Переход в начало файла // Вывод результата. Доступны только те записи, у которых Salary > 3000 Сообщить("Коды сотрудников, оклад которых более 3000 руб."); пока дбф.ВКонце() = 0 цикл // Пока не достигнут конец файла Сообщить(Строка(дбф.Id) + Символ Табуляции + "(оклад сотрудника равен " + Строка(дбф.Salary) + " руб.)"); дбф.Следующая(); конецЦикла; // пока имяФайла = "salary.dbf"; ПоказатьДБФ(дбф, имяФайла); // Код процедуры ПоказатьДБФ см. выше конецПроцедуры Результат работы процедуры ПоказатьДБФ см. на рис. 9.10. Результат: Коды сотрудников, оклад которых более 3000 руб. 301 (оклад сотрудника равен 3100 руб.) 122 (оклад сотрудника равен 3200 руб.) 2010 (оклад сотрудника равен 3200 руб.) 302 (оклад сотрудника равен 3200 руб.) 303 (оклад сотрудника равен 3200 руб.)
Рис. 9.10. Файл salary.dbf под действием снабженного фильтром индекса Salary 9.3.2.3.
ВЫРАЖЕНИЯ ИНДЕКСА
И
ФИЛЬТРА
Выражения, употребляемые в методе -ДобавитьИндекс, для индекса и фильтра, мо гут содержать в качестве операндов имена полей, константы и вызовы функций. Аргу ментами функций, применяемых в выражениях, могут также быть имена полей, кон станты и вызовы, функций. Например, в методе дбф.ДобавитьИндекс("Depart", "trim(str(ParentId)) + Name", 0, 0,""); индексное выражение содержит функции TRIM, STR и имена полей DBF-файла ParentId и Name, а в методе дбф.ДобавитьИндекс("Salary", "Salary", 0, 0, "Salary > 3000"); выражение фильтра включает имя поля Salary и числовую константу 3000.
В качестве логических констант используются .Т. и .F. для обозначения соответст венно констант истина и ложь. Константы типа Дата задаются в фигурных скобках. Регистр написания операндов выражения не имеет значения. Выражение индекса может иметь числовой, символьный, логический тип или тип Дата. Выражение фильтра, если задано, должно иметь логический тип. В выражениях индекса и фильтра могут применяться приведенные в табл. 9.7 опе рации (они расположены в таблице в порядке убывания их приоритета; операции с од ним приоритетом размещаются в одной строке таблицы). Таблица 9.7 Операции выражений индекса и фильтра Операции ** или ^ *,/ +,+ =, <> или #,<,>,<=,>= $ .NOT. .AND. .OR.
Значения Арифметические операции Унарный минус Возведение в степень Умножение, деление Сложение, вычитание Символьные операции Объединение (конкатенация) Объединение 2 (конкатенация 2) Операции отношения Равно, не равно, меньше, больше, меньше или равно, больше или равно, содержит Логические операции Логическое НЕ (отрицание) Логическое И Логическое ИЛИ
Замечания: 1. Все операции, кроме унарного минуса, являются двуместными (бинарными), то есть употребляются между двумя операндами. 2. Выражения вычисляются слева направо в порядке, определяемом приоритетом операций. Часть подвыражения, заключенная в круглые скобки, вычисляется в первую очередь. Более подробно о вычислении выражений см. в разд. 2.6. 3. Унарный минус имеет больший приоритет, чем иные арифметические (и другие) операции. Например, выражение -2**2 вернет, так же как и выражение (-2)**2 число 4.
4. Символьная операция -, в отличие от операции +, перемещает завершающие пробе лы первого операнда в конец результата, например, выражение "3 п "+"робела" вернет строку "3 п робела", а выражение "3 п " - "робела" - строку "3 пробела ". 5. Операция $ применяется только с символьными операндами. Выражение отноше ния с операцией $ вернет .Т., если первый операнд выражения является подстрокой второго, или .F. - в противном случае. Например выражение "БВ" $"АБВГ" вернет .Т., а выражение "БГ" $ "АБВГ " вернет .F.. 6. В отличие от 1С в выражениях индекса и фильтра нельзя смешивать символьные и арифметические операнды. Так, вместо "АБС" + 2 чтобы получить строку "АБС2", нужно употребить выражение "АБС" + trim(str(2)) или выражение "АБС" + str(2', 1) В выражениях индекса и фильтра используются приведенные в табл. 9.8 функции. Таблица 9.8 Функции для выражений индекса и фильтра Функция DATE() DAY(дama) DTOC(дama)
Что возвращает
Текущую дату, например 01/14/02 День месяца, заданного параметром дата Символьное представление даты, заданной параметром дата DTOS(дama) Символьное представление даты, заданной параметром дата, в виде ГГГГММДД IIF(лВыр, выр1, выр2) Результат выражения выр1, если истинно логическое выражение лВыр, или результат выражения выр2 в противном случае. Выражения выр1 и выр2 могут быть произвольного типа LTRIM(cтрокa) Строку, в которой удалены ведущие пробелы параметра строка
Тип результата Дата Число Строка " Совпадает с типом выр1 или выр2 Строка
Тип результата
Что возвращает
Функция MONTH(дaтa)
Номер месяца в году, заданного параметром дата
Число
STOD(cmpoкa)
Дату в формате ГПТММДД
Дата
STR(число, [длина], [десСим])
Строка Символьное представление параметра число в виде строки длиной длина, содержащей десСим десятич ных символов. Если параметры длина и десСим опущены, то результирующая строка имеет 10 сим волов и не содержит десятичной части. Если опущен параметр десСим, то результат также не содержит десятичной части
SUBSTR(строка, начПоз, [числоСим])
Подстроку параметра строка, начинающуюся с позиции начПоз и содержащую числоСим последующих символов. Если последний параметр опущен, то результатом будут все символы от позиции начПоз до конца строки строка
"
TIME()
Текущее время в формате ЧЧ:ММ:СС
"
TRIM(строка)
Строку, в которой удалены завершающие пробелы параметра строка
"
UPPER(строка)
Строку, преобразованную в верхний регистр
"
VAL(строка)
Числовое представление параметра строка
Число
YEAR(дата)
Номер года, заданного параметром дата
"
Замечание. Параметры функций являются выражениями. Причем параметр дата является выражением типа Дата; параметр строка - символьное выражение; парамет ры число, длина, десСим, начПоз и числоСим - числовые выражения. Примеры употребления функций приведены в табл. 9.9. Таблица 9.9 Примеры вызова функций для выражений индекса и фильтра Результат
Пример вызова
Тип
day(date())
14
Числовой
month(date())
1
year(date())
2002
" "
time()
16:08:46
dtoc(date())
01/14/02
Символьный "
dtos(date())
20020114
"
k=5 IIF(k > 8, 2 * 3, "A" + "B")
6
Числовой
k=9 IIF(k > 8, 2 * 3, "A" + "B") ltrim(" ABC")
AB
Символьный
ABC ABCD
" "
trim("
ABC
") + "D"
Тип
Результат
Пример вызова
substr("Небосклон", 3, 3)
бос
substr("Hебосклон", 5)
склон
upper("Небосклон")
НЕБОСКЛОН
" " " " " "
va1("Небосклон")
0
Числовой
val("-1.23")
-1.23
val("+432")
432
val("1.3e+2")
130
" " "
str(5.234) str(5.234, 5) str(5.234, 6, 4)
5 5 5.2340
9.4. ВЫВОДЫ 1. Объект XBase подсоединяется к DBF-файлу в режиме монопольного доступа. 2. Чтобы индексный файл соответствовал своему DBF-файлу, необходимо открывать первый вместе с DBF-файлом. Тогда изменения в DBF-файле автоматически вызо вут необходимые изменения индексного файла. 3. Если какое-то время DBF-файл редактировался при закрытом индексном файле, то, открывая последний, следует выполнить его переиндексацию. 4. Атрибут Ключ объекта XBase используется для задания операндов индексного выражения. 5. Для поиска по составному ключу следует использовать метод НайтиПоКлючу вза мен метода Найти. 6. Для просмотра находящихся в работе DBF-файлов полезно иметь процедуру, пе реносящую записи файла в таблицу значений. Вывод таблицы значений можно осуществить как в одноименный элемент диалога, так и в окно, открываемое ме тодом ВыбратьСтроку.
ПРИЛОЖЕНИЕ 1. ПРИМЕРЫ СТРУКТУР МЕТАДАННЫХ Структура метаданных отображается в текстовом файле, если, находясь в конфи гурации, в колонке Конфигурация выбрать пункт Описание структуры метаданных. Появляющийся в результате текст будет весьма обширным, но для пояснения его уст ройства вполне достаточно привести несколько фрагментов. Так, для созданной нами константы к5 файл будет содержать следующую инфор мацию: - Константа - Идентификатор - Синоним - Комментарий - Тип -Вид - Длина - Точность - Неотрицательный - РазделятьТриады -Периодический - ОбластьРаспространения
"к5" "Коэффициент к5" "Для вычисления премии 1234" "Число" "5" "2" "0" "0" "1" "ВсеИнформационныеБазы"
Если бы константа имела агрегатный тип специального назначения (разд. 3.1), то для нее было бы определено значение компонента Вид. Например, константа ГлБухгалтер имеет разновидность типа Справочник.Сотрудники_2, то есть ее компо нент Вид равен Сотрудники_2. Способ доступа к компонентам константы посредством объекта метаданные рас смотрен в разд. 1.7.3. Справочник Сотрудники_2 имеет следующую структуру метаданных: - Справочник - Идентификатор - Синоним - Комментарий - Владелец - КоличествоУровней - ДлинаКода - ДлинаНаименования - СерииКодов - ТипКода - ОсновноеПредставление - КонтрольУникальности - АвтоНумерация - ГруппыВпереди - СпособРедактирования - ЕдинаяФормаЭлемента
"Сотрудники_2" "Сотрудники" "Простой справочник" "3" "5" "30" "ВПределахПодчинения" "Числовой" "ВВидеНаименования" "1" "2" "1" "ОбоимиСпособами" "0"
- ОсновнаяФорма "Справочник.Сотрудники_2.ФормаСписка.ФормаСписка" - ОсновнаяФормаДляВыбора "Справочник.Сотрудники_2.ФормаСписка.ФормаДляВыбора" - ОбластьРаспространения "ВсеИнформационныеБазы" - АвтоРегистрация " 1" - ДополнительныеКодыИБ - Реквизит - Идентификатор "Оклад" - Синоним "Оклад сотрудника" - Комментарий "Меняется документом ИзменениеОклада" - Тип "Число" - Вид -Длина "10" - Точность "2" - Неотрицательный "1" -РазделятьТриады "1" -Периодический "1" - Сортировка "0" - Отбор "0" - РучноеИзменение "1" - ИзменяетсяДокументами "1" - Использование "ДляЭлемента" - Реквизит - Идентификатор "Образование" - Синоним "Образование" - Комментарий "Выбирается из справочника Образование" - Тип "Справочник" - Вид "Образование_2" - Длина "0" - Точность "0" - Неотрицательный "0" - РазделятьТриады "0" - Периодический "0" -Сортировка "1" -Отбор "1" -РучноеИзменение "1" - ИзменяетсяДокументами "0" - Использование "ДляЭлемента" - Реквизит - Идентификатор "ПриказОклад" - Синоним "Приказ об изменении оклада" - Комментарий "Ссылка на документ о новом окладе" - Тип "Документ" - Вид "ИзменениеОклада" -Длина "0" - Точность "0" - Неотрицательный "0" - РазделятьТриады "0" - Периодический "0"
- Сортировка - Отбор - РучноеИзменение - ИзменяетсяДокументами - Использование -Реквизит - Идентификатор - Синоним - Комментарий - Тип - Вид - Длина - Точность - Неотрицательный - РазделятьТриады - Периодический - Сортировка - Отбор -РучноеИзменение - ИзменяетсяДокументами - Использование - Реквизит - Идентификатор - Синоним - Комментарий - Тип -Вид - Длина - Точность - Неотрицательный - РазделятьТриады - Периодический - Сортировка - Отбор -РучноеИзменение - ИзменяетсяДокументами - Использование - ФормаСписка - Идентификатор - Синоним - Комментарий - ФормаСписка - Идентификатор - Синоним - Комментарий
"0" "0" "1" "0" "ДляЭлемента" "ПриказПрием" "Приказ о зачислении в штат" "Ссылка на документ ПриказОПриеме" "Документ" "ПриказОПриеме" "0" "0" "0" "0" "0" "0" "0" "1" "0" "ДляЭлемента" "Календарь" "Календарь" "Ссылка на объект типа Календарь" "Календарь" "0" "0" "0" "0" "0" "0" "0" "1" "0" "ДляЭлемента" "ФормаСписка" "В форме запрещен выбор групп" "ФормаДляВыбора" "Форма для выбора" "В форме разрешен выбор групп"
Все компоненты структуры объекта Справочник.Сотрудники_2 можно прочитать, употребив объект Метаданные, например так, как это выполнено в разд. 9.3.1.2.
Документ ИзменениеОклада представлен в метаданных следующим образом: - Документ - Идентификатор - Синоним - Комментарий - Журнал - ПериодичностьНомера - ДлинаНомера - ТипНомера - АвтоНумерация - КонтрольУникальности - Нумератор - ОперативныйУчет -Расчет - БухгалтерскийУчет - СоздаватьОперацию - АвтоНумерацияСтрок - АвтоудалениеДвижений - РедактированиеОпераций - РазрешитьПроведение - ВводимыеНаОснованииДокументы - ОснованиеДляЛюбогоДокумента - ОбластьРаспространения - АвтоРегистрация - ДополнительныеКодыИБ - РеквизитШапки - Идентификатор - Синоним - Комментарий - Тип -Вид - Длина - Точность - Неотрицательный - РазделятьТриады - РеквизитТабличнойЧасти - Идентификатор - Синоним - Комментарий - Тип - Вид - Длина - Точность - Неотрицательный - РазделятьТриады - ИтогПоКолонке - РеквизитТабличнойЧасти - Идентификатор - Синоним
"ИзменениеОклада" "Приказ о новом окладе" "Можно выпустить для группы сотрудников" "Журнал.ПриказыКадровые" "Все" "5" "Числовой" "2" "1" "0" "1" "0" "1" "1" "0" "1" "0" "ВсеИнформационныеБазы" " 1"
"ДатаНовОклада" "Дата нового оклада" "Дата, с которой вступает в силу новый оклад" "Дата" "0" "0" "0" "0" "Сотрудник" "Сотрудник" "Ссылка на справочник Сотрудники_2" "Справочник" "Сотрудники_2" "0" "0" "0" "0" "0" "ПрежнийОклад" "Действующий оклад"
- Комментарий - Тип -Вид -Длина - Точность -Неотрицательный - РазделятьТриады - ИтогПоКолонке - РеквизитТабличнойЧасти - Идентификатор - Синоним - Комментарий - Тип -Вид -Длина - Точность -Неотрицательный - РазделятьТриады - ИтогПоКолонке
"Носит осведомительный характер" "Число" "10" "2" "1" "1" "0" "НовыйОклад" "Новый оклад" "Оклад, назначенный настоящим приказом" "Число" "10" "2" "1" " 1" "0"
Журналы документов характеризуются следующими данными (на примере журна ла Табель): - Журнал - Идентификатор "Табель" - Синоним "Табель" - Комментарий "Журнал документов учета рабочего времени" - ТипЖурнала "Обычный" - Состав - ОсновнаяФорма "Журнал.Табель.Форма.ФормаСписка" - ОсновнаяФормаДляВыбора "Журнал.Табель.Форма.ФормаСписка" - ДляЖурналаПодчиненныхДокументов "0" - ДляПолногоЖурнала "0" - ФормаСписка - Идентификатор "ФормаСписка" - Синоним - Комментарий "Содержит графы Дата, Время, Документ и Номер" Структуру метаданных перечислений представим на примере введенного нами в разд. 7.3.3 перечисления В Р 2 : - Перечисление - Идентификатор - Синоним - Комментарий - Значение - Идентификатор - Комментарий - Представление - Значение - Идентификатор - Комментарий - Представление
"ВР_2" "Выбор расчета" "Документы, вводящие расчеты с разными ВР" "НачСальдо" "Расчеты с ВР НачСальдо_2" "Начальное сальдо" "Табель" "Расчеты с ВР Оклад_2, НДФЛ2 и ВБанк_2" "Расчеты Оклад/Тариф, НДФЛ и ВБанк"
- Значение - Идентификатор - Комментарий -Представление
"Премия" "ПремияКоэф_2, ПремияСум_2 и Премия 1234_2" "Премия коэффициентом, суммой и 1234"
Метаданные для журнала Зарплата_2: - ЖурналРасчетов - Идентификатор - Синоним - Комментарий - ОсновнойСправочник - ОсновнаяФорма - ОсновнаяФормаДляВыбора - РазмерПериода - ДатаОтсчета - ДлинаРезультата - ТочностьРезультата - ТипГрафОтбора - Реквизит - Идентификатор - Синоним - Комментарий - Тип -Вид - Длина - Точность - Неотрицательный - РазделятьТриады - Реквизит - Идентификатор - Синоним - Комментарий - Тип - Вид - Длина - Точность - Неотрицательный - РазделятьТриады - Реквизит - Идентификатор - Синоним - Комментарий - Тип -Вид . -Длина - Точность - Неотрицательный - РазделятьТриады - ФормаСписка - Идентификатор - Синоним - Комментарий
"Зарплата_2" "Журнал заработной платы" "Расчеты сотрудников из справочника Сотрудники_2" "Справочник.Сотрудники_2" "ЖурналРасчетов.Зарплата_2.Форма.ФормаСписка" "ЖурналРасчетов.Зарплата_2.Форма.ФормаСписка" "Месяц" "20010101" "10" "2" "НаКонецПериода" "всегоЧасов" "Отработанные часы" "Число часов, отработанных сотрудником в РП" "Число" "5" "1" "1" "0" "хозОп" "Хозоперация" "Введена для бухучета зарплаты" "Справочник" "ХозяйственнаяОперация" "0" "0" "0" "0" "строкаДок" "Номер строки документа" "Номер строки документа, породившего расчет" "Число" "5" "0" "0" "0" "ФормаСписка" "Данные расчета помещаются в одной строке"
Относительно правил перерасчета метаданные сообщают следующее (на примере правила перерасчета Премия 12342): - ПравилоПерерасчета - Идентификатор -Синоним - Комментарий - ТипПравила - КоличествоПериодов - ВедущийВидРасчета
"Премия1234_2" "Правило перерасчета Премия 1234_2" "Правило для премии 1234" "СледующийПериод" " 1" "ВидРасчета.Оклад_2" "ВидРасчета.ПремияКоэф_2" "ВидРасчета.ПремияСум_2"
- ПодчиненныйВидРасчета "ВидРасчета.Премия1234_2" Виды расчетов представляются следующими метаданными: - ВидРасчета - Идентификатор - Синоним - Комментарий - Очередность - ВытесняетВидРасчета
"ПремияКоэф_2" "Премия коэффициентом" "Премия, зависящая от отработанного времени" "5" "ВидРасчета.ПремияКоэф_2"
- ВытесняетсяВидомРасчета "ВидРасчета.ПремияКоэф_2" "ВидРасчета.ПремияСум_2" - ГруппаРасчетов "ГруппаРасчетов.ВсеНачисления_2" Группы ВР описываются так: - ГруппаРасчетов - Идентификатор - Синоним - Комментарий - ВидРасчета
"ВсеНачисления_2" "Все начисления" "Для сотрудников из справочника Сотрудники_2" "ВидРасчета.Оклад_2" "ВидРасчета.ПремияКоэф_2" "ВидРасчета.ПремияСум_2" "ВидРасчета.Премия1234_2"
Распечатки подобного рода лицу, сопровождающему систему, полезно иметь в пределах досягаемости, например для анализа структур объектов с целью их после дующего усовершенствования. Используя приведенные сведения, вы сможете прочитать и употребить по назна чению необходимые значения свойств объектов. Изменить значения подавляющего большинства метаданных можно, лишь находясь в конфигурации, в интерактивном режиме. Исключение составляют правила перерасчета: отражаемые в метаданных свойства правил можно редактировать соответствующими методами (разд. 7.16).
ПРИЛОЖЕНИЕ 2. ИНСТРУКЦИЯ ПО РАБОТЕ С ДИСКЕТОЙ Прилагаемая к пособию дискета содержит архивный файл lSConfig.zip, включающий файлы информационной базы и файлы внешних обработок в папке ExtForms. Кроме того, в папке DBF вы найдете файлы bal.dbf и bal.cdx, которые мы исполь зовали для загрузки начального сальдо (разд. 7.4.6.), а также файлы employee.dbf, employee.cdx и salary.dbf, salary.cdx, в которые мы сбросили данные справочника Сотрудники_2 (разд. 9.3.1). Папка ТХТ содержит текстовые файлы, загружаемые из модулей созданных нами объектов: обработок, справочников, журналов и документов. Для загрузки в модуле объекта используется, например, такая команда (разд. 1.11): #ЗагрузитьИзФайла d:\l cv77\Test\TXT\измОкл.txt При необходимости код из текстовых файлов можно перенести в модули соответ ствующих объектов. В папку BMP помещен файл p.bmp, содержащий последовательность пиктограмм для таблицы значений (см. пример 8 из разд. 3.4.4). В директорию USRDEF записаны сведения о зарегистрированных пользователях (разд. 1.3). Порядок использования дискеты таков: 1) в папке 1CV77, содержащей систему 1С:Предприятие версии 7.7, создайте дирек торию Test; 2) скопируйте с дискеты файл lSConfig.zip и извлеките из него имеющиеся в нем данные, используя опции архиватора WinZip, представленные на рис. П-2.1.
Рис. П-2.1. Извлекаем архивные файлы 3) убедитесь, что в папке Test есть директории ExtForms, DBF, TXT, BMP и USRDEF; при отсутствии повторите процедуру разархивации;
4) загружая 1С, зарегистрируйте информационную базу (рис. П-2.2);
Рис. П-2.2. Первая загрузка 1С с новой информационной базой 5) при авторизации доступа выбирается имя пользователя Ученик; пароль отсутствует; 6) в загруженной системе доступны все представленные в книге объекты. В пособии примеры, сопровождающие новый материал, запускались из обра ботки Проба, которая модифицировалась в соответствии с решаемой задачей. Чи тателю, начинающему изучение языка, полезно самостоятельно выполнить необ ходимые для запуска примеров действия. В то же время для оперативного запуска примеров в папке ExtForms читатель найдет обработки, содержащие эти примеры. При этом программы, не использующие переменные диалога (за исключением ука занных ниже процедур), размещены в обработке Проба. Все программы, кроме од ной, предназначенной для исполнения, должны быть закомментированы. (Напом ним, что для управления комментарием в 1С имеются иконки // и //Х, которые ра ботают с выделенным текстом.) Каждая программа модуля обработки Проба начинается текстом, содержащим ссылку на раздел, откуда программа взята, например такую: // Пример из разд. 1.4.2. ПЕРВАЯ ПРОГРАММА В разд. 1.11 указано, что переход от одной программы к другой быстрее вы полнить, если все программы разместить в текстовом файле, загружаемом в обра ботку командой #ЗагрузитьИзФайла d:\lcv77\Test\TXT\npoбa.txt Эта возможность реализована в обработке Проба. Поэтому нет необходимости от крывать ее в конфигураторе, а достаточно выполнить надлежащие изменения в файле пpoбa.txt, открыв его в 1С:Предприятии. Напомним, что .изменения возымеют эффект после их записи на диск и повторного вызова обработки, для чего у нас есть соответ ствующий пункт меню или ускоритель Alt+1. В файл Проба.txt не включен ряд программ, изменяющих состояние системы. На пример, в нем нет процедуры из разд. 7.2, выполняющей смену расчетного периода, процедур из разд. 7.4.5 удаления записей ЖЗ и мягкой смены расчетного периода, про цедур и функций из разд. 7.4.6, выполняющих загрузку начального сальдо из DBFфайла, и т. д. Замечание. Из-за большого числа строк в файле пpoбa.txt его компиляция может занять продолжительное время. Его можно уменьшить либо разбив файл на несколько частей, либо удалив не представляющий интереса код.
Отдельно в обработке Проба_1.ert размещена процедура Выполнить из разд. 5.12.2, Демонстрирующая общие методы справочников. Примеры, использующие помимо кнопок Пуск и Закрыть иные элементы диалога, демонстрируются обработками, список которых представлен в табл. П-2.1. Таблица П-2.1 Обработки для примеров пособия Имя обработки
Что демонстрирует
Проба.ert
Большинство программ, не использующих переменные диалога. Запускает из меню интерфейса Ученик в результате выбора Проба Пуск или Alt+1
Проба_1.ert
Общие методы справочников (процедура Выполнить из разд. 5.12.2)
Проба_2.ert
Пример из разд. 1.5. Диалог с числовым полем приведен на рис. 1.20
Проба_3.ert
Список непериодических констант в таблице значений (пример из разд. 1.8)
Проба_4.ert
Отчет со списком непериодических констант (пример из разд. 1.9)
Открыть.ert
Пример из разд. 1.12. Запускает из меню интерфейса Ученик в результате выбора Проба - Открыть или Alt+5
Открыть_2.ert, Проба_5.ert
Контекст обработки Проба_5, загружаемой из обработки Открыть_2. Модифицированный пример из разд. 1.13
Проба_6.ert
Список значений как элемент диалога (пример из разд. 3.3.4)
Проба_7.ert
Редактирование таблицы значений в диалоге (пример из разд. 3.4.2)
Проба_8.ert
Использование пиктограмм в таблице значений (пример 8 из разд. 3.4.4)
Проба_9.ert
Отчет о сотрудниках по справочнику Сотрудники_2 (пример из разд. 5.11.1)
Проба_10.ert
Тот же отчет, что и в npo6a_9.ert, но с запросом (пример из разд. 5.11.2)
Проба_11.ert
Данные запроса о суммарных результатах по хозяйственным операциям (программа из разд. 7.17.4.)
Проба_12.ert
То же, но с выводом данных о составе групп (программа из разд. 7.17.4)
Проба_13.ert
Данные о суммарных результатах по хозяйственным операциям возвращаются не запросом, а функцией сумХозОп2 (программа из разд. 7.17.4)
Проба_14.ert
Процедуры и функции из разд. 9.3.1.2 переноса данных 1С а DBF-файлы
Проба_15.ert
Вывод произвольного DBF-файла в таблицу значений (пример 6 из разд. 9.3.2.2)
Замечания: 1.
Обработку из табл. П-2.1 будет проще открыть, если в меню интерфейса Ученик выбрать Проба - Открыть или использовать Alt+5 и затем работать с приведенным на рис. П-2.3 диалогом.
Рис. П-2.3. Диалог формы, открывающей обработку 2. В некоторых примерах есть ссылки на удаленные объекты (например, в примере из разд. 2.5 есть ссылка на справочник Сотрудники, который затем был заменен на справочник Сотрудники_2; это связано с тем, что написание этих примеров предшествовало устранению объектов из конфигурации - такова логика создания пособия). Чтобы восстановить работоспособность программ, в ссылках на спра вочник имя Сотрудники заменяется на имя Сотрудники_2, имя Зарплата - на Зарплата_2 и т. д.
ЛИТЕРАТУРА 1. 1С:Предприятие 7.7. Описание встроенного языка: В 2 ч. Номер издания 77.001.03, 1999.909 с. 2. Агафонова М. Ю. и др. Большой экономический словарь / Под ред. А. Н. Азрилияна. М.: Институт новой экономики, 1997. 864 с. 3. Бартеньев О. В. Современный Фортран. М.: Диалог-МИФИ, 2000. 448 с. 4. Он же. Фортран для профессионалов. Математическая библиотека IMSL: Ч. 1. М.: Диалог-МИФИ, 2000. 448 с. 5. Майерс Г. Искусство тестирования программ. М.: Финансы и статистика, 1982.176 с. 6. Ленгсам Й., Огенстайн М., Тененбаум А. Структуры данных для персональных ЭВМ. М.: Мир, 1989. 568 с. 7. Любимский Э. 3., Мартынюк В. В., Трифонов Н. П. Программирование. М.: Наука, 1980. 608 с. 8. Microsoft Visual FoxPro. Developer's Guide. Microsoft Corporation, 1995. 496 p.
ОГЛАВЛЕНИЕ Предисловие
3
1. Начальные сведения
5
1.1. Введение
5
1.2. Интерфейс Ученик
5
1.3. Пользователь по имени Ученик 1.4. Обработка по имени Проба 1.4.1. Диалог обработки 1.4.2. Первая программа 1.4.3. Обсуждение первой программы 1.4.4. Модуль обработки Проба 1.5. Переменные диалога
.:
,
8 9 9 12 14 15 18
1.6. Глобальные имена
20
1.7. Константы 1.7.1. Доступ к константам 1.7.2. Непериодические и периодические константы 1.7.3. Примеры работы с непериодическими константами 1.7.4. Методы констант 1.8. Отображение списка констант в диалоговом окне
22 22 23 25 27 29
1.9. Формирование отчета со списком констант
31
1.10. Вывод списка констант в текстовый файл
34
1.11. Загрузка текста модуля из файла 1.12. Как открыть отчет или обработку в программе
36 37
1.13. Контекст обработки Проба
38
1.14. Функции для администратора 1.15. Выводы
42 44
2. Базовые понятия
46
2.1. Объекты и типы данных
46
2.2. Буквальные Константы
47
2.3. Системные Константы
48
2.4. Переменные числовые, символьные и даты
49
2.5. Ввод значений разных типов
50
2.6. Выражения и операции 2.6.1. Выражения 2.6.2. Арифметические операции для числового типа данных 2.6.3. Операции для строк и дат 2.6.4. "Истина" и "ложь" в 1С 2.6.5. Операции отношения 2.6.6. Логические операции 2.6.7. Приоритет выполнения операций 2.7. Массивы
53 53 54 55 55 55 56 56 57
2.8. Встроенные функции для разных типов данных 2.8.1. Встроенные математические функции 2.8.2. Встроенные функции для символьных данных 2.8.3. Встроенные функции для дат и времени 2.8.4. Встроенные функции преобразования типов данных 2.8.5. Форматирование данных 2.8.5.1. Форматирование числовых данных 2.8.5.2. Вывод денежных величин и целых чисел прописью 2.8.5.3. Форматирование символьных данных 2.8.5.4. Форматирование дат 2.8.5.5. Изменение способа вывода чисел прописью 2.9. Управляющие операторы и конструкции.... 2.9.1. Алгоритм как совокупность базовых структур 2.9.2. Ветвления "если" 2.9.3. "Попытка" 2.9.4. Циклы 2.9.5. О вычислении логических выражений 2.9.6. Пример записи алгоритма
59 59 60 61 65 67 68 68 69 70 70 72 72 73 74 76 79 79
2.9.7. Прерывания цикла. Объединение условий 2.9.8. Переход по метке
81 82
2.10. Пользовательские процедуры и функции 2.10.1. Программирование "сверху вниз" 2.10.2. Структура процедур и функций 2.10.3. Параметры процедур и функций 2.10.4. Вложенные вызовы процедур и функций 2.10.5. Оператор Возврат 2.11. Выводы
84 84 84 87 90 91 .....91
3. Агрегатные типы данных общего назначения 3.1. Общие свойства агрегатных типов данных 1С 3.2. Обобщение Пустого значения 3.3. Список значений 3.3.1. Структура списка значений 3.3.2. Пример списка значений 3.3.3. Методы списка значений 3.3.4. Список значений как элемент диалога 3.4. Таблица значений 3.4.1. Атрибуты таблицы значений 3.4.2. Редактирование таблицы значений в диалоге 3.4.3. Текущая строка таблицы значений 3.4.4. Методы таблицы значений 3.5. Выводы
4. Перечисления 4.1. Перечисления 1С 4.2. Перечисление Скидки 4.2.1. Создаем перечисление Скидки 4.2.2. Методы перечислений 4.3. Ввод значений перечисления 4.4. Выводы
93 93 98 99 99 100 103 107 109 109 112 114 115 131
132 132 133 133 135 137 138
6. Объекты типа Периодический 6.1. Порядок использования объекта Периодический
240 240
6.2. Атрибуты объекта Периодический
:242
6.3. Методы объекта Периодический
243
6.4. Просмотр истории периодического объекта
.246
6.5. Выводы
247
7. Журнал и виды расчетов. Календари и табель .... 248 7.1. Журнал расчетов в задаче начисления заработной платы 7.1.1. Понятие журнала расчетов 7.1.2. Пример простого расчета 7.1.3. Некоторые свойства журнала зарплаты и его расчетов. 7.1.4. Мероприятия по созданию журнала зарплаты 7.2. Задание расчетного периода журнала зарплаты
248 248 248 251 251 252
7.3. Виды расчетов 253 7.3.1. Свойства видов расчетов 253 7.3.1.1. Несовместимые виды расчетов. Вытеснение расчета 253 7.3.1.2. Длинные расчеты 254 7.3.2. Перечень видов расчетов предприятия 255 7.3.3. Доступ к документам, вводящим расчеты в журнал зарплаты 256 7.3.4. Порядок ввода и вычисления расчетов журнала зарплаты. Приоритет видов расчетов 257 7.3.5. Добавление видов расчетов в конфигурацию 257 7.3.6. Вид расчета Начальное сальдо 258 7.3.6.1. Свойства вида расчета Начальное сальдо 258 7.3.6.2. Модуль вида расчета Начальное сальдо 259 7.3.6.3. Документ НачПериода_2 261 7.3.7. Справочник хозяйственных операций видов расчетов 264 7.4. Построение журнала зарплата 265 7.4.1. Атрибуты журнала расчетов и его периода 265 7.4.2. Добавление журнала зарплаты в конфигурацию системы 267 7.4.3. Форма списка журнала зарплаты 269
7.4.4. Модуль формы списка журнала зарплаты 7.4.4.1. Процедуры, управляющие элементами диалога 7.4.4.2. Выбор вида расчета для ввода в журнал зарплаты 7.4.4.3. Ввод начального сальдо 7.4.5. Удаление записей журнала зарплаты и мягкая смена расчетного периода 7.4.6. Загрузка начального сальдо из DBF-файла 7.4.7. Отчет по начальному сальдо 7.5. Календари и Праздники
272 273 274 275 277 279 282 286
7.5.1. Создание календаря
...286
7.5.2. Применение календарей и празников 7.5.2.1. Доступ к календарям и праздникам 7.5.2.2. Атрибуты календарей и праздников 7.5.2.3. Методы календарей и праздников 7.5.2.4. Усовершенствование справочника Сотрудники_2 7.6. Табель 7.6.1. Порядок заполнения табеля 7.6.2. Реквизиты и диалог табеля 7.6.3. Модификация меню интерфейса Ученик 7.6.4. Модуль формы списка документа , 7.6.5. Модуль документа 7.6.6. Форма списка журнала документов Табель
287 287 289 290 292 294 294 295 300 300 304 307
7.7. Правила перерасчета видов расчетов
308
7.8. Виды расчетов документа Табель
309
7.8.1. Вид расчета Оклад_2 7.8.2. Вид расчета 7.8.3. Вид расчета ВБанк_2
НДФЛ_2 ;
7.9. Премии 7.9.1. Свойства документа 7.9.2. Диалог формы списка документа 7.9.2.1. Слои диалога 7.9.2.2. Закладки диалога 7.9.2.3. Элементы диалога и их формулы 7.9.3. Модуль формы списка документа 7.9.4. Модуль документа 7.9.5. Форма списка журнала документов Расчеты
309 311 312 312 312 315 315 317 ....319 320 325 328
7.10. Виды расчетов документа Премия 7.10.1. Премия коэффициентом 7.10.2. Премия суммой 7.10.3. Премия 1234 7.11. Взаимодействие вида расчета предприятия
329 329 329 330 330
7.12. Длинные расчеты. Эффект вытеснения расчета
331
7.13. Методы журнала расчетов и его периода
334
7.14. Предопределенные процедуры модуля формы журнала расчетов 7.15. Виды расчетов и их группы 7.15.1. Вывод списков видов расчетов и их групп 7.15.2. Атрибуты видов расчетов и их групп 7.15.3. Методы видов расчетов и их групп 7.16. Атрибуты и методы правил перерасчета 7.16.1. Атрибуты правил перерасчета 7.16.2. Методы правил перерасчета 7.17. Завершаем кодирование Процедур модуля формы списка журнала Зарплата_2 7.17.1. Расчет зарплаты 7.17.2. Формирование расчетного листка 7.17.3. Ведомость перечислений в банк 7.17.4. Сведения для бухгалтерского учета зарплаты 7.18. Первый расчетный период нового года 7.19. Выводы
344 346 346 347 348 349 349 350 352 353 355 359 361 364 365
8. Повышение эффективности функционирования системы... 366 8.1. 8.2. 8.3. 8.4.
Критерии эффективности Увеличение быстродействия Улучщение качества интерфейса Надежность программ
366 366 366 369
8.4.1. Проверка данных 8.4.2. Преодоление конфликтов 8.4.3. Транзакции 8.5. Защита данных 8.5.1. Постановка задачи 8.5.2. Справочник ПраваРасчетчика 8.5.3. Меню и права пользователей 8.5.4. Модификация модуля формы списка журнала Зарплата_2 8.5.5. Модификация модуля формы списка справочника Сотрудники_2 8.6. Сопровождение системы. Обновление конфигурации 8.6.1. Регламентные работы 8.6.2. Инициативные работы 8.6.3. Оптимизация конфигурации и информационной базы 8.6.4. Обновление конфигурации 8.7. Выводы
9. Обмен данными 9.1. Методы сортировки данных 9.1.1. Внешняя и внутренняя сортировка 9.1.2. Понятие ключа 9.1.3. Сортировка таблицы указателей 9.1.4. Сортировка массива методом пузырька 9.1.5. Быстрая сортировка 9.2. Поиск данных 9.2.1. Последовательный поиск 9.2.2. Бинарный поиск 9.2.3. Сравнение последовательного и бинарного поиска 9.3. Передача данных из 1С в DBF-файлы 9.3.1. Экспорт данных из справочника Сотрудники_2
369 370 374 376 376 379 380 381 384 386 386 390 392 395 396
397 397 397 397 398 399 401 402 403 403 ....405 405 405
9.3.1.1. Постановка задачи
405
9.3.1.2. Процедуры переноса данных
407
9.3.2. Атрибуты и методы объекта XBase 9.3.2.1. Атрибуты объекта XBase 9.3.2.2. Методы объекта XBase 9.3.2.3. Выражения индекса и фильтра
9.4. Выводы
Приложение 1. Примеры структур метаданных
416 416 418 427
431
432
Приложение 2. Инструкция по работе с дискетой ..439 Литература
443