Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Министерство образования Республики Беларусь
Учреждение образования «Белорусский государственный университет информатики и радиоэлектроники»
Факультет компьютерных систем и сетей
Кафедра программного обеспечения информационных технологий
Дисциплина: Основы алгоритмизации и программирование (ОАиП)
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА
к курсовой работе
на тему
Программа для регистрации заявок жилищно-коммунальной службы
БГУИР КП 1-40 01 01 05 ПЗ
Студент: гр. 251001 Выжимко А.И.
Руководитель: асс. Болтак С.В.
Минск 2013
СОДЕРЖАНИЕ
ВВЕДЕНИЕ…………………………………………………………………………5
1.2 Обзор существующих аналогов……………………………………….....12
2. Разработка алгоритма………………………………………………………….14
3. Разработка программного средства ……………………………………….....18
4. Обоснование технических приемов программирования……………………21
5. Тестирование, экспериментальные исследования и анализ полученных результатов………………………………………………………………………...22
6. Руководство пользователя программы ………………………………………25
ЗАКЛЮЧЕНИЕ…………………………………………………………………….29
Список литературы………………………………………………………………..30
Листинг программы……………………………………………………………….31
ВВЕДЕНИЕ
Динамические структуры данных
Статическими величинами называются такие, память под которые выделяется во время компиляции и сохраняется в течение всей работы программы.
Другой способ выделения памяти под данные называется динамическим. В этом случае память под величины отводится во время выполнения программы. Такие величины будем называть динамическими. Раздел оперативной памяти, распределяемый статически, называется статической памятью; динамически распределяемый раздел памяти называется динамической памятью (динамически распределяемой памятью).
Использование динамических величин предоставляет программисту ряд дополнительных возможностей. Во-первых, подключение динамической памяти позволяет увеличить объем обрабатываемых данных. Во-вторых, если потребность в каких-то данных отпала до окончания программы, то занятую ими память можно освободить для другой информации. В-третьих, использование динамической памяти позволяет создавать структуры данных переменного размера.
Следует отчетливо понимать, что работа с динамическими данными замедляет выполнение программы, поскольку доступ к величине происходит в два шага: сначала ищется указатель, затем по нему величина.
Работа с динамическими величинами связана с использованием типа данных ссылочного типа. Величины, имеющие ссылочный тип, называют указателями. Указатель содержит адрес поля в динамической памяти, хранящего величину определенного типа. Сам указатель располагается в статической памяти.
Запись
Запись - это сложный тип данных, позволяющие объединить данные разных типов. Запись можно назвать наиболее общим сложным типом данных. Название "запись" появилось из тех соображений, что данные разного типа можно встретить в таблицах: в каждой строке записаны сразу несколько разных значений. Таким образом, одна запись соответствует одной строке данных: она имеет несколько полей, каждое из которых хранит своё значение.
В данной программе используются записи для хранения информации о книгах. Каждая запись состоит из полей, содержащих следующую информацию: ФИО автора срокового типа, название книги строкового типа, год издания целочисленного типа, указатели на предыдущий и следующий элементы списка типа указатель на данную запись.
Абстрактный тип данных «список»
В математике список представляет собой последовательность элементов определенного типа: а1, а2, …..аn, где n≥0 и все аi имеют один тип. Количество, n длина списка, если n ≥ 1, то а1 первый элемент, а аn последний, в случае n = 0 имеем пустой список.
Списки являются чрезвычайно гибкой структурой: их легко сделать большими или меньшими; их элементы доступны для вставки или удаления в любой позиции списка. Списки можно объединять или разбивать на меньшие списки.
При реализации списков с помощью массивов их элементы располагаются в смежных ячейках массива. Это представление позволяет легко просматривать содержимое списка и вставлять новые элементы в его конец. Однако вставка нового элемента в середину списка требует перемещения всех последующих элементов на одну позицию к концу массива. Операция удаления также требует перемещения элементов списка с целью освобождения ячейки.
Рассмотрим реализацию однонаправленного связанного списка с использованием указателей, связывающих последовательные элементы списка. Однонаправленный связанный список представляет собой динамическую структуру данных, число элементов которой может изменяться по мере того, как данные помещаются в список или удаляются из него. Эта реализация освобождает от непрерывной области памяти для хранения списка и, следовательно, от необходимости перемещения элементов списка при вставке или удалении элементов. Однако ценой за это удобство становится использование дополнительной памяти для хранения указателей.
В данной программе используется абстрактный тип данный «список», реализованный на базе указателей, так как при реализации с помощью массива требуется много времени для перемещения элементов на одну позицию в случае удаления элемента из списка, а данная процедура может осуществляться довольно часто. Использование динамических структур данных позволило оптимизировать расход оперативной памяти и организовать гибкое управление данными.
Возможны несколько вариантов представления и хранения необходимых для работы программы данных.
Рассмотрим некоторые из них
1.1.1 Статические массивы.
Массив это набор однотипных компонентов (элементов), расположенных в памяти непосредственно друг за другом, доступ к которым осуществляется по индексу (индексам).
Преимуществом массива по сравнению с динамическими структурами данных является возможность произвольного доступа, т.е. отсутствует необходимость просмотра всех элементов, расположенных в массиве перед тем элементом, к которому требуется обратиться.
При использовании статических массивов существенным недостатком является необходимость выделения памяти под максимальное количество элементов, т.к. нет возможности изменять размер статического массива при добавлении в него нового элемента.
Нерационально использовать статические массивы и при необходимости удаления элементов. В этом случае придется производить большое количество дополнительных операций над оставшимися элементами, а также часть занятой памяти останется неиспользованной.
Т.к. элементами массива в случае поставленной задачи являются переменные типа запись, содержащие несколько полей, то расходы памяти при использовании массивов были бы неоправданно большими.
К тому же, в условиях поставленной задачи не удастся использовать преимущество прямого доступа к элементам, т.к. необходима возможность выбора элемента по различным признакам, в то время как тип индексации одномерного массива определяется единственным образом.
Таким образом, статические массивы не являются оптимальным решением для представления и хранения данных, необходимых для работы программы.
1.1.2 Динамические связанные списки.
Связанный список это базовая динамическая структура данных, состоящая из узлов, каждый из которых содержит как собственно данные, так и одну или две ссылки («связки») на следующий и/или предыдущий узел списка.
Преимуществом связанных списков по сравнению со статическими массивами является отсутствие ограничений на количество динамически добавляемых элементов (в реальности ограничено только объемом доступной памяти).
Также значительно проще, чем в статических массивах, выполняется процедура удаления.
Недостатком связанных списков является отсутствие возможности произвольного доступа к узлам, т.е. необходимость просмотра всех узлов, предшествующих заданному. Поэтому, в общем случае, работа со списками медленнее, чем с массивами.
К тому же, для хранения указателей на соседние элементы расходуется дополнительное количество памяти.
1.1.3 Базы данных.
База данных это информационная модель, позволяющая упорядоченно хранить данные о группе объектов, обладающих одинаковым набором свойств.
Преимуществом использования баз данных является удобство и относительная простота работы с данными, т.к. компоненты среды Delphi предоставляют для этого достаточно полные и гибкие возможности.
Множество операций над данными, таких как поиск и фильтрация, с помощью них описываются и выполняются быстрее.
Недостатком баз данных является более низкая скорость работы, т.к. физически база данных представляет собой файл или совокупность файлов, хранимых на внешнем носителе.
Поэтому использование базы данных требует большего объема памяти, в т.ч. для хранения служебной информации.
В условиях поставленной задачи, нет необходимости в использовании базы данных, т.к. она эффективна лишь при достаточно большом объеме хранимой информации. В противном случае, использование базы данных приведет к существенному снижению производительности программы и неоправданным затратам памяти.
1.1.4 Вывод.
Таким образом, проанализировав вышеописанные структуры данных, можно сделать вывод, что в случае поставленной задачи оптимальным решением является использование динамических связанных списков.
В программе используются динамические списки. С целью экономии памяти каждая запись списка содержит указатель только на следующий элемент списка, т.к. используемые алгоритмы позволяют решить задачу с помощью односвязных списков.
В математике список представляет собой последовательность элементов определенного типа: а1, а2, …..аn, где n≥0 и все аi имеют один тип. Количество, n длина списка, если n ≥ 1, то а1 первый элемент, а аn последний, в случае n = 0 имеем пустой список.
Списки являются чрезвычайно гибкой структурой: их легко сделать большими или меньшими; их элементы доступны для вставки или удаления в любой позиции списка. Списки можно объединять или разбивать на меньшие списки.
При реализации списков с помощью массивов их элементы располагаются в смежных ячейках массива. Это представление позволяет легко просматривать содержимое списка и вставлять новые элементы в его конец. Однако вставка нового элемента в середину списка требует перемещения всех последующих элементов на одну позицию к концу массива. Операция удаления также требует перемещения элементов списка с целью освобождения ячейки.
Рассмотрим реализацию однонаправленного связанного списка с использованием указателей, связывающих последовательные элементы списка. Однонаправленный связанный список представляет собой динамическую структуру данных, число элементов которой может изменяться по мере того, как данные помещаются в список или удаляются из него. Эта реализация освобождает от непрерывной области памяти для хранения списка и, следовательно, от необходимости перемещения элементов списка при вставке или удалении элементов. Однако ценой за это удобство становится использование дополнительной памяти для хранения указателей.
В этой реализации список состоит из ячеек, каждая из которых содержит элемент списка и указатель на следующую ячейку списка. Если список состоит из элементов то для i=1, 2,…, n-1 ячейка, содержащая элемент , имеет также указатель на ячейку, содержащую элемент . Ячейка, содержащая элемент , имеет указатель nil (нуль). Имеется также ячейка header (заголовок), которая указывает на ячейку, содержащую . Ячейка header не содержит элементов списка. В случае пустого списка заголовок имеет указатель nil, не указывающий ни на какую ячейку. Список, не содержащий элементов, называется пустым или нулевым. На рис. 1.1 показан однонаправленный связанный список.
Рис. 1.1 - Связанный список
Для однонаправленных списков удобно использовать следующее определение позиций элементов. Здесь для i=2, 3, … , n позиция i определяется как указатель на ячейку, содержащую указатель на элемент . Позиция 1 это указатель в ячейке заголовка, а позиция end(L) указатель в последней ячейке списка L. Формально структуру связанного списка можно определить следующим образом(рис. 1.2):
Рис. 1.2 - Структура связанного списка
Основные операции, выполняемые с элементами списка: просмотр, вставка, удаление.
На данный момент существуют различные аналоги данной программы. Ниже описан интерфейс одной из таковых(рис 1.3). Данная программа обладает большим набором возможностей учёта заявок в различных сферах обслуживания.
Рис. 1.3 - Аналог программы регистрации заявок
Вот некоторые из функций программы:
Недостатки программы:
2. Разработка алгоритма
Приведенные ниже схемы иллюстрируют структуру хранения данных на базе односвязных динамических списков(рис. 2.1, 2.2, 2.4).
Рисунок 2.1 Схема структуры списка заявок
Рисунок 2.2 Схема структуры списка адресов жильцов
Рисунок 2.3 Схема структуры адреса
Рисунок 2.4 Схема структуры списка обслуживаемых адресов
Предложенная структура данных позволяет динамически добавлять заявки и адреса в «базу данных». Также предусмотрено изменение и удаление элементов из описанных выше списков.
Алгоритм предусматривает следующие возможности:
Использованные в программе алгоритмы основаны на основных приемах работы с динамическими списками, рассмотренных в соответствующем разделе.
Рисунок 2.5 Схема программы
Рисунок 2.6 Схема процедуры добавления заявки
Рисунок 2.7 Схема процедуры изменения заявки
Рисунок 2.8 Схема процедуры удаления заявки
3. Разработка программного средства
Для разработки программного средства использовалась среда программирования Delphi - Embarcadero RAD studio XE2, предоставляющая возможность визуального создания программ.
Все возможные операции со всеми списками были разделены на группы по смыслу. В программе для удобства каждая группа операций находится на своей панели, что облегчает поиск необходимой операции. Поэтому были созданы панели операций над заявками, сортировки заявок, поиска заявок а также панель статистики поступлений заявок. В главном окне программы находится главный список - список заявок, отображаемый в таблице. Для произведения операций с другими списками используются кнопки для переключения на другие окна, в которых находятся информация и операции по другим спискам. Было создано главное меню, с помощью которого можно обновить список заявок, что используется после поиска заявки, показать список адресов жильцов, сменить логин и пароль пользователя а также выйти из программы.
4. Технические приемы программирования
1) При разработке интерфейса программы были использованы следующие компоненты Delphi:
2) В программе используется три динамических списка, используемых для хранения данных.
3) Для хранения указателей на начало списков используются глобальные переменные, т.к. доступ к спискам должен быть обеспечен в любой точке программы.
4) В программе предусмотрен ряд естественных ограничений (добавление существующей заявки, изменение заявки на существующую, ввод при регистрации заявки адреса, не обслуживаемого данной ЖЭС), реализованных с помощью предварительной проверки состояния списка при клике на соответствующих элементах управления.
5) С целью экономии памяти определена процедура для физического удаления списков.
Пример:
procedure TFrmServiceAddresses.BtnDeleteAddressClick(Sender: TObject);
var
H, Temp: PServiceAdds;
begin
if SgAdds.Row <> 0 then
begin
if (HeadAdds^.Street = SgAdds.Cells[0, SgAdds.Row]) and
(HeadAdds^.Number = SgAdds.Cells[1, SgAdds.Row]) then
begin
Temp := HeadAdds;
HeadAdds := HeadAdds^.Next;
Dispose(Temp);
end
else
begin
H := HeadAdds;
while (H^.Next^.Street <> SgAdds.Cells[0, SgAdds.Row]) and (H^.Next^.Number <> SgAdds.Cells[1, SgAdds.Row]) do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Dispose(Temp);
end;
GridDeleteRow(SgAdds.Row);
end;
end;
5. Тестирование, экспериментальные исследования и анализ полученных данных
Таблица 5.1 - Результаты тестов подпрограмм
Название подпрограммы |
Входные данные |
Ожидаемые результаты |
Пример вызова |
Полученные результаты |
AddReply |
Принимает введенные пользователем в поля ввода данные о жителе. |
Чтение данных, проверка и занесение в список новой заявки. |
Содержимое полей ввода: Пушкин А.С.; ул. Строителей; 42; 54; 758493; лифт; не готово |
Данные из полей ввода прочитаны, проверены на корректность, в список занесена новая заявка, содержащая введенные данные. |
Содержимое полей ввода: |
Данные из полей ввода прочитаны, проверены на корректность, но в список новая заявка не занесена, т.к. данный телефон используется другим жителем, выдано соответствующее сообщение. |
|||
Change Reply |
Принимает введенные пользователем в поля ввода данные о жителе. |
Чтение данных, проверка и изменение в списке данных о заявке. |
Содержимое полей ввода: |
Данные из полей ввода прочитаны, проверены на корректность, в списке изменились данные заявки. |
Содержимое полей ввода: |
Данные из полей ввода прочитаны, проверены на корректность, в списке данные заявки не изменились, т.к. введённый адрес занят, выдано соответствующее сообщение. |
|||
DeleteReply |
Принимает данные о заявке из полей выделенной строки таблицы. |
Поиск и удаление из списка заявки с заданными данными. |
Содержимое полей строки таблицы: |
Был найден и удален из списка диск с заданными данными. |
SearchBy Number |
Принимает введенный пользователем в поле ввода номер заявки. |
Чтение введенных данных, вывод на экран найденной по данному запросу заявки. |
Содержимое поля ввода: |
Данные прочитаны, поиск заявки с введённым номером, на экран была выведена заявка . |
Содержимое поля ввода: 7 |
Данные прочитаны, поиск заявки с введённым номером, на экран не была выведена заявка , т.к. заявки с заданным номером не существует, выдано соответствующее сообщение. |
|||
SortByName |
Сортирует список заявок по неисправности в алфавитном порядке. |
Список успешно отсортировался. |
В процессе тестирования были проверены все операции со списками с разными входными данными, ошибки в программе не нашлись.
6. Руководство пользователя
При запуске программы появляется окно авторизации(рис. 6.1):
Рисунок 6.1
Если вы впервые запускаете программу, вам необходимо зарегистрироваться. Для этого введите логин и пароль в соответствующие поля. При последующих запусках программы необходимо будет ввести эти данные. Если вы неправильно ввели пароль или логин, вы увидите следующую надпись(рис. 6.2):
Рисунок 6.2
При авторизации в системе мы попадаем на главное окно программы.
На главном окне находится таблица, а также операции, выполняемые над её элементами(заявками). Если после предыдущего запуска программы были сохранены заявки, то они автоматически загружаются в таблицу(рис. 6.3).
Рисунок 6.3
В программе доступны следующие операции над заявками:
6.1 Добавить заявку
Для добавления заявки в список нажмите на кнопку "Добавить" на панели операций, заполните все поля в открывшейся форме(рис. 6.4) и нажмите "Добавить".
Рисунок 6.4
При неверном вводе данных заявки могут появиться следующие сообщения об ошибках:
Рисунок 6.5
Рисунок 6.6
Рисунок 6.7
Перед добавлением заявки стоит проверить, обслуживается ли введённый адрес данной ЖЭС путём нажатия на кнопку ''Проверить''. Откроется новое окно(рис. 6.8).
Рисунок 6.8
В нём можно просмотреть таблицу, есть ли там нужный адрес. Если он там должен быть, нажмите на кнопку ''Добавить'' в данном окне. Откроется новое окно ввода данных(рис. 6.9):
Рисунок 6.9
Введите название улицы. В поле ''Дом'' данные вводить необязательно. Тогда будет считаться, что обслуживаются все дома данной улицы. Для задания конкретных домов введите их номера через пробел либо запятую в поле ''Дом'' и нажмите кнопку ''Добавить''.
6.2 Изменить заявку
Для изменения заявки выделите строку таблицы, содержащую данные
интересующей вас заявки, нажмите на кнопку "Изменить" на панели операций, исправьте интересующие вас поля в открывшейся форме(рис. 6.10) и нажмите "Изменить".
Рисунок 6.10
При неправильном вводе данных могут возникнуть сообщения об ошибках, описанные в предыдущем подпункте.
6.3 Удалить заявку
Выделите строку в таблице с данными ненужной вам заявки и нажмите на кнопку "Удалить" на панели операций.
6.4 Поиск диска
Введите номер заявки или ФИО жильца в соответствующее поле поиска на панели поиска(рис. 6.11) и нажмите "ok" для отображения интересующей вас заявки(заявок). Для просмотра всего списка заявок и очистки полей поиска выберите пункт меню Файл -> Обновить окно или нажмите F5(рис. 6.12).
Рисунок 6.11 Рисунок 6.12
При поиске несуществующей в списке заявки появляется сообщение(рис. 6.13):
Рисунок 6.13
6.5 Сортировка заявок
Для сортировки списка по дате или по неисправности выберите соответствующий пункт на панели сортировки(рис. 6.14).
Рисунок 6.14
6.6 Статистика
Для просмотра заявок, поступивших в определённый промежуток времени, введите нужный диапазон времени в поля на панели статистики и нажмите ''ok''(рис. 6.15):
Рисунок 6.15
Заявки, поступившие в заданный диапазон времени отобразятся в таблице а также сохранятся в текстовый файл в папке Debug.
Соответственным образом можно увидеть заявки, поступившие за текущие сутки а также не закрытые заявки, нажав на кнопку ''Показать'' под соответствующей надписью. Все отобразившиеся по данным условиям заявки также сохранятся в текстовые файлы в папку Debug.
При нажатии на кнопку ''Показать'' под надписью ''График поступления заявок'' откроется новое окно. Выберите нужный вам интервал времени поступления заявок и нажмите ''Показать''(рис. 6.16).
Рисунок 6.16
Также в программе можно отобразить список адресов жильцов. Для этого при работе в главном окне выберите в главном меню Вид -> Список адресов. Откроется новое окно(рис. 6.17):
Рисунок 6.17
Вы можете редактировать задолженность жильцов путём выделения строки таблицы с адресом нужного жильца и последующим нажатием на кнопку ''Редактировать''. Откроется новое окно(рис. 6.18):
Рисунок 6.18
Вы можете изменить задолженность путём ввода в соответствующее поле нужной суммы и последующим нажатием кнопки ''Изменить''. Для обнуления задолженности напишите в соответствующем поле 0 или не заполняйте его вообще.
Также в программе имеется возможность смены логина и пароля. Для этого работая в главном окне выберите пункт меню Опции -> Сменить пользователя. Откроется то же окно, что и при авторизации. Введите новые логин и пароль и нажмите ''ok''.
Для выхода из программы можно выбрать пункт в главном меню Файл -> Выйти.
При закрытии программы данные списков заявок; списков адресов жителей с задолженностями; адресов, обслуживаемых данной ЖЭС сохраняются в текстовые файлы. При следующем открытии программы они
будут загружены из файлов.
Заключение
Как было показано выше, данное приложение предоставляет пользователю возможности, перечисленные при постановке задачи.
При этом для решения задачи использованы структуры данных (динамические связанные списки. Разработанный алгоритм, созданный при помощи среды программирования Delphi, эффективно использует предоставляемые данной средой возможности. Таким образом, в процессе разработки данного программного средства мы на практике убедились в преимуществах работы с динамическими структурами данных.
В результате тестирования приложения было показано, что полученные результаты работы подпрограмм на конкретных рассмотренных (корректных и некорректных) наборах данных соответствуют ожидаемым, т. е. основные подпрограммы функционируют нормально.
В программе предусмотрен ряд естественных ограничений, продиктованных смыслом поставленной задачи и особенностями ее реализации в действительности. Данные ограничения подробно документированы в пункте «Руководство пользователя».
Список используемой литературы
Листинг программы
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls, Vcl.ExtCtrls,
unit3, unit4, unit6, unit8,
Vcl.Imaging.jpeg, Vcl.Imaging.pngimage, Vcl.Menus;
type
TFrmMain = class(TForm)
SgMain: TStringGrid;
PnlMain: TPanel;
BtnAdd: TButton;
LblRegReply: TLabel;
BtnDelete: TButton;
PnlActions: TPanel;
PnlSort: TPanel;
PnlSearch: TPanel;
LblSearch: TLabel;
EdtSearchNum: TEdit;
EdtSearchName: TEdit;
MainMenu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
PnlGreeting: TPanel;
LblGreeting: TLabel;
LblHint: TLabel;
PnlStatistics: TPanel;
EdtHoursFrom: TEdit;
EdtMinsFrom: TEdit;
PnlTime: TPanel;
EdtMinsTill: TEdit;
EdtHoursTill: TEdit;
LblShowByTime: TLabel;
BtnShowByTime: TButton;
LblShowByDay: TLabel;
LblShowUnclosed: TLabel;
BtnShowByDay: TButton;
BtnShowUnclosed: TButton;
LblStatistics: TLabel;
LblShowGraph: TLabel;
BtnShowGraph: TButton;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
BtnSearchNum: TButton;
BtnSearchName: TButton;
BtnChange: TButton;
Rg: TRadioGroup;
N3: TMenuItem;
N4: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure BtnAddClick(Sender: TObject);
procedure N1Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure PnlGreetingMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
procedure LblHintMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
procedure LblGreetingMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
procedure BtnShowGraphClick(Sender: TObject);
procedure N5Click(Sender: TObject);
procedure N7Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SgMainDrawCell(Sender: TObject; ACol, ARow: integer; Rect: TRect;
State: TGridDrawState);
procedure RgClick(Sender: TObject);
procedure BtnChangeClick(Sender: TObject);
procedure BtnDeleteClick(Sender: TObject);
procedure BtnSearchNumClick(Sender: TObject);
procedure BtnSearchNameClick(Sender: TObject);
procedure BtnShowByTimeClick(Sender: TObject);
procedure BtnShowByDayClick(Sender: TObject);
procedure BtnShowUnclosedClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmMain: TFrmMain;
IsAdded: Boolean;
implementation
{$R *.dfm}
uses Unit2;
// при нажатии на кнопку 'Добавить'
procedure TFrmMain.BtnAddClick(Sender: TObject);
begin
with FrmReplyReg do
begin
EdtName.Text := '';
EdtStreet.Text := '';
EdtNumber.Text := '';
EdtRoom.Text := '';
EdtPhone.Text := '';
CmbProblem.ItemIndex := 0;
CmbReadyness.ItemIndex := 0;
CmbReadyness.Enabled := false;
EdtStreet.Enabled := true;
EdtNumber.Enabled := true;
EdtRoom.Enabled := true;
BtnAddReply.Visible := true;
BtnChangeReply.Visible := false;
IsAdded := true;
end;
// открываем форму регистрации заявок
FrmReplyReg.ShowModal;
end;
// При нажатии на кнопку 'Изменить'
procedure TFrmMain.BtnChangeClick(Sender: TObject);
var
ReplyAdds: string;
i: integer;
begin
// Заполняем поля в форме редактирования заявок
if SgMain.Row <> 0 then
begin
with FrmReplyReg do
begin
// Имя жильца
EdtName.Text := SgMain.Cells[1, SgMain.Row];
ReplyAdds := SgMain.Cells[2, SgMain.Row];
// Улица
EdtStreet.Text := Copy(ReplyAdds, 1, Pos(',', ReplyAdds) - 1);
ReplyAdds := Copy(ReplyAdds, Pos('.', ReplyAdds) + 1,
Length(ReplyAdds) - Pos('.', ReplyAdds));
// Номер дома
EdtNumber.Text := Copy(ReplyAdds, 1, Pos(',', ReplyAdds) - 1);
ReplyAdds := Copy(ReplyAdds, Pos('.', ReplyAdds) + 1,
Length(ReplyAdds) - Pos('.', ReplyAdds));
// Номер квартиры
EdtRoom.Text := ReplyAdds;
// Телефон
EdtPhone.Text := SgMain.Cells[3, SgMain.Row];
// Неисправность
i := 0;
while i <> CmbProblem.Items.Count - 1 do
begin
if CmbProblem.Items[i] = SgMain.Cells[4, SgMain.Row] then
begin
CmbProblem.ItemIndex := i;
break;
end;
i := i + 1;
end;
// Готовность
if SgMain.Cells[6, SgMain.Row] = 'Не готово' then
CmbReadyness.ItemIndex := 0
else
CmbReadyness.ItemIndex := 1;
CmbReadyness.Enabled := true;
EdtStreet.Enabled := false;
EdtNumber.Enabled := false;
EdtRoom.Enabled := false;
BtnAddReply.Visible := false;
BtnChangeReply.Visible := true;
end;
IsAdded := false;
// открываем форму регистрации заявок
FrmReplyReg.ShowModal;
end;
end;
// процедура изменения номера заявок при удалении заявки
procedure SetNewReplyNumber(Deleted: integer);
var
H: PReply;
begin
H := HeadReply;
while H <> nil do
begin
// у всех заявок, чей номер больше номера удалённой, он уменьшается на 1
if H^.Num > Deleted then
H^.Num := H^.Num - 1;
H := H^.Next;
end;
// номер следующей при добавлении заявки будет на 1 меньше чем должен был
ReplyNumber := ReplyNumber - 1;
end;
// при нажатии на кнопку 'Удалить'
procedure TFrmMain.BtnDeleteClick(Sender: TObject);
// процедура удаления строки из таблицы
procedure GridDeleteRow(RowNumber: integer);
var
i: integer;
begin
with FrmAddresses do
begin
SgAddresses.Row := RowNumber;
// если удаляется последняя строка таблицы
if SgAddresses.Row = SgAddresses.RowCount - 1 then
SgAddresses.RowCount := SgAddresses.RowCount - 1
else
begin
// не последняя
for i := RowNumber to SgAddresses.RowCount - 1 do
SgAddresses.Rows[i] := SgAddresses.Rows[i + 1];
SgAddresses.RowCount := SgAddresses.RowCount - 1;
end;
end;
end;
// процедура удаления адреса из списка адресов
procedure DelAddsFromList;
var
P, Temp: PAddsList;
H: PReply;
DeletingRow: integer;
begin
// проверка, существует ли потенциально этот же удаляемый адрес в списке
H := HeadReply;
while H <> nil do
begin
// если да, то выходим из процедуры, адрес из списка не удаляется
if (H^.Adds^.Street + ', д.' + H^.Adds^.Number + ', кв.' +
H^.Adds^.Room = SgMain.Cells[2, SgMain.Row]) then
Exit;
H := H^.Next;
end;
// если удаляем головной адрес
if HeadAddsList^.Adds^.Street + ', д.' + HeadAddsList^.Adds^.Number +
', кв.' + HeadAddsList^.Adds^.Room = SgMain.Cells[2, SgMain.Row] then
begin
Temp := HeadAddsList;
HeadAddsList := HeadAddsList^.Next;
Dispose(Temp);
DeletingRow := 1;
end
else
begin
// если удаляем произвольный(не головной) адрес
P := HeadAddsList;
DeletingRow := 2;
with P^.Next^.Adds^ do
while Street + ', д.' + Number + ', кв.' + Room <> SgMain.Cells
[2, SgMain.Row] do
begin
P := P^.Next;
DeletingRow := DeletingRow + 1;
end;
Temp := P^.Next;
P^.Next := P^.Next^.Next;
Dispose(Temp);
end;
// удаляем строку из таблицы
GridDeleteRow(DeletingRow);
end;
var
H, Temp: PReply;
RowNum, ColNum: integer;
begin
H := HeadReply;
// если выделена не заглавная строка таблицы
if SgMain.Row <> 0 then
begin
{ если данные первого в списке диска совпадают с данными диска
выделенной строки таблицы, то удаляем первый элемент из списка,
а также очищаем занимаемую им память }
if inttostr(H^.Num) = SgMain.Cells[0, SgMain.Row] then
begin
Temp := HeadReply;
HeadReply := HeadReply^.Next;
Dispose(Temp);
end
else
{ в противном случае ищем совпадения данных текущего диска с данными диска
в выделенной строке таблицы, после чего удаляем этот элемент из списка
и очищаем занимаемую им память }
begin
while inttostr(H^.Next^.Num) <> SgMain.Cells[0, SgMain.Row] do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Dispose(Temp);
end;
// удаляем адрес из списка
DelAddsFromList;
// присваиваем значение номеру будущей заявки
SetNewReplyNumber(strtoint(SgMain.Cells[0, SgMain.Row]));
// вывод на экран списка
H := HeadReply;
SgMain.RowCount := SgMain.RowCount - 1;
for RowNum := 1 to SgMain.RowCount - 1 do
begin
for ColNum := 0 to 6 do
begin
case ColNum of
0:
SgMain.Cells[ColNum, RowNum] := inttostr(H^.Num);
1:
SgMain.Cells[ColNum, RowNum] := H^.Name;
2:
SgMain.Cells[ColNum, RowNum] := H^.Adds^.Street + ', д.' +
H^.Adds^.Number + ', кв.' + H^.Adds^.Room;
3:
SgMain.Cells[ColNum, RowNum] := H^.Phone;
4:
SgMain.Cells[ColNum, RowNum] := H^.Problem;
5:
SgMain.Cells[ColNum, RowNum] := datetostr(H^.IncomeDate) + ', ' +
timetostr(H^.IncomeTime);
6:
SgMain.Cells[ColNum, RowNum] := H^.Ready;
end;
end;
H := H^.Next;
end;
end;
end;
// процедура печати результата поиска
procedure PrintSearch(Searching: string; T: integer);
var
H, Temp, PrintReply: PReply;
begin
with FrmMain do
begin
// PrintReply - список заявок, которые удовлетворяют условию поиска
PrintReply := nil;
H := HeadReply;
// проходимся по всему списку
while H <> nil do
begin
{ параметр T передаём при вызове процедуры, если он = 1,
то ищем заявки по номеру, иначе по имени.
Comparing - номер заявки /имя жильца из текущего списка,
Searching - номер заявки /имя жильца, которого мы хотим найти }
if T = 1 then
begin
if Searching = inttostr(H^.Num) then
begin
// если PrintReply пустой, то создаём его и добавляем 1ый элемент
if PrintReply = nil then
begin
New(PrintReply);
PrintReply^ := H^;
PrintReply^.Next := nil;
end
else
// если не пустой, то добавляем элемент
begin
Temp := PrintReply;
while Temp^.Next <> nil do
Temp := Temp^.Next;
New(Temp^.Next);
Temp^.Next^ := H^;
Temp^.Next^.Next := nil;
end;
end;
end
else
// если строка Searching является строкой/подстрокой строки Comparing
if Pos(AnsiLowerCase(Searching), AnsiLowerCase(H^.Name)) <> 0 then
begin
// если PrintReply пустой, то создаём его и добавляем 1ый элемент
if PrintReply = nil then
begin
New(PrintReply);
PrintReply^ := H^;
PrintReply^.Next := nil;
end
else
// если не пустой, то добавляем элемент
begin
Temp := PrintReply;
while Temp^.Next <> nil do
Temp := Temp^.Next;
New(Temp^.Next);
Temp^.Next^ := H^;
Temp^.Next^.Next := nil;
end;
end;
// проверяем следующий элемент списка
H := H^.Next;
end;
{ после того, как проверили весь список, если PrintReply пустой,
показать соответствующее сообщение }
if PrintReply = nil then
ShowMessage('По данному запросу заявок не найдено.')
else
begin
{ если PrintReply не пустой, то выводим все его элементы на экран,
последовательно удаляя элементы PrintReply'a для экономии памяти
(повышение эффективности программы) }
SgMain.RowCount := 1;
while PrintReply <> nil do
begin
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(PrintReply^.Num);
SgMain.Cells[1, SgMain.RowCount - 1] := PrintReply^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := PrintReply^.Adds^.Street +
', д.' + PrintReply^.Adds^.Number + ', кв.' + PrintReply^.Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := PrintReply^.Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := PrintReply^.Problem;
SgMain.Cells[5, SgMain.RowCount - 1] :=
datetostr(PrintReply^.IncomeDate) + ', ' +
timetostr(PrintReply^.IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := PrintReply^.Ready;
Temp := PrintReply;
PrintReply := PrintReply^.Next;
Dispose(Temp);
end;
end;
end;
end;
// при нажатии на кнопку поиска по ФИО жильца
procedure TFrmMain.BtnSearchNameClick(Sender: TObject);
begin
PrintSearch(EdtSearchName.Text, 2);
end;
// при нажатии на кнопку поиска по номеру заявки
procedure TFrmMain.BtnSearchNumClick(Sender: TObject);
begin
PrintSearch(EdtSearchNum.Text, 1);
end;
// процедура вывода на экран и в файл заявок, удовлетворяющих условиям поиска
procedure ShowReplies(By: String; NewName: string);
var
H, Temp, PrintReply: PReply;
F: TextFile;
Condition: Boolean;
begin
// если список заявок не пуст
if HeadReply <> nil then
begin
H := HeadReply;
// PrintReply - список печатающихся заявок
PrintReply := nil;
while H <> nil do
begin
// By определяет выводим заявки за день или не готовые
if By = 'ByDay' then
Condition := (H^.IncomeDate = Date)
else
Condition := (H^.Ready = 'Не готово');
// если условие соблюдается, добавляем заявку в список печатающихся заявок
if Condition = true then
begin
if PrintReply = nil then
begin
New(PrintReply);
PrintReply^ := H^;
PrintReply^.Next := nil;
end
else
begin
Temp := PrintReply;
while Temp^.Next <> nil do
Temp := Temp^.Next;
New(Temp^.Next);
Temp^.Next^ := H^;
Temp^.Next^.Next := nil;
end;
end;
H := H^.Next;
end;
// Если существуют заявки по данному запросу
if PrintReply <> nil then
begin
// Запись в файл заявок по данному запросу
AssignFile(F, 'Заявки.txt');
Rewrite(F);
CloseFile(F);
Rename(F, NewName);
Rewrite(F);
writeln(F,
'№ ФИО Адрес Телефон Неисправность Дата и время поступления Готовность');
Temp := PrintReply;
while Temp <> nil do
begin
if Temp^.Next = nil then
write(F, inttostr(Temp^.Num) + ' ' + Temp^.Name + ' ' +
Temp^.Adds^.Street + ', ' + Temp^.Adds^.Number + ', ' +
Temp^.Adds^.Room + ' ' + Temp^.Phone + ' ' + Temp^.Problem +
' ' + datetostr(Temp^.IncomeDate) + ', ' +
timetostr(Temp^.IncomeTime) + ' ' + Temp^.Ready)
else
writeln(F, inttostr(Temp^.Num) + ' ' + Temp^.Name + ' ' +
Temp^.Adds^.Street + ', ' + Temp^.Adds^.Number + ', ' +
Temp^.Adds^.Room + ' ' + Temp^.Phone + ' ' + Temp^.Problem +
' ' + datetostr(Temp^.IncomeDate) + ', ' +
timetostr(Temp^.IncomeTime) + ' ' + Temp^.Ready);
Temp := Temp^.Next;
end;
CloseFile(F);
// Вывод на экран заявок по данному запросу
with FrmMain do
begin
SgMain.RowCount := 1;
while PrintReply <> nil do
begin
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(PrintReply^.Num);
SgMain.Cells[1, SgMain.RowCount - 1] := PrintReply^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := PrintReply^.Adds^.Street +
', д.' + PrintReply^.Adds^.Number + ', кв.' +
PrintReply^.Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := PrintReply^.Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := PrintReply^.Problem;
SgMain.Cells[5, SgMain.RowCount - 1] :=
datetostr(PrintReply^.IncomeDate) + ', ' +
timetostr(PrintReply^.IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := PrintReply^.Ready;
// последовательно очищаем память, занимаемую печатаемым списком
Temp := PrintReply;
PrintReply := PrintReply^.Next;
Dispose(Temp);
end;
end;
end
else
ShowMessage('По данному запросу заявок не найдено.');
end;
end;
// при нажатии на кнопку 'Показать' заявки за день
procedure TFrmMain.BtnShowByDayClick(Sender: TObject);
begin
ShowReplies('ByDay', 'Заявки за ' + datetostr(Date) + ' .txt');
end;
// при нажатии на кнопку 'ok' - показать заявки, входящие во временной диапазон
procedure TFrmMain.BtnShowByTimeClick(Sender: TObject);
var
TimeFrom, TimeTill: TTime;
H, Temp, PrintReply: PReply;
NewName: string;
F: TextFile;
begin
// если поля ввода времени заполнены
if (EdtHoursFrom.Text <> '') and (EdtMinsFrom.Text <> '') and
(EdtHoursTill.Text <> '') and (EdtMinsTill.Text <> '') then
// Если время введено неправильно или в неправильном формате
if (strtoint(EdtHoursFrom.Text) > 23) or (strtoint(EdtMinsFrom.Text) > 59)
or (strtoint(EdtHoursTill.Text) > 23) or (strtoint(EdtMinsTill.Text) > 59)
then
ShowMessage
('Правильно заполните поля в формате hh mm : hh mm, где hh - часы(число находится в диапазоне 0-23), а mm - минуты(число находится в диапазоне 0-59)')
else
begin
// TimeFrom - время 'до', TimeTill - время 'после'
TimeFrom := strtotime(EdtHoursFrom.Text + ':' + EdtMinsFrom.Text + ':00');
TimeTill := strtotime(EdtHoursTill.Text + ':' + EdtMinsTill.Text + ':00');
// если время 'до' > время 'после'
if TimeFrom > TimeTill then
ShowMessage('Время ''до'' больше времени ''после''.')
// если список заявок не пустой
else if HeadReply <> nil then
begin
H := HeadReply;
// PrintReply - список заявок для печати
PrintReply := nil;
while H <> nil do
begin
{ если время заявки находится в указанном диапазоне, то добавляем её
в список для печати }
if (H^.IncomeTime >= TimeFrom) and (H^.IncomeTime <= TimeTill) then
begin
if PrintReply = nil then
begin
New(PrintReply);
PrintReply^ := H^;
PrintReply^.Next := nil;
end
else
begin
Temp := PrintReply;
while Temp^.Next <> nil do
Temp := Temp^.Next;
New(Temp^.Next);
Temp^.Next^ := H^;
Temp^.Next^.Next := nil;
end;
end;
H := H^.Next;
end;
// Если по данному запросу найдены заявки
if PrintReply <> nil then
begin
// Запись в файл заявок по данному запросу
AssignFile(F, 'Заявки.txt');
Rewrite(F);
CloseFile(F);
NewName := 'Заявки с ' + EdtHoursFrom.Text + '.' + EdtMinsFrom.Text +
' по ' + EdtHoursTill.Text + '.' + EdtMinsTill.Text + '.txt';
Rename(F, NewName);
Rewrite(F);
writeln(F,
'№ ФИО Адрес Телефон Неисправность Дата и время поступления Готовность');
Temp := PrintReply;
while Temp <> nil do
begin
if Temp^.Next = nil then
write(F, inttostr(Temp^.Num) + ' ' + Temp^.Name + ' ' +
Temp^.Adds^.Street + ', ' + Temp^.Adds^.Number + ', ' +
Temp^.Adds^.Room + ' ' + Temp^.Phone + ' ' +
Temp^.Problem + ' ' + datetostr(Temp^.IncomeDate) + ', ' +
timetostr(Temp^.IncomeTime) + ' ' + Temp^.Ready)
else
writeln(F, inttostr(Temp^.Num) + ' ' + Temp^.Name + ' ' +
Temp^.Adds^.Street + ', ' + Temp^.Adds^.Number + ', ' +
Temp^.Adds^.Room + ' ' + Temp^.Phone + ' ' +
Temp^.Problem + ' ' + datetostr(Temp^.IncomeDate) + ', ' +
timetostr(Temp^.IncomeTime) + ' ' + Temp^.Ready);
Temp := Temp^.Next;
end;
CloseFile(F);
// Вывод на экран заявок по данному запросу
SgMain.RowCount := 1;
while PrintReply <> nil do
begin
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(PrintReply^.Num);
SgMain.Cells[1, SgMain.RowCount - 1] := PrintReply^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := PrintReply^.Adds^.Street +
', д.' + PrintReply^.Adds^.Number + ', кв.' +
PrintReply^.Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := PrintReply^.Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := PrintReply^.Problem;
SgMain.Cells[5, SgMain.RowCount - 1] :=
datetostr(PrintReply^.IncomeDate) + ', ' +
timetostr(PrintReply^.IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := PrintReply^.Ready;
// последовательно очищаем память, занимаемую печатаемым списком
Temp := PrintReply;
PrintReply := PrintReply^.Next;
Dispose(Temp);
end;
end
else
ShowMessage('По данному запросу заявок не найдено.');
end;
end
else
ShowMessage('Заполните все поля.');
end;
// при нажатии на кнопку 'Показать' график поступления заявок
procedure TFrmMain.BtnShowGraphClick(Sender: TObject);
begin
// открывается форма с графиком
FrmGraph.ShowModal;
end;
// при нажатии на кнопку 'Показать' незакрытые заявки
procedure TFrmMain.BtnShowUnclosedClick(Sender: TObject);
begin
ShowReplies('ByReady', 'Незакрытые заявки.txt');
end;
// при закрытии формы
procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
var
F: TextFile;
i: integer;
begin
// Сохранение списка обслуживаемых адресов
AssignFile(F, 'Adds.txt');
if HeadAdds <> nil then
begin
Rewrite(F);
while HeadAdds <> nil do
begin
writeln(F, HeadAdds^.Street);
if HeadAdds^.Next = nil then
write(F, HeadAdds^.Number)
else
writeln(F, HeadAdds^.Number);
HeadAdds := HeadAdds^.Next;
end;
CloseFile(F);
end
else if FileExists('Adds.txt') then
Erase(F);
// Сохранение списка заявок
AssignFile(F, 'Reply.txt');
if HeadReply <> nil then
begin
Rewrite(F);
while HeadReply <> nil do
begin
writeln(F, HeadReply^.Num);
writeln(F, HeadReply^.Name);
writeln(F, HeadReply^.Adds^.Street);
writeln(F, HeadReply^.Adds^.Number);
writeln(F, HeadReply^.Adds^.Room);
writeln(F, HeadReply^.Phone);
writeln(F, HeadReply^.Problem);
writeln(F, HeadReply^.IncomeDate);
writeln(F, HeadReply^.IncomeTime);
if HeadReply^.Next = nil then
write(F, HeadReply^.Ready)
else
writeln(F, HeadReply^.Ready);
HeadReply := HeadReply^.Next;
end;
CloseFile(F);
end
else if FileExists('Reply.txt') then
Erase(F);
// Сохранение списка адресов
AssignFile(F, 'AddsList.txt');
if HeadAddsList <> nil then
begin
Rewrite(F);
while HeadAddsList <> nil do
begin
writeln(F, HeadAddsList^.Adds^.Street);
writeln(F, HeadAddsList^.Adds^.Number);
writeln(F, HeadAddsList^.Adds^.Room);
if HeadAddsList^.Next = nil then
write(F, inttostr(HeadAddsList^.Owed))
else
writeln(F, inttostr(HeadAddsList^.Owed));
HeadAddsList := HeadAddsList^.Next;
end;
CloseFile(F);
end
else if FileExists('AddsList.txt') then
Erase(F);
// Сохранение списка неисправностей
AssignFile(F, 'Problems.txt');
with FrmReplyReg do
if CmbProblem.Items.Count <> 0 then
begin
Rewrite(F);
for i := 0 to CmbProblem.Items.Count - 1 do
if i <> CmbProblem.Items.Count - 1 then
writeln(F, CmbProblem.Items[i])
else
write(F, CmbProblem.Items[i]);
CloseFile(F);
end
else if FileExists('Problems.txt') then
Erase(F);
end;
// при создании формы
procedure TFrmMain.FormCreate(Sender: TObject);
var
F: TextFile;
H: PServiceAdds;
X: PReply;
P: PAddsList;
begin
SgMain.Cells[0, 0] := '№';
SgMain.Cells[1, 0] := ' ФИО';
SgMain.Cells[2, 0] := ' Адрес';
SgMain.Cells[3, 0] := ' Телефон';
SgMain.Cells[4, 0] := ' Проблема';
SgMain.Cells[5, 0] := ' Дата поступления';
SgMain.Cells[6, 0] := ' Готовность';
// Загрузка списка обслуживаемых адресов
AssignFile(F, 'Adds.txt');
if FileExists('Adds.txt') then
begin
Reset(F);
while not Eof(F) do
begin
if HeadAdds = nil then
begin
New(HeadAdds);
Readln(F, HeadAdds^.Street);
Readln(F, HeadAdds^.Number);
HeadAdds^.Next := nil;
end
else
begin
H := HeadAdds;
while H^.Next <> nil do
H := H^.Next;
New(H^.Next);
Readln(F, H^.Next^.Street);
Readln(F, H^.Next^.Number);
H^.Next^.Next := nil;
end;
end;
CloseFile(F);
end;
// Инициализация номера следующей заявки
ReplyNumber := 1;
// Загрузка списка заявок
AssignFile(F, 'Reply.txt');
if FileExists('Reply.txt') then
begin
Reset(F);
while not Eof(F) do
begin
if HeadReply = nil then
begin
New(HeadReply);
Readln(F, HeadReply^.Num);
Readln(F, HeadReply^.Name);
New(HeadReply^.Adds);
Readln(F, HeadReply^.Adds^.Street);
Readln(F, HeadReply^.Adds^.Number);
Readln(F, HeadReply^.Adds^.Room);
Readln(F, HeadReply^.Phone);
Readln(F, HeadReply^.Problem);
Readln(F, HeadReply^.IncomeDate);
Readln(F, HeadReply^.IncomeTime);
Readln(F, HeadReply^.Ready);
HeadReply^.Next := nil;
ReplyNumber := ReplyNumber + 1;
end
else
begin
X := HeadReply;
while X^.Next <> nil do
X := X^.Next;
New(X^.Next);
Readln(F, X^.Next^.Num);
Readln(F, X^.Next^.Name);
New(X^.Next^.Adds);
Readln(F, X^.Next^.Adds^.Street);
Readln(F, X^.Next^.Adds^.Number);
Readln(F, X^.Next^.Adds^.Room);
Readln(F, X^.Next^.Phone);
Readln(F, X^.Next^.Problem);
Readln(F, X^.Next^.IncomeDate);
Readln(F, X^.Next^.IncomeTime);
Readln(F, X^.Next^.Ready);
X^.Next^.Next := nil;
ReplyNumber := ReplyNumber + 1;
end;
end;
CloseFile(F);
end;
// вывод на экран списка заявок
X := HeadReply;
while X <> nil do
begin
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(X^.Num);
SgMain.Cells[1, SgMain.RowCount - 1] := X^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := X^.Adds^.Street + ', д.' +
X^.Adds^.Number + ', кв.' + X^.Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := X^.Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := X^.Problem;
SgMain.Cells[5, SgMain.RowCount - 1] := datetostr(X^.IncomeDate) + ', ' +
timetostr(X^.IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := X^.Ready;
X := X^.Next;
end;
// загрузка списка адресов
AssignFile(F, 'AddsList.txt');
if FileExists('AddsList.txt') then
begin
Reset(F);
while not Eof(F) do
begin
if HeadAddsList = nil then
begin
New(HeadAddsList);
New(HeadAddsList^.Adds);
Readln(F, HeadAddsList^.Adds^.Street);
Readln(F, HeadAddsList^.Adds^.Number);
Readln(F, HeadAddsList^.Adds^.Room);
Readln(F, HeadAddsList^.Owed);
HeadAddsList^.Next := nil;
end
else
begin
P := HeadAddsList;
while P^.Next <> nil do
P := P^.Next;
New(P^.Next);
New(P^.Next^.Adds);
Readln(F, P^.Next^.Adds^.Street);
Readln(F, P^.Next^.Adds^.Number);
Readln(F, P^.Next^.Adds^.Room);
Readln(F, P^.Next^.Owed);
P^.Next^.Next := nil;
end;
end;
CloseFile(F);
end;
end;
// прячет начальное приветствие при наведении на первую надпись
procedure TFrmMain.LblGreetingMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
begin
PnlGreeting.Hide;
LblGreeting.Hide;
LblHint.Hide;
end;
// прячет начальное приветствие при наведении на вторую надпись
procedure TFrmMain.LblHintMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
begin
PnlGreeting.Hide;
LblGreeting.Hide;
LblHint.Hide;
end;
// при нажатии на пункт 'Выйти' в главном меню
procedure TFrmMain.N1Click(Sender: TObject);
begin
// закрывает приложение
FrmMain.Close;
end;
// при нажатии на пункт меню 'Список адресов'
procedure TFrmMain.N4Click(Sender: TObject);
begin
// открывает форму с адресами
FrmAddresses.Show;
end;
// при нажатии на пункт 'Обновить' в меню
procedure TFrmMain.N5Click(Sender: TObject);
var
H: PReply;
begin
// если мы прошли авторизацию
if FrmLogin.Visible = false then
begin
H := HeadReply;
SgMain.RowCount := 1;
// вывод на экран всего списка заявок и очистка полей поиска
while H <> nil do
begin
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(H^.Num);
SgMain.Cells[1, SgMain.RowCount - 1] := H^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := H^.Adds^.Street + ', д.' +
H^.Adds^.Number + ', кв.' + H^.Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := H^.Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := H^.Problem;
SgMain.Cells[5, SgMain.RowCount - 1] := datetostr(H^.IncomeDate) + ', ' +
timetostr(H^.IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := H^.Ready;
H := H^.Next;
end;
EdtSearchNum.Text := '';
EdtSearchName.Text := '';
end;
end;
// при нажатии на пункт меню 'Смена пользователя'
procedure TFrmMain.N7Click(Sender: TObject);
begin
FrmLogin.Caption := 'Смена пользователя';
// открывается форма авторизации
FrmLogin.ShowModal;
end;
// прячет начальное приветствие при наведении на красную панель
procedure TFrmMain.PnlGreetingMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: integer);
begin
PnlGreeting.Hide;
LblGreeting.Hide;
LblHint.Hide;
end;
// при нажатии на сортировку
procedure TFrmMain.RgClick(Sender: TObject);
begin
IsAdded := false;
if Rg.ItemIndex = 0 then
// сортируем по дате
FrmReplyReg.SortByDate;
if Rg.ItemIndex = 1 then
// сортируем по неисправности
FrmReplyReg.SortByProblem;
end;
// делает таблицу заявок шире, если элементов более 22, чтобы поместился ScrollBar
procedure TFrmMain.SgMainDrawCell(Sender: TObject; ACol, ARow: integer;
Rect: TRect; State: TGridDrawState);
begin
if (SgMain.RowCount > 22) and (SgMain.Width = 1005) then
SgMain.Width := 1023;
if (SgMain.Width = 1023) and (SgMain.RowCount < 23) then
SgMain.Width := 1005;
end;
end.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit1;
type
TFrmLogin = class(TForm)
EdLogin: TEdit;
EdPassWord: TEdit;
BtnOk: TButton;
LblReg1: TLabel;
LblReg2: TLabel;
procedure BtnOkClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmLogin: TFrmLogin;
implementation
{$R *.dfm}
// при нажатии на кнопку 'ok'
procedure TFrmLogin.BtnOkClick(Sender: TObject);
var
F: TextFile;
Log, Pass: string;
begin
AssignFile(F, 'Login.txt');
{ если файл не существует или мы меняем логин и пароль, то данные авторизации
в файле перезаписываются }
if (not(FileExists('Login.txt'))) or (FrmLogin.Caption = 'Смена пользователя')
then
begin
Rewrite(F);
Writeln(F, EdLogin.Text);
Writeln(F, EdPassWord.Text);
CloseFile(F);
FrmLogin.Hide;
FrmMain.Show;
FrmLogin.Close;
end
else
begin
Reset(F);
Readln(F, Log);
Readln(F, Pass);
CloseFile(F);
// если логин и пароль совпадают, входим на главную форму
if (AnsiSameText(EdLogin.Text, Log)) and (EdPassWord.Text = Pass) then
begin
FrmLogin.Hide;
FrmMain.Show;
FrmLogin.Close;
end
else
// иначе остаёмся на этой же форме
begin
LblReg1.Show;
LblReg2.Show;
LblReg1.Caption := 'Неверный логин';
LblReg2.Caption := ' или пароль';
end;
end;
end;
// при закрытии формы
procedure TFrmLogin.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// если мы пытаемся войти в систему а не меняем логи и пароль, то выходим из программы
if FrmMain.Visible = false then
FrmMain.Close;
end;
// при создании формы
procedure TFrmLogin.FormCreate(Sender: TObject);
var
F: TextFile;
begin
AssignFile(F, 'Login.txt');
// устанавливаем видимость надписей
if FileExists('Login.txt') then
begin
Reset(F);
if eof(F) then
begin
LblReg1.Visible := true;
LblReg2.Visible := true;
end;
CloseFile(F);
end
else
begin
LblReg1.Visible := true;
LblReg2.Visible := true;
end;
end;
// при показе формы
procedure TFrmLogin.FormShow(Sender: TObject);
begin
if FrmLogin.Caption = 'Смена пользователя' then
begin
LblReg1.Visible := true;
LblReg2.Visible := true;
LblReg1.Caption := 'Введите новые';
LblReg2.Caption := 'логин и пароль';
end;
end;
end.
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Unit4,
Unit5;
type
PReply = ^Reply;
PAdds = ^Adds;
PAddsList = ^AddsList;
Reply = Record
Num: integer;
Name: string;
Adds: PAdds;
Phone: string;
Problem: string;
IncomeDate: TDate;
IncomeTime: TTime;
Ready: string;
Next: PReply;
End;
Adds = Record
Street: string;
Number: string;
Room: string;
End;
AddsList = Record
Adds: PAdds;
Owed: integer;
Next: PAddsList;
End;
TFrmReplyReg = class(TForm)
PnlRegistration: TPanel;
LblName: TLabel;
LblPhone: TLabel;
LblStreet: TLabel;
LblNumber: TLabel;
LblRoom: TLabel;
EdtName: TEdit;
EdtRoom: TEdit;
EdtPhone: TEdit;
EdtStreet: TEdit;
EdtNumber: TEdit;
LblProblem: TLabel;
BtnCheckAdress: TButton;
BtnAddReply: TButton;
PnlCheck: TPanel;
LblCheckAdress: TLabel;
BtnChangeReply: TButton;
CmbReadyness: TComboBox;
LblReadyness: TLabel;
CmbProblem: TComboBox;
BtnAddProblem: TButton;
procedure BtnCheckAdressClick(Sender: TObject);
procedure BtnAddReplyClick(Sender: TObject);
procedure BtnChangeReplyClick(Sender: TObject);
procedure BtnAddProblemClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
procedure SortByProblem;
procedure SortByDate;
{ Public declarations }
end;
var
FrmReplyReg: TFrmReplyReg;
HeadReply: PReply;
ReplyNumber: integer;
HeadAddsList: PAddsList;
implementation
{$R *.dfm}
uses unit1, unit8;
// функция проверки адреса(обслуживает ли данная ЖЭС данный адрес)
function CheckAddress(S, N: string): Boolean;
var
i, j: integer;
begin
if N = 'все' then
Exit
else
begin
Result := false;
repeat
if Pos(S, N) <> 0 then
begin
i := Pos(S, N);
j := i - 1 + Length(S);
if ((i = 1) or (N[i - 1] = ' ') or (N[i - 1] = ',')) and
((j = Length(N)) or (N[j + 1] = ' ') or (N[j + 1] = ',')) then
Result := true
else
begin
if j <> Length(N) then
N := Copy(N, j + 1, Length(N) - j)
else
break;
end;
end;
until (Pos(S, N) = 0) or (Result = true);
end;
end;
// процедура сортировки по неисправности
procedure TFrmReplyReg.SortByProblem;
var
H, N, NHeadReply, Temp: PReply;
RowNum, ColNum: integer;
begin
H := HeadReply;
NHeadReply := nil;
while H <> nil do
begin
// если новый(сортированный) список заявок ещё пуст
if NHeadReply = nil then
begin
New(NHeadReply);
NHeadReply^ := H^;
NHeadReply^.Next := nil;
end
else
begin
N := NHeadReply;
if AnsiLowerCase(H^.Problem) < AnsiLowerCase(N^.Problem) then
begin
Temp := NHeadReply;
New(NHeadReply);
NHeadReply^ := H^;
NHeadReply^.Next := Temp;
end
else
// если очередной элемент старго списка не прошёл условие входа в отсортированный
begin
while N <> nil do
begin
// условие сортировки
if N^.Next = nil then
begin
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := nil;
break;
end;
if AnsiLowerCase(H^.Problem) < AnsiLowerCase(N^.Next^.Problem) then
begin
Temp := N^.Next;
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := Temp;
break;
end;
N := N^.Next;
end;
end;
end;
H := H^.Next;
end;
{ если прошлись уже по всему текущему списку, значит отсортированный список
готов, неотсортированный список больше не нужен, удаляем его
для экономии памяти(повышение эффективности программы) }
if HeadReply <> nil then
begin
while HeadReply <> nil do
begin
Temp := HeadReply;
HeadReply := HeadReply^.Next;
Dispose(Temp);
end;
HeadReply := NHeadReply;
if IsAdded = true then
FrmMain.SgMain.RowCount := FrmMain.SgMain.RowCount + 1;
// печать отсортированного списка
H := HeadReply;
with FrmMain do
for RowNum := 1 to SgMain.RowCount - 1 do
begin
for ColNum := 0 to 6 do
begin
case ColNum of
0:
SgMain.Cells[ColNum, RowNum] := inttostr(H^.Num);
1:
SgMain.Cells[ColNum, RowNum] := H^.Name;
2:
SgMain.Cells[ColNum, RowNum] := H^.Adds^.Street + ', д.' +
H^.Adds^.Number + ', кв.' + H^.Adds^.Room;
3:
SgMain.Cells[ColNum, RowNum] := H^.Phone;
4:
SgMain.Cells[ColNum, RowNum] := H^.Problem;
5:
SgMain.Cells[ColNum, RowNum] := datetostr(H^.IncomeDate) + ', ' +
timetostr(H^.IncomeTime);
6:
SgMain.Cells[ColNum, RowNum] := H^.Ready;
end;
end;
H := H^.Next;
end;
end;
end;
// процедура сортировки по дате
procedure TFrmReplyReg.SortByDate;
// функция сравнения дат
function DateCompare(OldDate, NewDate: TDate;
OldTime, NewTime: TTime): Boolean;
begin
Result := false;
// условие сравнения
if OldDate < NewDate then
Result := true
else if (OldDate = NewDate) and (OldTime < NewTime) then
Result := true;
end;
var
H, N, NHeadReply, Temp: PReply;
RowNum, ColNum: integer;
begin
H := HeadReply;
NHeadReply := nil;
while H <> nil do
begin
// если новый список заявок ещё пуст
if NHeadReply = nil then
begin
New(NHeadReply);
NHeadReply^ := H^;
NHeadReply^.Next := nil;
end
else
begin
N := NHeadReply;
if DateCompare(H^.IncomeDate, N^.IncomeDate, H^.IncomeTime, N^.IncomeTime)
then
begin
Temp := NHeadReply;
New(NHeadReply);
NHeadReply^ := H^;
NHeadReply^.Next := Temp;
end
else
// если очередной элемент старго списка не прошёл условие входа в отсортированный
begin
while N <> nil do
begin
// условие сортировки
if N^.Next = nil then
begin
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := nil;
break;
end;
if DateCompare(H^.IncomeDate, N^.Next^.IncomeDate, H^.IncomeTime,
N^.Next^.IncomeTime) then
begin
Temp := N^.Next;
New(N^.Next);
N^.Next^ := H^;
N^.Next^.Next := Temp;
break;
end;
N := N^.Next;
end;
end;
end;
H := H^.Next;
end;
{ если прошлись уже по всему текущему списку, значит отсортированный список
готов, неотсортированный список больше не нужен, удаляем его
для экономии памяти(повышение эффективности программы) }
if HeadReply <> nil then
begin
while HeadReply <> nil do
begin
Temp := HeadReply;
HeadReply := HeadReply^.Next;
Dispose(Temp);
end;
HeadReply := NHeadReply;
if IsAdded = true then
FrmMain.SgMain.RowCount := FrmMain.SgMain.RowCount + 1;
// печать отсортированного списка
H := HeadReply;
with FrmMain do
for RowNum := 1 to SgMain.RowCount - 1 do
begin
for ColNum := 0 to 6 do
begin
case ColNum of
0:
SgMain.Cells[ColNum, RowNum] := inttostr(H^.Num);
1:
SgMain.Cells[ColNum, RowNum] := H^.Name;
2:
SgMain.Cells[ColNum, RowNum] := H^.Adds^.Street + ', д.' +
H^.Adds^.Number + ', кв.' + H^.Adds^.Room;
3:
SgMain.Cells[ColNum, RowNum] := H^.Phone;
4:
SgMain.Cells[ColNum, RowNum] := H^.Problem;
5:
SgMain.Cells[ColNum, RowNum] := datetostr(H^.IncomeDate) + ', ' +
timetostr(H^.IncomeTime);
6:
SgMain.Cells[ColNum, RowNum] := H^.Ready;
end;
end;
H := H^.Next;
end;
end;
end;
// при нажатии на кнопку '+'
procedure TFrmReplyReg.BtnAddProblemClick(Sender: TObject);
begin
// открытие формы редактирования неисправностей
FrmProblem.ShowModal;
end;
// при нажатии на кнопку 'Добавить'
procedure TFrmReplyReg.BtnAddReplyClick(Sender: TObject);
// Процедура добавления адреса в список адресов
procedure AddsToList(PAddress: PAdds);
var
P, Temp: PAddsList;
RowNum: integer;
begin
// если головного адреса нет, то создаём
if HeadAddsList = nil then
begin
New(HeadAddsList);
New(HeadAddsList^.Adds);
HeadAddsList^.Adds^ := PAddress^;
HeadAddsList^.Owed := 0;
HeadAddsList^.Next := nil;
end
else
begin
// если есть, добавляем в список по условию
P := HeadAddsList;
while P <> nil do
begin
if (PAddress^.Street = P^.Adds^.Street) and
(PAddress^.Number = P^.Adds^.Number) and
(PAddress^.Room = P^.Adds^.Room) then
Exit;
P := P^.Next;
end;
P := HeadAddsList;
if AnsiLowerCase(PAddress^.Street + PAddress^.Number + PAddress^.Room) <
AnsiLowerCase(P^.Adds^.Street + P^.Adds^.Number + P^.Adds^.Room) then
begin
Temp := HeadAddsList;
New(HeadAddsList);
New(HeadAddsList^.Adds);
HeadAddsList^.Adds^ := PAddress^;
HeadAddsList^.Owed := 0;
HeadAddsList^.Next := Temp;
end
else
begin
while P^.Next <> nil do
begin
if AnsiLowerCase(PAddress^.Street + PAddress^.Number + PAddress^.Room)
< AnsiLowerCase(P^.Next^.Adds^.Street + P^.Next^.Adds^.Number +
P^.Next^.Adds^.Room) then
begin
Temp := P^.Next;
New(P^.Next);
New(P^.Next^.Adds);
P^.Next^.Adds^ := PAddress^;
P^.Next^.Owed := 0;
P^.Next^.Next := Temp;
break;
end;
P := P^.Next;
end;
if P^.Next = nil then
begin
New(P^.Next);
New(P^.Next^.Adds);
P^.Next^.Adds^ := PAddress^;
P^.Next^.Owed := 0;
P^.Next^.Next := nil;
end;
end;
end;
// Добавление в таблицу адресов данного
with FrmAddresses do
begin
SgAddresses.RowCount := SgAddresses.RowCount + 1;
P := HeadAddsList;
for RowNum := 1 to SgAddresses.RowCount - 1 do
begin
SgAddresses.Cells[0, RowNum] := P^.Adds^.Street + ', д.' +
P^.Adds^.Number + ', кв.' + P^.Adds^.Room;
if P^.Owed = 0 then
SgAddresses.Cells[1, RowNum] := 'нет'
else
SgAddresses.Cells[1, RowNum] := inttostr(P^.Owed);
P := P^.Next;
end;
end;
end;
var
H: PServiceAdds;
P: PReply;
begin
// Если все поля заполнены
if (EdtName.Text <> '') and (EdtStreet.Text <> '') and (EdtNumber.Text <> '')
and (EdtRoom.Text <> '') and (EdtPhone.Text <> '') and
(CmbProblem.ItemIndex <> -1) then
begin
// если в поле 'Квартира' содержатся запятые или точки
if (Pos(',', EdtRoom.Text) <> 0) or (Pos('.', EdtRoom.Text) <> 0) then
begin
ShowMessage
('В поле ''Квартира'' не должны содержаться запятые или точки.');
Exit;
end;
H := HeadAdds;
// Проверка на вхождение адреса в число обслуживаемых
while H <> nil do
begin
if (AnsiLowerCase(H^.Street) = AnsiLowerCase(EdtStreet.Text)) and
((CheckAddress(EdtNumber.Text, H^.Number) = true) or (H^.Number = 'все'))
then
break;
H := H^.Next;
end;
// Проверка на существование текущей заявки
P := HeadReply;
while P <> nil do
begin
// Если такая заявка уже имеется
if (AnsiLowerCase(EdtStreet.Text) = AnsiLowerCase(P^.Adds^.Street)) and
(AnsiLowerCase(EdtNumber.Text) = AnsiLowerCase(P^.Adds^.Number)) and
(AnsiLowerCase(EdtRoom.Text) = AnsiLowerCase(P^.Adds^.Room)) and
(AnsiLowerCase(CmbProblem.Text) = AnsiLowerCase(P^.Problem)) then
begin
ShowMessage('Данная заявка уже имеется.');
Exit;
end;
// Если данный телефон уже имеется
if (AnsiLowerCase(EdtStreet.Text) <> AnsiLowerCase(P^.Adds^.Street)) and
(AnsiLowerCase(EdtNumber.Text) <> AnsiLowerCase(P^.Adds^.Number)) and
(AnsiLowerCase(EdtRoom.Text) <> AnsiLowerCase(P^.Adds^.Room)) and
(AnsiLowerCase(EdtPhone.Text) = AnsiLowerCase(P^.Phone)) then
begin
ShowMessage('Данный телефон зарегистрирован на другого жителя.');
Exit;
end;
// Если данный адрес уже есть в списке, но зарегистрирован на другое лицо
if (AnsiLowerCase(EdtStreet.Text) = AnsiLowerCase(P^.Adds^.Street)) and
(AnsiLowerCase(EdtNumber.Text) = AnsiLowerCase(P^.Adds^.Number)) and
(AnsiLowerCase(EdtRoom.Text) = AnsiLowerCase(P^.Adds^.Room)) and
((AnsiLowerCase(EdtName.Text) <> AnsiLowerCase(P^.Name)) or
(AnsiLowerCase(EdtPhone.Text) <> AnsiLowerCase(P^.Phone))) then
begin
ShowMessage
('Данный адрес и/или данный телефон относятся к другому жителю.');
Exit;
end;
P := P^.Next;
end;
// Если адрес проверку не прошёл
if H = nil then
begin
ShowMessage('Введённый адрес не обслуживается данной ЖЭС.');
Exit;
end
else
// Если прошёл
begin
// если список не существует
if HeadReply = nil then
begin
// создаём список
New(HeadReply);
with HeadReply^ do
begin
ReplyNumber := 1;
Num := ReplyNumber;
Name := AnsiUpperCase(EdtName.Text[1]) + Copy(EdtName.Text, 2,
Length(EdtName.Text) - 1);
New(Adds);
Adds^.Street := AnsiUpperCase(EdtStreet.Text[1]) +
Copy(EdtStreet.Text, 2, Length(EdtStreet.Text) - 1);
Adds^.Number := EdtNumber.Text;
Adds^.Room := EdtRoom.Text;
Phone := EdtPhone.Text;
Problem := CmbProblem.Text;
IncomeDate := Date;
IncomeTime := Time;
Ready := CmbReadyness.Text;
Next := nil;
ReplyNumber := ReplyNumber + 1;
AddsToList(HeadReply^.Adds);
with FrmMain do
begin
// вывод на экран списка, состоящего из 1 элемента(диска)
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(Num);
SgMain.Cells[1, SgMain.RowCount - 1] := HeadReply^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := Adds^.Street + ', д.' +
Adds^.Number + ', кв.' + Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := Problem;
SgMain.Cells[5, SgMain.RowCount - 1] := datetostr(IncomeDate) + ', '
+ timetostr(IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := Ready;
ShowMessage('Заявка принята.');
end;
end;
end
else
// Если список существует
begin
// Добавляем заявку в список
P := HeadReply;
while P^.Next <> nil do
P := P^.Next;
New(P^.Next);
with P^.Next^ do
begin
Num := ReplyNumber;
P^.Next^.Name := AnsiUpperCase(EdtName.Text[1]) +
Copy(EdtName.Text, 2, Length(EdtName.Text) - 1);
New(Adds);
Adds^.Street := AnsiUpperCase(EdtStreet.Text[1]) +
Copy(EdtStreet.Text, 2, Length(EdtStreet.Text) - 1);
Adds^.Number := EdtNumber.Text;
Adds^.Room := EdtRoom.Text;
Phone := EdtPhone.Text;
Problem := CmbProblem.Text;
IncomeDate := Date;
IncomeTime := Time;
Ready := CmbReadyness.Text;
Next := nil;
ReplyNumber := ReplyNumber + 1;
AddsToList(P^.Next^.Adds);
end;
// Если активирована сортировка по дате
if FrmMain.Rg.ItemIndex = 0 then
begin
SortByDate;
ShowMessage('Заявка принята.');
Exit;
end;
// Если активирована сортировка по неисправности
if FrmMain.Rg.ItemIndex = 1 then
begin
SortByProblem;
ShowMessage('Заявка принята.');
Exit;
end;
with FrmMain, P^.Next^ do
begin
// Если никакая сортировка не активирована, печатаем новую заявку
SgMain.RowCount := SgMain.RowCount + 1;
SgMain.Cells[0, SgMain.RowCount - 1] := inttostr(Num);
SgMain.Cells[1, SgMain.RowCount - 1] := P^.Next^.Name;
SgMain.Cells[2, SgMain.RowCount - 1] := Adds^.Street + ', д.' +
Adds^.Number + ', кв.' + Adds^.Room;
SgMain.Cells[3, SgMain.RowCount - 1] := Phone;
SgMain.Cells[4, SgMain.RowCount - 1] := Problem;
SgMain.Cells[5, SgMain.RowCount - 1] := datetostr(IncomeDate) + ', ' +
timetostr(IncomeTime);
SgMain.Cells[6, SgMain.RowCount - 1] := Ready;
ShowMessage('Заявка принята.');
end;
end;
end;
end
else
ShowMessage('Заполните все поля.');
end;
// При нажатии на кнопку 'Изменить'
procedure TFrmReplyReg.BtnChangeReplyClick(Sender: TObject);
// Процедура корректировки ФИО и телефонов заявок
{ Если ещё какая-нибудь заявка из списка имеет тот же адрес что и изменяемая
и были изменены ФИО и/или телефон, то изменяем и в ней ФИО и/или телефон }
procedure CorrectNamesAndPhones(NameChanged, PhoneChanged: Boolean);
var
P: PReply;
RowNum, ColNum: integer;
begin
with FrmMain do
begin
if HeadReply^.Adds^.Street + ', д.' + HeadReply^.Adds^.Number + ', кв.' +
HeadReply^.Adds^.Room = SgMain.Cells[2, SgMain.Row] then
begin
if NameChanged = true then
HeadReply^.Name := AnsiUpperCase(EdtName.Text[1]) +
Copy(EdtName.Text, 2, Length(EdtName.Text) - 1);
if PhoneChanged = true then
HeadReply^.Phone := EdtPhone.Text;
end;
P := HeadReply;
while P^.Next <> nil do
begin
if P^.Next^.Adds^.Street + ', д.' + P^.Next^.Adds^.Number + ', кв.' +
P^.Next^.Adds^.Room = SgMain.Cells[2, SgMain.Row] then
begin
if NameChanged = true then
P^.Next^.Name := AnsiUpperCase(EdtName.Text[1]) +
Copy(EdtName.Text, 2, Length(EdtName.Text) - 1);
if PhoneChanged = true then
P^.Next^.Phone := EdtPhone.Text;
end;
P := P^.Next;
end;
// Вывод на экран списка заявок
P := HeadReply;
with FrmMain do
for RowNum := 1 to SgMain.RowCount - 1 do
begin
for ColNum := 0 to 6 do
begin
case ColNum of
0:
SgMain.Cells[ColNum, RowNum] := inttostr(P^.Num);
1:
SgMain.Cells[ColNum, RowNum] := P^.Name;
2:
SgMain.Cells[ColNum, RowNum] := P^.Adds^.Street + ', д.' +
P^.Adds^.Number + ', кв.' + P^.Adds^.Room;
3:
SgMain.Cells[ColNum, RowNum] := P^.Phone;
4:
SgMain.Cells[ColNum, RowNum] := P^.Problem;
5:
SgMain.Cells[ColNum, RowNum] := datetostr(P^.IncomeDate) + ', '
+ timetostr(P^.IncomeTime);
6:
SgMain.Cells[ColNum, RowNum] := P^.Ready;
end;
end;
P := P^.Next;
end;
end;
end;
var
P: PReply;
NameChanged, PhoneChanged: Boolean;
begin
// Если все поля заполнены
if (EdtName.Text <> '') and (EdtStreet.Text <> '') and (EdtNumber.Text <> '')
and (EdtRoom.Text <> '') and (EdtPhone.Text <> '') and
(CmbProblem.ItemIndex <> -1) then
begin
// Проверяем изменились ли ФИО и телефон жителя
NameChanged := false;
PhoneChanged := false;
with FrmMain do
begin
if SgMain.Cells[1, SgMain.Row] <> EdtName.Text then
NameChanged := true;
if SgMain.Cells[3, SgMain.Row] <> EdtPhone.Text then
PhoneChanged := true;
end;
// Проверка на существование текущей заявки
P := HeadReply;
while P <> nil do
begin
// Если данная заявка уже имеется
if (P^.Name = EdtName.Text) and (P^.Adds^.Street = EdtStreet.Text) and
(P^.Adds^.Number = EdtNumber.Text) and (P^.Adds^.Room = EdtRoom.Text)
and (P^.Phone = EdtPhone.Text) and (P^.Problem = CmbProblem.Text) and
(P^.Ready = CmbReadyness.Text) then
begin
ShowMessage('Данная заявка уже имеется.');
Exit;
end;
// Если данный телефон используется другим жителем
if (EdtPhone.Text = P^.Phone) and (PhoneChanged = true) then
begin
ShowMessage('Данный телефон зарегистрирован на другого жителя.');
Exit;
end;
P := P^.Next;
end;
P := HeadReply;
// Если изменяем 1ый элемент списка
if inttostr(P^.Num) = FrmMain.SgMain.Cells[0, FrmMain.SgMain.Row] then
begin
// Изменяем поля 1го элемента списка
HeadReply^.Name := AnsiUpperCase(EdtName.Text[1]) +
Copy(EdtName.Text, 2, Length(EdtName.Text) - 1);
HeadReply^.Phone := EdtPhone.Text;
HeadReply^.Problem := CmbProblem.Text;
HeadReply^.Ready := CmbReadyness.Text;
{ Если ещё какая-нибудь заявка из списка имеет тот же адрес что и изменяемая
и были изменены ФИО и/или телефон, то изменяем и в ней ФИО и/или телефон }
CorrectNamesAndPhones(NameChanged, PhoneChanged);
// Если активирована сортировка по неисправности
if FrmMain.Rg.ItemIndex = 1 then
begin
SortByProblem;
ShowMessage('Заявка успешно отредактирована.');
FrmReplyReg.Close;
Exit;
end
else
begin
ShowMessage('Заявка успешно отредактирована.');
FrmReplyReg.Close;
end;
end
else
// Если изменяем не 1ый элемент списка
begin
P := HeadReply;
while inttostr(P^.Next^.Num) <> FrmMain.SgMain.Cells
[0, FrmMain.SgMain.Row] do
P := P^.Next;
with P^.Next^ do
begin
P^.Next^.Name := AnsiUpperCase(EdtName.Text[1]) +
Copy(EdtName.Text, 2, Length(EdtName.Text) - 1);
Phone := EdtPhone.Text;
Problem := CmbProblem.Text;
Ready := CmbReadyness.Text;
end;
{ Если ещё какая-нибудь заявка из списка имеет тот же адрес что и изменяемая
и были изменены ФИО и/или телефон, то изменяем и в ней ФИО и/или телефон }
CorrectNamesAndPhones(NameChanged, PhoneChanged);
// Если активирована сортировка по неисправности
if FrmMain.Rg.ItemIndex = 1 then
begin
SortByProblem;
ShowMessage('Заявка успешно отредактирована.');
FrmReplyReg.Close;
Exit;
end
else
begin
ShowMessage('Заявка успешно отредактирована.');
FrmReplyReg.Close;
end;
end;
end
else
ShowMessage('Заполните все поля.');
end;
// при нажатии на кнопку 'Проверить'
procedure TFrmReplyReg.BtnCheckAdressClick(Sender: TObject);
begin
// открытие формы с адресами, обслуживаемых ЖЭС
FrmServiceAddresses.ShowModal;
end;
// при создании формы
procedure TFrmReplyReg.FormCreate(Sender: TObject);
var
F: TextFile;
NewProblem: string;
begin
// Загрузка неисправностей
AssignFile(F, 'Problems.txt');
if FileExists('Problems.txt') then
begin
Reset(F);
while not Eof(F) do
begin
Readln(F, NewProblem);
CmbProblem.Items.Add(NewProblem);
end;
CmbProblem.ItemIndex := 0;
CloseFile(F);
end;
end;
end.
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls, Vcl.ExtCtrls,
Unit7;
type
PServiceAdds = ^ServiceAdds;
ServiceAdds = Record
Street: string;
Number: string;
Next: PServiceAdds;
End;
TFrmServiceAddresses = class(TForm)
SgAdds: TStringGrid;
PnlAddress: TPanel;
BtnAddAddress: TButton;
BtnChangeAddress: TButton;
BtnDeleteAddress: TButton;
LblAddsOperations: TLabel;
procedure FormCreate(Sender: TObject);
procedure BtnAddAddressClick(Sender: TObject);
procedure BtnDeleteAddressClick(Sender: TObject);
procedure BtnChangeAddressClick(Sender: TObject);
procedure SgAddsDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
State: TGridDrawState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmServiceAddresses: TFrmServiceAddresses;
HeadAdds: PServiceAdds;
Correct: Boolean;
implementation
{$R *.dfm}
// при нажатии на кнопку 'Добавить' новый сервисный адрес
procedure TFrmServiceAddresses.BtnAddAddressClick(Sender: TObject);
begin
with FrmNewAddress do
begin
BtnAddNewAddress.Visible := true;
BtnChangeNewAddress.Visible := false;
EdtNewStreet.Text := '';
EdtNewNumber.Text := '';
end;
// открытие формы для добавления нового сервисного адреса
FrmNewAddress.ShowModal;
end;
// удаление строки из таблицы
procedure GridDeleteRow(RowNumber: Integer);
var
i: Integer;
begin
with FrmServiceAddresses do
begin
SgAdds.Row := RowNumber;
// если удаляется последняя строка таблицы
if SgAdds.Row = SgAdds.RowCount - 1 then
SgAdds.RowCount := SgAdds.RowCount - 1
else
begin
// не последняя
for i := RowNumber to SgAdds.RowCount - 1 do
SgAdds.Rows[i] := SgAdds.Rows[i + 1];
SgAdds.RowCount := SgAdds.RowCount - 1;
end;
end;
end;
// При нажатии на кнопку 'Изменить' сервисный адрес
procedure TFrmServiceAddresses.BtnChangeAddressClick(Sender: TObject);
var
H, Temp: PServiceAdds;
i: Integer;
begin
// если выделили не строку с заголовком
if SgAdds.Row <> 0 then
begin
i := SgAdds.Row - 1;
with FrmNewAddress do
begin
BtnAddNewAddress.Visible := false;
BtnChangeNewAddress.Visible := true;
H := HeadAdds;
while i <> 0 do
begin
H := H^.Next;
i := i - 1;
end;
{ вставляем в редактируемые поля формы изменения адреса улицу и дома изменяемого адреса }
EdtNewStreet.Text := H^.Street;
EdtNewNumber.Text := H^.Number;
Correct := false;
// открытие формы редактирования сервисного адреса
FrmNewAddress.ShowModal;
// если изменения были произведены
if Correct = true then
begin
// Если в списке сервисных адресов только один сервисный адрес
if HeadAdds^.Next = nil then
begin
HeadAdds^.Street := EdtNewStreet.Text;
if EdtNewNumber.Text = '' then
HeadAdds^.Number := 'все'
else
HeadAdds^.Number := EdtNewNumber.Text;
SgAdds.Cells[0, SgAdds.Row] := HeadAdds^.Street;
SgAdds.Cells[1, SgAdds.Row] := HeadAdds^.Number;
ShowMessage('Адрес успешно отредактирован.');
end
else
// Если в списке адресов больее одного сервисного адреса
begin
// Извлекаем адрес Temp из списка сервисных адресов HeadAdds
Temp := H;
if Temp = HeadAdds then
HeadAdds := HeadAdds^.Next
else
begin
H := HeadAdds;
while H^.Next <> Temp do
H := H^.Next;
H^.Next := H^.Next^.Next;
end;
Temp^.Street := EdtNewStreet.Text;
if EdtNewNumber.Text = '' then
Temp^.Number := 'все'
else
Temp^.Number := EdtNewNumber.Text;
// Вставляем отредактированный Temp в список адресов HeadAdds
if Temp^.Street < HeadAdds^.Street then
begin
Temp^.Next := HeadAdds;
HeadAdds := Temp;
end
else
begin
H := HeadAdds;
while H^.Next <> nil do
if H^.Next^.Street < Temp^.Street then
H := H^.Next
else
break;
Temp^.Next := H^.Next;
H^.Next := Temp;
end;
// Печать изменённого списка адресов
H := HeadAdds;
i := 1;
while H <> nil do
begin
SgAdds.Cells[0, i] := H^.Street;
SgAdds.Cells[1, i] := H^.Number;
H := H^.Next;
i := i + 1;
end;
ShowMessage('Адрес успешно отредактирован.');
end;
end;
end;
end;
end;
// При нажатии на кнопку 'Удалить'
procedure TFrmServiceAddresses.BtnDeleteAddressClick(Sender: TObject);
var
H, Temp: PServiceAdds;
begin
// если выделена не заглавная строка
if SgAdds.Row <> 0 then
begin
// если выделена первая строка, удаляется головной элемент из списка
if (HeadAdds^.Street = SgAdds.Cells[0, SgAdds.Row]) and
(HeadAdds^.Number = SgAdds.Cells[1, SgAdds.Row]) then
begin
Temp := HeadAdds;
HeadAdds := HeadAdds^.Next;
Dispose(Temp);
end
else
begin
H := HeadAdds;
// если выделена произвольная(не первая и не нулевая) строка
while (H^.Next^.Street <> SgAdds.Cells[0, SgAdds.Row]) and
(H^.Next^.Number <> SgAdds.Cells[1, SgAdds.Row]) do
H := H^.Next;
Temp := H^.Next;
H^.Next := H^.Next^.Next;
Dispose(Temp);
end;
// удаление строки из таблицы
GridDeleteRow(SgAdds.Row);
end;
end;
// при создании формы
procedure TFrmServiceAddresses.FormCreate(Sender: TObject);
var
H: PServiceAdds;
begin
SgAdds.Cells[0, 0] := ' Улица';
SgAdds.Cells[1, 0] := ' Дом';
SgAdds.ColWidths[1] := 210;
SgAdds.Width := 415;
// Печать таблицы сервисных адресов
H := HeadAdds;
while H <> nil do
begin
SgAdds.RowCount := SgAdds.RowCount + 1;
SgAdds.Cells[0, SgAdds.RowCount - 1] := H^.Street;
SgAdds.Cells[1, SgAdds.RowCount - 1] := H^.Number;
H := H^.Next;
end;
end;
// делает таблицу сервисных адресов шире, если элементов более 7, чтобы поместился ScrollBar
procedure TFrmServiceAddresses.SgAddsDrawCell(Sender: TObject;
ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
begin
if (SgAdds.RowCount > 7) and (SgAdds.Width = 415) then
SgAdds.Width := 433;
if (SgAdds.Width = 433) and (SgAdds.RowCount < 8) then
SgAdds.Width := 415;
end;
end.
unit Unit5;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFrmProblem = class(TForm)
EdtNewProblem: TEdit;
BtnAddNewProblem: TButton;
BtnDelNewProblem: TButton;
procedure BtnAddNewProblemClick(Sender: TObject);
procedure BtnDelNewProblemClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmProblem: TFrmProblem;
implementation
{$R *.dfm}
uses unit1, unit2, unit3;
// при нажатии на кнопку 'Добавить'
procedure TFrmProblem.BtnAddNewProblemClick(Sender: TObject);
var
i: integer;
begin
// если введена не пустая строка
if EdtNewProblem.Text <> '' then
with FrmReplyReg do
begin
// проверка на уникальность неисправности
for i := 0 to CmbProblem.Items.Count - 1 do
if CmbProblem.Items[i] = EdtNewProblem.Text then
begin
ShowMessage('Данная неисправность уже имеется.');
Exit;
end;
// добавление новой неисправности в выпадающее меню
CmbProblem.Items.Add(EdtNewProblem.Text);
ShowMessage('Неисправность успешно добавлена.');
if CmbProblem.ItemIndex = -1 then
CmbProblem.ItemIndex := 0;
end;
end;
// при нажатии на кнопку 'Удалить'
procedure TFrmProblem.BtnDelNewProblemClick(Sender: TObject);
var
i: integer;
begin
// если введена не пустая строка
if EdtNewProblem.Text <> '' then
with FrmReplyReg do
for i := 0 to CmbProblem.Items.Count - 1 do
// если текст неисправности = введённому в поле, то она удаляется из выпадающего меню
if CmbProblem.Items[i] = EdtNewProblem.Text then
begin
CmbProblem.Items.Delete(i);
ShowMessage('Неисправность успешно удалена.');
CmbProblem.ItemIndex := 0;
Exit;
end;
end;
// при создании формы
procedure TFrmProblem.FormCreate(Sender: TObject);
begin
// скрывает основную форму, открывает окно логина
FrmMain.Hide;
FrmLogin.ShowModal;
end;
end.
unit Unit6;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VCLTee.TeEngine, VCLTee.Series,
VCLTee.TeeProcs, VCLTee.Chart, Vcl.ExtCtrls, Vcl.StdCtrls, DateUtils,
VCLTee.tecanvas;
type
TFrmGraph = class(TForm)
PnlGraph: TPanel;
LblReply: TLabel;
CmbDate: TComboBox;
BtnShowGraph: TButton;
Chart1: TChart;
Series1: TBarSeries;
procedure BtnShowGraphClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmGraph: TFrmGraph;
implementation
{$R *.dfm}
uses unit3;
// при нажатии на кнопку 'Показать'
procedure TFrmGraph.BtnShowGraphClick(Sender: TObject);
var
H: PReply;
S: string;
i: integer;
Day: array [1 .. 24] of integer;
Week: array [1 .. 7] of integer;
Month: array of integer;
Year: array [1 .. 12] of integer;
begin
// если показываем заявки за день
if CmbDate.ItemIndex = 0 then
begin;
Chart1.Visible := true;
Series1.Clear;
Chart1.LeftAxis.LabelsFont.Size := 8;
Chart1.BottomAxis.LabelsFont.Size := 8;
Chart1.BottomAxis.Maximum := 24.8;
H := HeadReply;
// заполняем массив нулями
// индекс массива - в каком часу поступила заявка
for i := 1 to 24 do
Day[i] := 0;
// проходим по всему списку заявок
while H <> nil do
begin
{ если дата поступления заявки = сегодняшней, то добавляем в массив
в зависимости от времени поступления }
if H^.IncomeDate = Date then
begin
S := Copy(TimeToStr(H^.IncomeTime), 1,
Pos(':', TimeToStr(H^.IncomeTime)) - 1);
Day[strtoint(S)] := Day[strtoint(S)] + 1;
end;
H := H^.Next;
end;
// если кол-во заявок за i-ый час не = 0, отображаем это на графике
for i := 1 to 24 do
begin
if Day[i] <> 0 then
Series1.AddXY(i, Day[i]);
end;
end;
// если показываем заявки за неделю
if CmbDate.ItemIndex = 1 then
begin
Chart1.Visible := true;
Series1.Clear;
Chart1.LeftAxis.LabelsFont.Size := 8;
Chart1.BottomAxis.LabelsFont.Size := 8;
Chart1.BottomAxis.Maximum := 7.8;
H := HeadReply;
// заполняем массив нулями
// индекс массива - в какой день недели поступила заявка
for i := 1 to 7 do
Week[i] := 0;
// проходим по всему списку заявок
while H <> nil do
begin
{ если на этой недели заявка была добавлена в список, то увеличиваем
соответствующее значение в массиве }
if DaysBetween(Date, H^.IncomeDate) <= DayOfTheWeek(Date) - 1 then
Week[DayOfTheWeek(Date) - DaysBetween(Date, H^.IncomeDate)] :=
Week[DayOfTheWeek(Date) - DaysBetween(Date, H^.IncomeDate)] + 1;
H := H^.Next;
end;
// печатаем график
for i := 1 to 7 do
case i of
1:
Series1.AddXY(1, Week[i], 'Понедельник');
2:
Series1.AddXY(2, Week[i], 'Вторник');
3:
Series1.AddXY(3, Week[i], 'Среда');
4:
Series1.AddXY(4, Week[i], 'Четверг');
5:
Series1.AddXY(5, Week[i], 'Пятница');
6:
Series1.AddXY(6, Week[i], 'Суббота');
7:
Series1.AddXY(7, Week[i], 'Воскресенье');
end;
end;
// если показываем заявки за месяц
if CmbDate.ItemIndex = 2 then
begin
Chart1.Visible := true;
Series1.Clear;
Chart1.LeftAxis.LabelsFont.Size := 7;
Chart1.BottomAxis.LabelsFont.Size := 7;
if MonthOf(Date) = 2 then
Chart1.BottomAxis.Maximum := MonthDays[IsLeapYear(YearOf(Date))][2] + 0.8
else
Chart1.BottomAxis.Maximum := MonthDays[IsLeapYear(YearOf(Date))
][MonthOf(Date)] + 0.8;
H := HeadReply;
// устанавливаем длину массива = количеству дней в текущем месяце
SetLength(Month, trunc(Chart1.BottomAxis.Maximum));
// заполняем массив нулями
for i := 0 to Length(Month) - 1 do
Month[i] := 0;
// проходимся по всему списку
while H <> nil do
begin
{ если заявка поступила в текущем месяце, увеличиваем соответствующее
значение в массиве }
if (MonthOf(Date) = MonthOf(H^.IncomeDate)) and
(YearOf(Date) = YearOf(H^.IncomeDate)) then
Month[DayOfTheMonth(H^.IncomeDate) - 1] :=
Month[DayOfTheMonth(H^.IncomeDate) - 1] + 1;
H := H^.Next;
end;
// если кол-во заявок за i-ый день месяца не = 0, отображаем это на графике
for i := 0 to Length(Month) - 1 do
if Month[i] <> 0 then
Series1.AddXY(i + 1, Month[i]);
end;
// если показываем заявки за год
if CmbDate.ItemIndex = 3 then
begin
Chart1.Visible := true;
Series1.Clear;
Chart1.LeftAxis.LabelsFont.Size := 7;
Chart1.BottomAxis.LabelsFont.Size := 7;
Chart1.BottomAxis.Maximum := 12.8;
H := HeadReply;
// заполняем массив нулями
for i := 1 to 12 do
Year[i] := 0;
// проходимся по всему списку
while H <> nil do
begin
{ если заявка была добавлена в этом году, увеличиваем соответствующее
значение в массиве }
if YearOf(Date) = YearOf(H^.IncomeDate) then
Year[MonthOf(H^.IncomeDate)] := Year[MonthOf(H^.IncomeDate)] + 1;
H := H^.Next;
end;
// печатаем график
for i := 1 to 12 do
case i of
1:
Series1.AddXY(1, Year[i], 'Январь');
2:
Series1.AddXY(2, Year[i], 'Февраль');
3:
Series1.AddXY(3, Year[i], 'Март');
4:
Series1.AddXY(4, Year[i], 'Апрель');
5:
Series1.AddXY(5, Year[i], 'Май');
6:
Series1.AddXY(6, Year[i], 'Июнь');
7:
Series1.AddXY(7, Year[i], 'Июль');
8:
Series1.AddXY(8, Year[i], 'Август');
9:
Series1.AddXY(9, Year[i], 'Сентябрь');
10:
Series1.AddXY(10, Year[i], 'Октябрь');
11:
Series1.AddXY(11, Year[i], 'Ноябрь');
12:
Series1.AddXY(12, Year[i], 'Декабрь');
end;
end;
end;
// при создании
procedure TFrmGraph.FormCreate(Sender: TObject);
begin
// не показывает график
Chart1.Visible := false;
end;
end.
unit Unit7;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFrmNewAddress = class(TForm)
EdtNewStreet: TEdit;
EdtNewNumber: TEdit;
BtnAddNewAddress: TButton;
LblStreet: TLabel;
LblNumber: TLabel;
BtnChangeNewAddress: TButton;
procedure BtnAddNewAddressClick(Sender: TObject);
procedure BtnChangeNewAddressClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmNewAddress: TFrmNewAddress;
implementation
{$R *.dfm}
uses unit4;
// При нажатии на кнопку 'Добавить'
procedure TFrmNewAddress.BtnAddNewAddressClick(Sender: TObject);
// Добавление сервисного адреса в список
function AddNewAdds: boolean;
var
H, Temp: PServiceAdds;
begin
Result := false;
H := HeadAdds;
// проверяем сервисный адрес на уникальность
while H <> nil do
begin
if AnsiLowerCase(H^.Street) = AnsiLowerCase(EdtNewStreet.Text) then
begin
ShowMessage('Данный адрес уже имеется в списке.');
Exit;
end;
H := H^.Next;
end;
// если список сервисных адресов пуст, то создаём его
if HeadAdds = nil then
begin
New(HeadAdds);
HeadAdds^.Street := AnsiUpperCase(EdtNewStreet.Text[1]) +
Copy(EdtNewStreet.Text, 2, Length(EdtNewStreet.Text) - 1);
if EdtNewNumber.Text = '' then
HeadAdds^.Number := 'все'
else
HeadAdds^.Number := EdtNewNumber.Text;
HeadAdds^.Next := nil;
end
else
begin
// добавляем адрес в список сервисных адресов
if EdtNewStreet.Text < HeadAdds^.Street then
begin
New(H);
H^.Street := AnsiUpperCase(EdtNewStreet.Text[1]) +
Copy(EdtNewStreet.Text, 2, Length(EdtNewStreet.Text) - 1);
if EdtNewNumber.Text = '' then
H^.Number := 'все'
else
H^.Number := EdtNewNumber.Text;
H^.Next := HeadAdds;
HeadAdds := H;
end
else
begin
H := HeadAdds;
while H^.Next <> nil do
begin
if EdtNewStreet.Text < H^.Next^.Street then
begin
New(Temp);
Temp^.Street := AnsiUpperCase(EdtNewStreet.Text[1]) +
Copy(EdtNewStreet.Text, 2, Length(EdtNewStreet.Text) - 1);
if EdtNewNumber.Text = '' then
Temp^.Number := 'все'
else
Temp^.Number := EdtNewNumber.Text;
Temp^.Next := H^.Next;
H^.Next := Temp;
Result := true;
Exit;
end
else
H := H^.Next;
end;
New(H^.Next);
H^.Next^.Street := AnsiUpperCase(EdtNewStreet.Text[1]) +
Copy(EdtNewStreet.Text, 2, Length(EdtNewStreet.Text) - 1);
if EdtNewNumber.Text = '' then
H^.Next^.Number := 'все'
else
H^.Next^.Number := EdtNewNumber.Text;
H^.Next^.Next := nil;
end;
end;
Result := true;
end;
// Добавление сервисного адреса в таблицу
procedure PrintNewAdds;
var
i: integer;
H: PServiceAdds;
begin
with FrmServiceAddresses do
begin
SgAdds.RowCount := SgAdds.RowCount + 1;
H := HeadAdds;
for i := 1 to SgAdds.RowCount - 1 do
begin
SgAdds.Cells[0, i] := H^.Street;
SgAdds.Cells[1, i] := H^.Number;
H := H^.Next;
end;
end;
end;
begin
// если поле 'Улица' заполнено, оба поля не содержат точек и запятых
if EdtNewStreet.Text <> '' then
begin
if (Pos(',', EdtNewStreet.Text) <> 0) or (Pos('.', EdtNewStreet.Text) <> 0)
or (Pos(',', EdtNewNumber.Text) <> 0) or (Pos('.', EdtNewNumber.Text) <> 0)
then
begin
ShowMessage
('В полях ''Улица'' и ''Дом'' не должны содержаться запятые или точки.');
Exit;
end;
// если сервисный адрес добавляется в список, то он добавляется в таблицу
if AddNewAdds = true then
begin
PrintNewAdds;
ShowMessage('Адрес успешно добавлен в список.');
end;
end
else
ShowMessage('Заполните необходимые поля.');
end;
// Функция проверки наличия сервисного адреса в списке
function CheckAdds: boolean;
var
H: PServiceAdds;
i: integer;
begin
Result := false;
i := FrmServiceAddresses.SgAdds.Row - 1;
H := HeadAdds;
while H <> nil do
begin
if (H^.Street = FrmNewAddress.EdtNewStreet.Text) and (i <> 0) then
begin
Result := true;
Exit;
end;
H := H^.Next;
i := i - 1;
end;
end;
// При нажатии на кнопку 'Изменить'
procedure TFrmNewAddress.BtnChangeNewAddressClick(Sender: TObject);
begin
{ если поле 'Улица' заполнено, оба поля не содержат точек и запятых и данного сервисного
адреса нет в списке, то указываем, что Correct = true, то есть сервисный адрес будет
изменён }
if EdtNewStreet.Text <> '' then
begin
if (Pos(',', EdtNewStreet.Text) <> 0) or (Pos('.', EdtNewStreet.Text) <> 0)
or (Pos(',', EdtNewNumber.Text) <> 0) or (Pos('.', EdtNewNumber.Text) <> 0)
then
begin
ShowMessage
('В полях ''Улица'' и ''Дом'' не должны содержаться запятые или точки.');
Exit;
end;
if CheckAdds = true then
ShowMessage
('Данная улица уже имеется в списке либо никаких изменений не было произведено.')
else
begin
Correct := true;
FrmNewAddress.Close;
end;
end
else
ShowMessage('Заполните необходимые поля.');
end;
end.
unit Unit8;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.StdCtrls, Vcl.ExtCtrls,
unit9;
type
TFrmAddresses = class(TForm)
SgAddresses: TStringGrid;
PnlOweChange: TPanel;
BtnOweChange: TButton;
procedure FormCreate(Sender: TObject);
procedure SgAddressesDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure BtnOweChangeClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmAddresses: TFrmAddresses;
implementation
{$R *.dfm}
uses unit1, unit3;
procedure TFrmAddresses.BtnOweChangeClick(Sender: TObject);
begin
if SgAddresses.Row <> 0 then
FrmOweChange.ShowModal;
end;
// при создании формы
procedure TFrmAddresses.FormCreate(Sender: TObject);
var
P: PAddsList;
begin
SgAddresses.ColWidths[1] := 150;
SgAddresses.Cells[0, 0] := ' Адрес';
SgAddresses.Cells[1, 0] := ' Задолженность';
// вывод на экран списка адресов жильцов
P := HeadAddsList;
while P <> nil do
begin
with FrmAddresses do
begin
SgAddresses.RowCount := SgAddresses.RowCount + 1;
SgAddresses.Cells[0, SgAddresses.RowCount - 1] := P^.Adds^.Street + ', д.'
+ P^.Adds^.Number + ', кв.' + P^.Adds^.Room;
if P^.Owed <> 0 then
SgAddresses.Cells[1, SgAddresses.RowCount - 1] := inttostr(P^.Owed)
else
SgAddresses.Cells[1, SgAddresses.RowCount - 1] := 'нет';
end;
P := P^.Next;
end;
end;
// делает таблицу адресов шире, если элементов более 11, чтобы поместился ScrollBar
procedure TFrmAddresses.SgAddressesDrawCell(Sender: TObject;
ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
begin
if (SgAddresses.RowCount > 11) and (SgAddresses.Width = 406) then
begin
FrmAddresses.Width := 430;
SgAddresses.Width := 424;
end;
if (SgAddresses.Width = 424) and (SgAddresses.RowCount < 12) then
begin
FrmAddresses.Width := 412;
SgAddresses.Width := 406;
end;
end;
end.
unit Unit9;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TFrmOweChange = class(TForm)
LblAddress: TLabel;
LblAddressChange: TLabel;
LblOwe: TLabel;
EdtOweChange: TEdit;
BtnChangeOwe: TButton;
procedure FormShow(Sender: TObject);
procedure BtnChangeOweClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FrmOweChange: TFrmOweChange;
implementation
{$R *.dfm}
uses unit8, unit3;
// при нажатии на кнопку 'Редактировать' задолженность
procedure TFrmOweChange.BtnChangeOweClick(Sender: TObject);
var
P: PAddsList;
AddsNumber: integer;
S: string;
begin
with FrmAddresses do
begin
// смена задолженности в таблице
S := EdtOweChange.Text;
if S <> '' then
while S[1] = '0' do
begin
S := Copy(S, 2, Length(S) - 1);
if S = '' then
break;
end;
if S = '' then
SgAddresses.Cells[1, SgAddresses.Row] := 'нет'
else
SgAddresses.Cells[1, SgAddresses.Row] := S;
// выбираем элемент списка адресов, чьё поле задолженности будем менять
P := HeadAddsList;
AddsNumber := SgAddresses.Row;
while AddsNumber <> 1 do
begin
P := P^.Next;
AddsNumber := AddsNumber - 1;
end;
end;
// меняем поле задолженности
if S = '' then
P^.Owed := 0
else
P^.Owed := strtoint(S);
ShowMessage('Задолженность успешно отредактирована.');
FrmOweChange.Close;
end;
// при показе формы
procedure TFrmOweChange.FormShow(Sender: TObject);
begin
with FrmAddresses do
begin
// инициализация текста полей ввода
LblAddressChange.Caption := SgAddresses.Cells[0, SgAddresses.Row];
if SgAddresses.Cells[1, SgAddresses.Row] <> 'нет' then
EdtOweChange.Text := SgAddresses.Cells[1, SgAddresses.Row]
else
EdtOweChange.Text := '';
end;
end;
end.