У вас вопросы?
У нас ответы:) SamZan.net

на тему Программа для регистрации заявок жилищнокоммунальной службы БГУИР КП 140 01 01 05 ПЗ

Работа добавлена на сайт samzan.net: 2016-03-13

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 1.2.2025

Министерство образования Республики Беларусь

Учреждение образования «Белорусский государственный университет информатики и радиоэлектроники»

Факультет компьютерных систем и сетей

Кафедра программного обеспечения информационных технологий

Дисциплина:  Основы алгоритмизации и программирование (ОАиП)

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

к курсовой работе

на тему

Программа для регистрации заявок жилищно-коммунальной службы

БГУИР КП  1-40 01 01  05  ПЗ

Студент:  гр. 251001 Выжимко А.И.

          Руководитель: асс. Болтак С.В.

Минск 2013


СОДЕРЖАНИЕ

ВВЕДЕНИЕ…………………………………………………………………………5

  1.  Аналитический обзор литературы и существующих аналогов
  2.   Аналитический обзор литературы………………..………………….…...6

     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.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.2 Обзор существующих аналогов

На данный момент существуют различные аналоги данной программы. Ниже описан интерфейс одной из таковых(рис 1.3). Данная программа обладает большим набором возможностей учёта заявок в различных сферах обслуживания.

Рис. 1.3 - Аналог программы регистрации заявок

Вот некоторые из функций программы:

  1.  Программа учета абонентов. Работа с населением.
  2.  Система управления государственными предприятиями автоматически производит начисление в начале месяца за любые предоставляемые услуги;
  3.  Любые дополнительные начисления абонентским отделом;
  4.  Автоматическое и ручное начисление пени неплательщикам;
  5.  Контроль водоканала поддерживает работу по любым тарифам, в том числе и по дифференцированным;
  6.  Программа учета электроэнергии (электрической энергии);
  7.  Управление коммунальное предприятие включает современный анализ хозяйственной деятельности, учет расчетов;
  8.  Учет в жкх, контроль гкп поддерживает учет водоснабжения;

Недостатки программы:

  1.  Переизбыток функций;
  2.  Нет статистики и графика поступления заявок;


2. Разработка алгоритма

Приведенные ниже схемы иллюстрируют структуру хранения данных на базе односвязных динамических списков(рис. 2.1, 2.2, 2.4).                                                                           

Рисунок 2.1 – Схема структуры списка заявок

Рисунок 2.2 – Схема структуры списка адресов жильцов

Рисунок 2.3 – Схема структуры адреса

Рисунок 2.4 – Схема структуры списка обслуживаемых адресов

Предложенная структура данных позволяет динамически добавлять заявки и адреса в «базу данных». Также предусмотрено изменение и удаление элементов из описанных выше списков.

Алгоритм предусматривает следующие возможности:

  1.  добавление заявки в список;                             
  2.  изменение заявки в списке;                             
  3.  удаление заявки из списка;                             
  4.  добавление, изменение, удаление обслуживаемых адресов;                             
  5.  сортировка списка заявок по дате и по неисправности;
  6.  поиск заявки по номеру или по ФИО жильца;
  7.  вывод на экран и в текстовый файл заявок за определённый временной интервал;
  8.  вывод на экран и в текстовый файл незакрытых заявок;
  9.  просмотр списка адресов жильцов;
  10.  просмотр графика поступления заявок за определённый временной интервал;
  11.  автоматическое сохранение описанных выше списков в текстовый файл при выходе из программы;
  12.  автоматическая загрузка описанных выше списков из текстового файла при запуске программы;

Использованные в программе алгоритмы основаны на основных приемах работы с динамическими списками, рассмотренных в соответствующем разделе.

Рисунок 2.5 – Схема программы

Рисунок 2.6 – Схема процедуры добавления заявки

Рисунок 2.7 – Схема процедуры изменения заявки

Рисунок 2.8 – Схема процедуры удаления заявки


3. Разработка программного средства

Для разработки программного средства использовалась среда программирования Delphi - Embarcadero RAD studio XE2, предоставляющая возможность визуального создания программ.

Все возможные операции со всеми списками были разделены на группы по смыслу. В программе для удобства каждая группа операций находится на своей панели, что облегчает поиск необходимой операции. Поэтому были созданы панели операций над заявками, сортировки заявок, поиска заявок а также панель статистики поступлений заявок. В главном окне программы находится главный список - список заявок, отображаемый в таблице. Для произведения операций с другими списками используются кнопки для переключения на другие окна, в которых находятся информация и операции по другим спискам. Было создано главное меню, с помощью которого можно обновить список заявок, что используется после поиска заявки, показать список адресов жильцов, сменить логин и пароль пользователя а также выйти из программы.


4. Технические приемы программирования

1) При разработке интерфейса программы были использованы следующие компоненты Delphi:

  1.  TLabel для установки поясняющих текстовых меток;
  2.  TEdit для ввода данных;
  3.  TCombobox для выбора неисправности при регистрации а также при выборе интервала времени на графике;
  4.  TButton для выполнения определённого действия по нажатию кнопки;
  5.  TRadioGroup для выбора типа сортировки;
  6.  TStringGrid для отображения списков;
  7.  TPanel для удобства размещения операций;
  8.  TChart для отображения графика.

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; лифт; не готово

