Access
Проблемы отчетов в ACCESS Сергей Гущенко,
Важным элементом программ работы с базами данных является механизм "отчетов". Так как структура баз может быть достаточно сложной, для реализации таких программ недостаточно знать основные возможности "конструктора отчетов". Может потребоваться знание ряда слабо документированных функций и технологических приемов
Выходные документы в СУБД проектируются с помощью механизма отчетов. В MS Access этот механизм имеет массу возможностей, позволяющих создавать выходные документы без обращения к программированию на встроенном языке VBA. Однако не все задачи можно решить таким путем.
Рассмотрим пример. Допустим, есть таблица "TELEFKOD" телефонных кодов с 528-ю записями, начало которой имеет следующий вид:
NPP | GOROD | KOD_TEL | OBLAST |
1 | Авдеевка | 06236 | Донецкая |
2 | Акимовка | 06131 | Запорожская |
3 | Александрия | 05235 | Кировоградская |
4 | Александровка | 06269 | Донецкая |
5 | Александровка | 05269 | Кировоградская |
6 | Алушта | 06560 | Крым |
Причем поле "NPP" имеет тип Счетчик (Длинное целое), особенность которого состоит в следующем: при удалении записи в этом поле удаляется и значение, которое больше повторяться не будет. В результате перечень порядковых номеров может не совпасть с перечнем значений в этом поле.
В качестве выходного документа для этой таблицы подготовлен несложный отчет, вид которого в Конструкторе представлен на рис. 1.
В этом отчете названия колонок (шапка таблицы) размещены в верхнем колонтитуле, в качестве номера по порядку используется поле "NPP", в нижнем колонтитуле указывается номер страницы и добавлено Примечание отчета с количеством выводимых записей и примером подписей. Модуль класса пустой (то есть никаких программных кодов на языке VBA нет). Распечатывается такой отчет без проблем. Но могут возникнуть претензии к размещению Примечания. По умолчанию его свойство Не разрывать имеет значение "Да". (рис. 2).
Это означает: если всё Примечание не помещается на последней странице строк с данными, то оно будет целиком перенесено на следующую.
Но не всегда такое решение допустимо - особенно если печатается финансовый документ. Поэтому можно поменять значение этого свойства на "Нет" - тогда Примечание будет начинаться сразу после вывода последней записи. Это решает рассматриваемую проблему, но не до конца. Может возникнуть ситуация, когда одна подпись (например, Директора) окажется на одной странице (последней с данными), а другая перенесется на следующую,- тоже плохо. Более приемлемым решением в этом случае - с небольшой высотой Примечания - было бы его размещение полностью на отдельной странице, но так, чтобы к нему сверху автоматически добавлялись несколько последних строк с данными. И вот тут без обращения к VBA уже не обойтись. Вариантом решения этой задачи может оказаться следующий текст Модуля класса с кодами обработки событий:
Option Compare Database Option Explicit Dim I1 As Integer, KolZap As Integer, KolZ As Integer Dim dd As Database, zap As Recordset,
Private Sub Report_NoData (Cancel As Integer) MsgBox (" А нету записей!") Cancel = True End Sub
Private Sub Report_Open (Cancel As Integer) Set dd = CurrentDb Set zap = dd. OpenRecordset (" TELEFKOD", dbOpenDynaset) If Not zap. BOF Then zap. MoveLast KolZap = zap. RecordCount End If zap. Close KolZ = 0 End Sub
Private Sub ВерхнийКолонтитул_Format (Cancel As Integer, FormatCount As Integer) Me![EndStr1]. Visible = False I1 = 0 End Sub
Private Sub ОбластьДанных_Format (Cancel As Integer, FormatCount As Integer) I1 = I1 + 1 KolZ = Me. CurrentRecord If I1 > 39 Then If KolZap - KolZ < 3 Then Me![EndStr1]. Visible = True End If End If End Sub Во-первых, здесь по ходу дела задействовано интересное свойство Report_NoData - обработка ситуации, когда в таблице-источнике данных нет записей. Текст процедуры почти стандартен, строка Cancel = True приводит к прекращению печати отчета.
При форматировании каждой новой страницы его "видимость" отключается (Me![EndStr1]. Visible = False). Включается же при форматировании Области данных - при наступлении определенного условия, которое состоит из двух частей. Первая контролирует область листа, на котором Примечание уже не может разместиться полностью. В данном отчете опытным путем определено, что такой момент возникает после печати 39-й строки (If I1 > 39 Then). Целочисленной переменной, в которой учитывается номер печатаемой строки, является I1.
Но в этом примере есть и ошибка - итоговое поле для подсчета количества записей в Примечании отчета насчитало 528 записей. А последний Номер по порядку значится как "529". Такая ситуация возможна в случае, если в качестве номера по порядку выводится поле, имеющее тип Счетчик, а в процессе заполнения таблицы были выполнены удаления записей (в примере была удалена одна запись). Поэтому в качестве поля порядкового номера в отчете лучше использовать свободное поле, не связанное с исходной таблицей. В качестве данных для такого поля надо установить значение "=1" и указать вариант Для всего в параметре Сумма с накоплением (рис. 5 - вид в Конструкторе и рис. 6 - при предварительном просмотре).
Это также делается через свободное поле (рис. 7), но его заполнение выполняется в модуле класса. Под него объявляется новая переменная, например: Dim NStrP As Integer. Она должна обнуляться при форматировании верхнего колонтитула и заполняться при форматировании области данных, например:
Private Sub ВерхнийКолонтитул_Format (Cancel As Integer, FormatCount As Integer) ….. NStrP = 0 End Sub Private Sub ОбластьДанных_Format (Cancel As Integer, FormatCount As Integer) ….. NStrP = NStrP + 1 Me![NSTR1] = NStrP End Sub
Последним вариантом учета строк рассмотрим нумерацию в группе. Access позволяет сортировать и группировать данные прямо при выводе. Эти настройки выполняются в Конструкторе через диалоговое окно Сортировка и группировка (вызывается по команде меню Вид) - рис. 8. Для создания группировки по какому-либо полю этого окна, его (поле) надо выбрать в списке (в примере - поле OBLAST) и указать "Да" в параметре Заголовок группы. Здесь же можно установить сортировку для других полей без группировки по ним (в примере - по полю GOROD). Вывод порядкового номера в группе производится в "свободном" поле, в параметре Данные которого указано "=1", но Сумма с накоплением установлена Для группы. Фрагмент результата показан на рис. 9.
Следующей проблемой может оказаться размещение в нижнем колонтитуле промежуточных сумм по какому-либо полю. Такая ситуация часто возникает в финансовых документах - например, при распечатке ведомости на зарплату. На первый взгляд, сделать это можно по технологии, рассмотренной ранее: объявляется переменная, обнуляется при форматировании верхнего колонтитула и заполняется при форматировании области данных. Но поскольку результат должен быть занесен в "свободное" поле, размещенное в другом разделе (в Нижнем колонтитуле), сумма окажется неправильной - ведь процесс форматирования некоторых разделов "отчетов" в Access может повторяться!!! Это является особенностью технологии подготовки "отчетов" - нужно помнить об этом при программировании.
В частности, в рассматриваемом примере заполнение переменной для промежуточной суммы (SumStr) можно выполнять не при форматировании области данных, а при отработке свойства печати области данных (ОбластьДанных_Print), в которой можно проконтролировать ситуацию - будет печататься лист с этими данными или нет. И именно в зависимости от результатов контроля должно быть заполнено поле, расположенное в нижнем колонтитуле. Допустим, оно имеет имя SumS, а переменная, в которой накапливается значение на странице,- SumStr. Тогда текст кода подобной процедуры будет иметь следующий вид:
Private Sub ОбластьДанных_Print (Cancel As Integer, PrintCount As Integer) 'PrintCount = 0 - страница не печатается, ' = 1 - будет печататься SumStr = SumStr + NStrP If PrintCount = 1 Then Me![SumS] = SumStr End Sub
Для решения проблемы команду заполнения поля № строки на странице Подчиненного отчета надо переместить из обработки события Форматирования в обработку события Печать раздела Область данных, например:
Private Sub ОбластьДанных_Print (Cancel As Integer, PrintCount As Integer) NStrP = NStrP + 1 Me![NSTR1] = NStrP SumStr = SumStr + NStrP If PrintCount = 1 Then Me![SumS] = SumStr End Sub