Данные из полей ввода прочитаны, проверены на корректность, в список занесена новая заявка, содержащая введенные данные.

Содержимое полей ввода:
Михалков Р.Н.; ул. Гикало; 34; 26; 758493; техобслуживание; не готово

Данные из полей ввода прочитаны, проверены на корректность, но в список новая заявка не занесена, т.к. данный телефон используется другим жителем, выдано соответствующее сообщение.

Change

Reply

Принимает введенные пользователем в поля ввода данные о жителе.

Чтение данных, проверка и изменение в списке данных о заявке.

Содержимое полей ввода:
Пушкин А.С.; ул. Строителей; 42; 54; 907356; лифт; готово

Данные из полей ввода прочитаны, проверены на корректность, в списке изменились данные заявки.

Содержимое полей ввода:
Крестов А.И.; ул. Строителей; 42; 54; 643648; водоснабжение;  не готово

Данные из полей ввода прочитаны, проверены на корректность, в списке данные заявки  не изменились, т.к. введённый адрес занят, выдано соответствующее сообщение.

DeleteReply

Принимает данные о заявке из полей выделенной строки таблицы.



Поиск и удаление из списка заявки с заданными данными.

Содержимое полей строки таблицы:
Пушкин А.С.; ул. Строителей; 42; 54; 907356; лифт; готово

Был найден и удален из списка диск с заданными данными.

SearchBy

Number

Принимает введенный пользователем в поле ввода номер заявки.

Чтение введенных данных,  вывод на экран найденной по данному запросу заявки.

Содержимое поля ввода:
2

Данные прочитаны, поиск заявки с введённым номером, на экран была выведена заявка .

Содержимое поля ввода:

7

Данные прочитаны, поиск заявки с введённым номером, на экран не была выведена заявка , т.к. заявки с заданным номером не существует, выдано соответствующее сообщение.

SortByName

Сортирует список заявок по неисправности в алфавитном порядке.

Список успешно отсортировался.

В процессе тестирования были проверены все операции со списками с разными входными данными, ошибки в программе не нашлись.


6. Руководство пользователя

При запуске программы появляется окно авторизации(рис. 6.1):

Рисунок  6.1

Если вы впервые запускаете программу, вам необходимо зарегистрироваться. Для этого введите логин и пароль в соответствующие поля. При последующих запусках программы необходимо будет ввести эти данные. Если вы неправильно ввели пароль или логин, вы увидите следующую надпись(рис. 6.2):

Рисунок  6.2

При авторизации в системе мы попадаем на главное окно программы.

На главном окне находится таблица, а также операции, выполняемые над её элементами(заявками). Если после предыдущего запуска программы были сохранены заявки, то они автоматически загружаются в таблицу(рис. 6.3).

Рисунок  6.3

В программе доступны следующие операции над заявками:

  1.  Добавить
  2.  Изменить
  3.  Удалить
  4.  Поиск
  5.  Сортировка
  6.  Статистика

6.1   Добавить заявку

Для добавления заявки в список нажмите на кнопку "Добавить" на панели операций, заполните все поля в открывшейся форме(рис. 6.4) и нажмите "Добавить".

Рисунок 6.4

При неверном вводе данных заявки могут появиться следующие сообщения об ошибках:

  1.  При добавлении заявки, которая уже есть в списке; при изменении заявки на такую, которая уже есть в списке(рис. 6.5):

Рисунок 6.5

  1.  При добавлении или изменении заявки, содержащей используемый другим человеком телефон в поле 'телефон'(рис. 6.6):

Рисунок 6.6

  1.  При добавлении или изменении диска при вводе в поля адреса, не обслуживаемого данной ЖЭС(рис. 6.7):

Рисунок 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, эффективно использует предоставляемые данной средой возможности. Таким образом, в процессе разработки данного программного средства мы на практике убедились в преимуществах работы с динамическими структурами данных.

В результате тестирования приложения было показано, что полученные результаты работы подпрограмм на конкретных рассмотренных (корректных и некорректных) наборах данных соответствуют ожидаемым, т. е. основные подпрограммы функционируют нормально.

В программе предусмотрен ряд естественных ограничений, продиктованных смыслом поставленной задачи и особенностями ее реализации в действительности. Данные ограничения подробно документированы в пункте «Руководство пользователя».


Список используемой литературы

  1.  Вирт Н.  Алгоритмы и структуры данных. – . М.: Мир, 1989.
  2.  Сухарев М. В. Основы Delphi. Профессиональный подход.
  3.  Осипов Д. Delphi. Профессиональное программирование, 2006.


Листинг программы

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.




1. Суть і призначення ліквідаційної процедури
2. РЕФЕРАТ ДИСЕРТАЦІЇ НА ЗДОБУТТЯ НАУКОВОГО СТУПЕНЯ ДОКТОРА АРХІТЕКТУРИ Харків 1
3. Магматические породы нормального ряда
4. конспект лекций Реферат Невербальные формы коммуникации- функции структура правила формы национальны
5. Перемены К началу августа старшие братья наконецто вплотную занялись подготовкой к новому турне Майкла
6. Критические схолии к мирной теореме
7. Право власності.html
8. Информационный стресc в учебной деятельности
9. не освещали в прессе
10. тримоксазол Бициллин1 Викасол Гепарин Глибенкламид маннинил Диазепам сибазон Диг