Similar presentations:
Проектирование и разработка автоматизированной информационной системы курьерской службы
1.
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РЕСПУБЛИКИ ТАТАРСТАНГосударственное автономное профессиональное образовательное
учреждение
Казанский педагогический колледж
ПРОЕКТИРОВАНИЕ И РАЗРАБОТКА АВТОМАТИЗИРОВАННОЙ
ИНФОРМАЦИОННОЙ СИСТЕМЫ КУРЬЕРСКОЙ СЛУЖБЫ
Курсовая работа
Исполнитель:
Насипов Амир Илгамович
студент 3 курса 931 группы
очной формы обучения
По специальности 09.02.07
«Информационные системы и
Программировние»
Руководитель:
Саляхова Фануза Мунировна
Казань 2026
2.
ОГЛАВЛЕНИЕВВЕДЕНИЕ .......................................................................................................... 3
ГЛАВА
1.
ТЕОРЕТИЧЕСКИЕ
ОСНОВЫ
АВТОМАТИЗАЦИИ
КУРЬЕРСКОЙ СЛУЖБЫ .................................................................................... 5
1.1. Анализ деятельности курьерской службы как объекта автоматизации ..... 5
1.2. Обоснование выбора технологий разработки .............................................. 9
ГЛАВА
2.
ПРОЕКТИРОВАНИЕ
АВТОМАТИЗИРОВАННОЙ
И
ИНФОРМАЦИОННОЙ
РЕАЛИЗАЦИЯ
СИСТЕМЫ
КУРЬЕРСКОЙ СЛУЖБЫ .................................................................................. 12
2.1. Анализ предметной области ....................................................................... 12
2.2. Реализация проекта базы данных ............................................................... 15
ЗАКЛЮЧЕНИЕ .................................................................................................. 23
БИБЛИОГРАФИЧЕСКИЙ СПИСОК ............................................................... 24
ПРИЛОЖЕНИЯ.................................................................................................. 28
2
3.
ВВЕДЕНИЕАктуальность: в современном мире электронная коммерция и услуги
доставки играют ключевую роль в экономике. Курьерские службы
обеспечивают «последнюю милю» – наиболее сложный и затратный этап
логистики. Малый бизнес часто сталкивается с проблемой хаотичного
управления заказами с помощью бумажных журналов или Excel-таблиц, что
ведёт к ошибкам, задержкам и финансовым потерям. В связи с этим
разработка автоматизированной информационной системы (АИС) для
курьерской службы является актуальной задачей, имеющей практическое
значение.
Проблема: существующие готовые решения часто избыточно сложны
и дороги для малого бизнеса, что порождает следующие проблемы:
информационный
хаос
(потеря
заказов,
дублирование
данных),
неэффективное распределение курьеров, трудности с ручным расчётом
зарплаты, отсутствие оперативной аналитики и невозможность отследить
статус заказа.
Цель работы – проектирование и разработка АИС для курьерской
службы, обеспечивающей учёт заказов, назначение курьеров, расчёт
заработной платы и формирование отчётности.
Для достижения цели необходимо решить следующие задачи:
1. Провести анализ деятельности курьерской службы и выявить
проблемы ручного управления;
2. Обосновать выбор технологий (WPF, MS SQL Server, Entity
Framework);
3. Спроектировать
структуру
базы
данных
(инфологическую
и
физическую модели);
4. Разработать архитектуру приложения и функциональные требования
для ролей «Администратор» и «Курьер»;
3
4.
5. Реализоватьпользовательский
интерфейс
и
бизнес-логику
(управление заказами, расчёт зарплаты, отчёты);
6. Провести тестирование программного модуля.
Объект исследования – деятельность курьерской службы по доставке.
Предмет исследования – автоматизация процессов управления
заказами, учёта работы курьеров и расчёта заработной платы.
Методы исследования: анализ литературы и существующих решений,
сравнительный
анализ
технологий,
моделирование
бизнес-процессов,
проектирование БД, объектно-ориентированное программирование на C#,
тестирование и отладка.
Практическая значимость заключается в возможности использования
разработанной системы в малом бизнесе для автоматизации рутинных
операций, сокращения времени обработки заказов, прозрачного учёта выплат
и получения аналитики.
Структура работы: состоит из введения, двух глав, заключения,
библиографического списка и приложений. В первой главе рассматриваются
теоретические аспекты автоматизации курьерской службы, и обосновывается
выбор технологий. Во второй главе описываются проектирование базы
данных, архитектура приложения и его реализация. В приложениях
представлены ER-диаграмма, листинги ключевых классов и скриншоты
интерфейса.
4
5.
ГЛАВА 1. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ АВТОМАТИЗАЦИИКУРЬЕРСКОЙ СЛУЖБЫ
1.1. Анализ деятельности курьерской службы как объекта
автоматизации
Курьерская
служба
представляет
собой
специализированную
организацию или структурное подразделение, основной функцией которого
является доставка различных отправлений (грузов, документов, товаров, еды)
от отправителя к получателю. В современной логистической системе
курьерские службы выполняют критически важную функцию «последней
мили» (last mile) – завершающего этапа логистической цепочки, на котором
заказ непосредственно передаётся клиенту. От эффективности организации
этого
этапа
напрямую
зависит
удовлетворённость
потребителя,
его
лояльность и, в конечном счёте, конкурентоспособность бизнеса. В условиях
стремительного роста электронной коммерции и сервисов доставки готовой
еды, когда клиенты ожидают максимальной скорости, прозрачности и
удобства, автоматизация процессов становится не просто преимуществом, а
необходимым условием выживания и развития компании.
Деятельность любой курьерской службы можно представить как
совокупность взаимосвязанных бизнес-процессов, которые требуют чёткой
координации и контроля. К числу ключевых процессов относятся:
1. Приём и обработка заказов. Этот этап включает регистрацию заявки
от клиента (через сайт, мобильное приложение, по телефону), согласование
адресов и времени доставки, фиксацию особых условий.
2. Планирование и диспетчеризация. На этом этапе происходит
распределение поступивших заказов между курьерами. Диспетчер или
автоматическая система должна учитывать множество факторов: текущую
загруженность каждого курьера, его местоположение, наличие специального
транспорта, срочность заказа, а также строить оптимальные маршруты,
минимизирующие пробег и время в пути.
5
6.
3. Выполнение доставки. Непосредственное перемещение курьера помаршруту, передача груза получателю, фиксация факта доставки, а также
обработка возвратов и отказов.
4. Учёт
и
контроль.
Этот
процесс
обеспечивает
мониторинг
выполнения всех заказов в реальном времени.
5. Взаиморасчёты с курьерами. Расчёт заработной платы курьеров
может быть достаточно сложным: он включает базовую ставку за заказ,
бонусы за сложность, компенсацию расходов на топливо или амортизацию
транспорта, а также штрафы за нарушения. Автоматизация этого процесса
исключает ошибки и экономит время бухгалтерии.
6. Аналитика и планирование. Сбор и обработка накопленных данных
позволяют оценивать эффективность работы каждого курьера, выявлять
наиболее
востребованные
прогнозировать
пиковые
направления
нагрузки
и
и
временные
принимать
промежутки,
обоснованные
управленческие решения.
Многие
небольшие
курьерские
службы
и
индивидуальные
предприниматели продолжают использовать устаревшие методы управления:
бумажные журналы, Excel-таблицы, телефонные звонки. Такой подход
порождает ряд серьёзных проблем:
потеря и дублирование информации, ошибки при передаче заказов;
неэффективное распределение заказов из-за отсутствия объективных
данных о загруженности курьеров;
высокая трудоёмкость и ошибки при расчёте заработной платы;
отсутствие оперативной аналитики для принятия управленческих
решений;
низкий уровень контроля – ни клиенты, ни диспетчер не видят
актуальный статус доставки;
сложность масштабирования бизнеса при росте числа заказов.
Внедрение специализированной информационной системы позволяет
кардинально изменить ситуацию и получить ряд значительных преимуществ:
6
7.
1. Снижение влияния человеческого фактора. Система обрабатываетданные в реальном времени, исключая ошибки при передаче информации и
обеспечивая логиста актуальными данными для контроля и анализа. Все
операции фиксируются, что повышает дисциплину и ответственность.
2. Повышение скорости и эффективности доставки. Автоматическое
распределение заказов и построение маршрутов с учётом пробок и
загруженности курьеров позволяет существенно сократить время в пути.
Реальный опыт компаний подтверждает эти преимущества. Так, внедрение
системы «Курьерика» в компании «Нефтьмагистраль» (управляющей сетями
«Магбургер» и «Братья Караваевы») позволило сократить время прибытия
курьеров на 30% за счёт автоматизированного назначения.
3. Прозрачность и контролируемость. Руководитель и диспетчер могут
в режиме онлайн отслеживать местоположение всех курьеров на карте,
видеть статусы заказов, контролировать соблюдение сроков и мгновенно
реагировать на любые нештатные ситуации (пробки, поломки, изменение
адреса). Это повышает управляемость и позволяет предотвращать сбои.
4. Автоматизация расчёта заработной платы. Система самостоятельно
выполняет расчёт мотивационных выплат курьерам на основе пройденного
расстояния, тарифной сетки, индивидуальных коэффициентов и бонусов за
сложность. Это исключает ошибки, экономит время бухгалтера и делает
систему оплаты прозрачной для самих курьеров.
5. Аналитика и планирование. Накопление и обработка данных
позволяют проводить глубокий анализ эффективности работы. Руководство
может сравнивать доходность различных направлений (например, пунктов
выдачи
заказов),
выявлять
отклонения
и
их
причины,
оценивать
производительность каждого курьера. На основе этих данных можно
прогнозировать пиковые нагрузки и заранее планировать необходимое
количество персонала, избегая как простоев, так и перегрузок.
6. Повышение качества клиентского сервиса. Возможность для клиента
отслеживать статус своего заказа в реальном времени, точное соблюдение
7
8.
обещанных сроков доставки, оперативное уведомление о задержках – всё этоповышает
удовлетворённость
и
лояльность,
стимулирует
повторные
автоматизации
доставки
обращения и положительные отзывы.
Рынок
программных
продуктов
для
достаточно разнообразен. Крупные компании разрабатывают собственные
решения или интегрируют модули в ERP-системы. Для среднего и малого
бизнеса существуют готовые предложения, например облачный сервис «1СКурьерика». Однако такие решения могут быть слишком дороги или сложны
для микробизнеса, требуют адаптации под специфику конкретной компании.
В
результате
многие
малые
предприятия
либо
отказываются
от
автоматизации, либо используют разрозненные инструменты, не решая
проблему комплексно.
Таким образом, анализ деятельности курьерской службы как объекта
автоматизации показывает, что ручное управление приводит к потере
информации,
неэффективному
распределению
ресурсов,
трудоёмким
расчётам и отсутствию прозрачности. Автоматизация позволяет не только
устранить эти недостатки, но и повысить скорость доставки, качество
обслуживания
и
обоснованность
управленческих
решений.
Однако
существующие готовые продукты не всегда доступны или оптимальны для
малого бизнеса, что подтверждает актуальность разработки собственной
информационной системы, адаптированной к конкретным потребностям и
масштабам деятельности.
8
9.
1.2. Обоснование выбора технологий разработкиВыбор технологического стека – ключевой этап проектирования,
определяющий
архитектуру,
сопровождения
системы.
производительность
При
разработке
и
возможности
автоматизированной
информационной системы для курьерской службы необходимо было выбрать
платформу для клиентского приложения, СУБД и технологию доступа к
данным. Основные критерии: соответствие задачам проекта, простота
освоения, наличие качественных инструментов, интеграция компонентов и
доступность для малого бизнеса. В результате анализа выбраны: Windows
Presentation Foundation (WPF) на платформе .NET Framework, Microsoft SQL
Server и Entity Framework с подходом Database First.
Выбор в пользу WPF обусловлен рядом преимуществ:
Современная
архитектура и производительность. WPF использует
векторную графику и аппаратное ускорение через DirectX, обеспечивая
высокую производительность и масштабируемость интерфейсов. Это важно
для отображения больших объёмов данных (списки заказов, отчёты).
Декларативная
разметка
XAML позволяет
отделить
описание
интерфейса от кода, упрощая разработку и сопровождение.
Мощная система привязки данных (Data Binding) минимизирует код
синхронизации
интерфейса
с
данными,
что
особенно
важно
при
динамическом отображении списков заказов и клиентов.
Богатые
возможности
стилизации
и
шаблонизации позволяют
унифицировать внешний вид элементов управления и создавать интуитивно
понятный интерфейс.
Поддержка
паттерна MVVM облегчает тестирование и поддержку
приложения.
Актуальность
и поддержка Microsoft. WPF остаётся надёжным
выбором для Windows-приложений, имеет подробную документацию и
активное сообщество.
9
10.
Таким образом, WPF обеспечивает создание функционального исовременного
интерфейса,
необходимого
для
эффективной
работы
администратора и курьера.
В качестве СУБД выбрана Microsoft SQL Server. Это бесплатная
версия, оптимальная для небольших проектов:
Бесплатность
и доступность.
Версия может использоваться в
производственных средах без лицензионных отчислений, что критично для
малого бизнеса.
Достаточная
функциональность. Поддерживаются
все
ключевые
возможности: транзакции, ограничения целостности, внешние ключи,
триггеры, представления. Ограничения (10 ГБ, 1 ГБ ОЗУ, один процессор)
несущественны для масштабов курьерской службы.
Удобные инструменты администрирования – SQL Server Management
Studio (SSMS). SSMS позволяет визуально проектировать таблицы, связи,
индексы, отлаживать запросы и создавать диаграммы, что использовалось на
всех этапах работы с БД.
Высокая интеграция с .NET и Visual Studio обеспечивает бесшовное
подключение и взаимодействие.
Надёжность и масштабируемость. При росте бизнеса возможен лёгкий
переход на старшие версии SQL Server.
SQL Server представляет собой идеальный баланс между стоимостью,
функциональностью и надёжностью для данного проекта.
Для взаимодействия приложения с базой данных выбран Entity
Framework (EF) – стандартный ORM для .NET. Преимущества:
Повышение скорости разработки. EF позволяет работать с данными
как с объектами C#, автоматически генерируя код CRUD-операций.
Подход
Database First. База данных была спроектирована заранее в
SSMS. EF автоматически создаёт классы сущностей и контекст на основе
существующей схемы, что обеспечивает полный контроль над структурой БД
и исключает ошибки ручного написания кода.
10
11.
Удобство запросов через LINQ. Запросы пишутся на C# с проверкойтипов и поддержкой IntelliSense.
Автоматическое
отслеживание изменений и генерация SQL-команд
при сохранении.
Навигационные свойства упрощают доступ к связанным данным.
Использование EF значительно сокращает объём кода и упрощает
сопровождение системы.
Комбинация WPF + SQL Server + Entity Framework (Database
First) полностью
соответствует
задачам
разработки:
обеспечивает
современный интерфейс, надёжное и бесплатное хранение данных, высокую
скорость разработки благодаря ORM и полную интеграцию в среде Visual
Studio. Выбранный стек учитывает реальные потребности и финансовые
возможности малого бизнеса.
11
12.
ГЛАВА 2. ПРОЕКТИРОВАНИЕ И РЕАЛИЗАЦИЯАВТОМАТИЗИРОВАННОЙ ИНФОРМАЦИОННОЙ СИСТЕМЫ
КУРЬЕРСКОЙ СЛУЖБЫ
2.1. Анализ предметной области
Курьерская
служба
представляет
собой
организацию,
осуществляющую доставку различных грузов, документов, товаров и
продуктов от отправителя к получателю. В современных условиях
стремительного роста электронной коммерции и сервисов доставки еды
эффективность работы курьерской службы напрямую зависит от степени
автоматизации
её
ключевых
процессов.
Автоматизация
позволяет
минимизировать влияние человеческого фактора, сократить время обработки
заказов, оптимизировать маршруты курьеров и обеспечить прозрачность
финансовых расчётов.
На основе анализа деятельности типичной курьерской службы были
выделены
следующие
основные
функции
и
процессы,
требующие
автоматизации:
Управление заказами: приём заказов: регистрация заявки от клиента,
фиксация контактных данных, адреса доставки, описания груза, его
сложности и срочности; редактирование заказов: возможность изменения
параметров заказа до момента его выполнения (адрес, состав, время);
отслеживание статусов: контроль текущего состояния заказа (принят,
назначен курьеру, в пути, доставлен, отменён), статусы должны обновляться
в реальном времени; история заказов: хранение информации о всех
выполненных доставках для последующего анализа.
Управление клиентами: регистрация клиентов: внесение и хранение
данных о клиентах (физических и юридических лицах): ФИО/название,
телефон, адрес, история заказов; поиск и фильтрация: быстрый доступ к
информации о клиенте, его предыдущих заказах и предпочтениях.
12
13.
Управление курьерами: учёт курьеров: хранение личных данных (ФИО,телефон, email), а также рабочей информации: базовая ставка за заказ,
признак активности (работает/уволен); назначение на заказы: распределение
поступающих
заказов
между
свободными
курьерами
с
учётом
их
загруженности и местоположения; контроль работы: возможность просмотра
истории выполненных заказов каждым курьером и расчёта его заработка.
Расчёт заработной платы: начисление выплат: автоматический расчёт
суммы, причитающейся курьеру за выполненный заказ. Расчёт учитывает
базовую ставку курьера и бонус за сложность (если заказ помечен как
сложный); учёт выплат: фиксация начисленных сумм, даты начисления и
отметки о фактической выплате курьеру; прозрачность: предоставление
курьеру информации о его доходах за выбранный период.
Формирование отчётности: отчёт по заказам: анализ количества заказов
и общей выручки за день, неделю, месяц; группировка по датам, статусам,
курьерам; отчёт по заработку курьеров: суммирование выплат каждому
курьеру за заданный период, контроль задолженности; аналитические
данные: выявление пиковых нагрузок,
оценка эффективности работы
курьеров.
Администрирование
и
безопасность:
разграничение
прав
доступа: наличие двух ролей – администратор (полный доступ) и курьер
(ограниченный доступ: только просмотр своих заказов и изменение их
статуса); аутентификация пользователей: вход в систему по логину и паролю,
хранение учётных записей с указанием роли и привязкой к конкретному
курьеру (для роли Courier).
На основе проведённого анализа предметной области были определены
следующие ключевые сущности, которые в дальнейшем легли в основу
проектирования базы данных:
Clients (Клиенты) – хранит информацию о заказчиках: ФИО/название,
телефон, адрес.
13
14.
Couriers (Курьеры) – содержит данные о курьерах: личные данные,базовая ставка, признак активности.
Orders (Заказы) – основная таблица, фиксирующая каждый заказ: дата,
ссылки на клиента и курьера, статус, описание, адрес доставки, признак
сложности, сумма бонуса, общая стоимость, а также информация о том, кто
создал заказ (администратор).
OrderStatuses (Статусы заказов) – справочник возможных состояний
заказа (например, «Принят», «Назначен курьеру», «В пути», «Доставлен»,
«Отменён»).
Payments
(Выплаты) – учёт начислений курьерам за выполненные
заказы: ссылка на курьера и заказ, дата начисления, сумма, отметка о
выплате.
Users (Пользователи) – учётные записи для входа в систему: логин,
пароль, роль (Admin/Courier), ссылка на курьера (если роль Courier), признак
активности.
Проведённый анализ предметной области позволил выявить все
ключевые процессы, требующие автоматизации, и определить состав
сущностей,
необходимых
для
реализации
информационной
системы.
Полученные результаты будут использованы на следующих этапах курсовой
работы для логического и физического проектирования базы данных, а также
для
разработки
интерфейса
приложения,
выполнение описанных функций.
14
обеспечивающего
удобное
15.
2.2. Реализация проекта базы данныхОбзор известных аналогов показал, что на сегодняшний день
разработано немалое количество сервисов для автоматизации курьерской
доставки. Рассмотрим один из них – сервис «Яндекс Доставка».
Главная страница сайта представлена на рис. 1.
Рис. 1. Главная страница сервиса «Яндекс Доставка»
Яндекс Доставка – платформа для организации логистики, работающая
более чем в 1000 городах. Сервис предлагает экспресс-доставку, доставку в
течение дня, грузовые перевозки и отправку через сеть пунктов выдачи.
Отслеживание заказов доступно в приложении Яндекс Go.
Создание собственной автоматизированной информационной системы
курьерской службы позволяет реализовать аналогичный функционал с
учётом специфики конкретного бизнеса и без затрат на лицензирование.
Создание базы данных сайта с помощью Microsoft SQL Server
Management Studio:
В процессе создания сайта была разработана физическая модель базы
данных (см. Приложение 1). Эта модель включает несколько основных
таблиц, каждая из которых выполняет определённые функции. Ниже
представлены ключевые таблицы, созданные в базе данных CourierServiceDB
(рис. 2).
15
16.
Рис. 2. Диаграмма базы данных CourierServiceDB1) Таблица «Клиенты» (Clients) (рис. 3)
ClientID - уникальный идентификатор клиентов; Full Name название клиента; Phone – номер; Adress - адресс.
Рис. 3. Таблица Clients
2) Таблица «Курьеры» (Couriers) (рис. 4)
CourierID - уникальный идентификатор курьеров; Full Name - ФИО
курьеров; Phone - телефон; Email - электронная почта; Base Rate - базовая
ставка; Is Active - активность.
Рис. 4. Таблица Couriers
3) Таблица «Заказы» (Orders) (рис. 5)
OrderID - уникальный идентификатор заказов; Order Date – дата
заказов; ClientID - уникальный идентификатор клиентов; CourierID уникальный идентификатор курьеров; StatusID - уникальный идентификатор
статуса заказов; Description - описание; Delivery Address – адресс доставки; Is
16
17.
Complex – сложность доставки; Bonus Amount – сумма бонуса; Total Amount- общая сумма; Create By.
Рис. 5. Таблица Orders
4)
Таблица «Статус Заказов» (Order Statuses) (рис. 6)
StatusID - уникальный идентификатор статуса заказов; Status Name имя статуса.
Рис. 6. Таблица Order Statuses
5) Таблица «Выплаты» (Payments) (рис. 7)
PaymentID - уникальный идентификатор выплат; CourierID уникальный идентификатор курьеров; OrderID - уникальный идентификатор
заказов; Payment Date – дата выплат; Amount - сумма; Is Paid - выплачено.
Рис. 7. Таблица Order Statuses
6) Таблица «Пользователи» (Users) (рис. 8)
UserID - уникальный идентификатор пользователя; Login - логин;
Password - пароль; Role - роль; CourierID - уникальный идентификатор
курьеров; Is Active - активность.
17
18.
Рис. 8. ТаблицаДалее будет представлено описание разработки пользовательского
интерфейса на основе технологии WPF (Windows Presentation Foundation) в
среде разработки Visual Studio. Целью разработки является создание
удобного и функционального интерфейса для учёта часов преподавателя.
Этапы разработки WPF проекта с использованием ADO.NET:
При создании проекта выбираем Приложение WPF(.NET Framework)
(рис. 9), после настраиваем новый проект (рис. 10).
Рис. 9. Этап создания проекта
Рис. 10. Настройка нового проекта
Для привязки базы данных из SSMS к Visual Studio необходимо в
Visual Studio добавить новый элемент (рис. 11), выбрать Model ADO.NET
(рис. 12).
18
19.
Рис. 11. Добавление нового элементаРис. 12. Выбор Model ADO.NET EDM
После этого выбираем «Конструктор EF из базы данных» (рис. 13),
далее необходимо выбрать подключение к данным, далее нажимаем «Создать
соединение» и заполняем необходимые параметры, проверяем подключение
к базе кнопкой «Проверить подключение», если выводится сообщение
«Успешное подключение”, нажимаем на «Ок» (рис. 14) и можем увидеть
строку подключения (рис. 15).
Рис. 13. Выбор содержимого модели
19
20.
Рис. 14. Выбор подключения к данным и свойства подключенияРис. 15. Строка подключения
Выбираем доступный Framework (рис. 16).
Рис. 16. Выбор подходящего фреймворка
Проводим выбор и объектов базы данных, которые пригодятся нам при
сборке проекта (рис. 17).
Рис. 17. Выбор параметров и объектов базы данных
Данный проект представляет собой приложение на WPF (Windows
Presentation
Foundation),
предназначенное
20
для
автоматизации
работы
21.
курьерской службы. Администратор может войти в систему, используя своиучетные данные, и получить доступ к личному кабинету, где отображается
информация о заказах, курьерах, выплатах и отчётах. Курьер также может
войти в систему, но его функционал ограничен просмотром назначенных
заказов и изменением их статуса.
Проект состоит из следующих основных окон:
1. Окно авторизации (LoginWindow) – здесь пользователь вводит логин
и пароль для входа в систему. В зависимости от роли (администратор или
курьер) открывается соответствующее окно (см. рис. 18 Приложение 2).
2. Главное окно администратора (AdminWindow) – после успешной
авторизации администратор попадает на данное окно, которое содержит
четыре вкладки (см. рис. 19 Приложение 2):
o Заказы – просмотр, добавление и редактирование заказов (см. рис. 20
Приложение 2).
o Курьеры – просмотр списка курьеров (см. рис. 21 Приложение 2).
o Отчёты – формирование отчётов по количеству заказов за период и по
заработку курьеров (см. рис. 22 Приложение 2).
o Выплаты – просмотр начисленных выплат и отметка о выплате (см.
рис. 23 Приложение 2).
3. Окно курьера (CourierWindow) – после успешной авторизации
курьер попадает на данное окно, где отображается список назначенных ему
заказов с возможностью изменить статус на «В пути» или «Доставлен» (см.
рис. 28 Приложение 2).
4. Окно добавления/редактирования заказа (AddEditOrderWindow) –
открывается из окна администратора для создания нового заказа или
изменения существующего. Содержит поля для ввода информации о клиенте,
курьере, адресе, описании, сложности, бонусе и сумме (см. рис. 24,27
Приложение 2).
5. Окно добавления/редактирования курьера (AddEditCourierWindow) –
открывается из окна администратора для создания нового курьера. Содержит
21
22.
поля для ввода информации о ФИО клиента, телефоне, электронной почте,базовой ставки и активности (см. рис. 25 Приложение 2).
При попытке зайти с неправильным логином или паролем выводится
сообщение об ошибке, предупреждающее о неверных учётных данных (см.
рис. 29 Приложение 2).
В окне администратора на вкладке «Заказы» доступны кнопки
«Добавить заказ», «Редактировать» и «Обновить» (см. рис. 20 Приложение
2).
При
выборе
заказа
и
нажатии
«Редактировать»
открывается
окно AddEditOrderWindow с заполненными полями (см. рис. 27 Приложение
2), но неоюходимо сначала выбрать заказ, который нужно отредактировать, а
иначе выходить окно «Выберите заказ» (см. рис. 26 Приложение 2). При
нажатии «Добавить» открывается окно AddEditOrderWindow с пустыми
полями (см. рис. 24 Приложение 2). На вкладке «Отчёты» администратор
выбирает
тип
отчёта
и
период,
после
чего
таблица
заполняется
соответствующими данными (см. рис. 22 Приложение 2). На вкладке
«Выплаты» отображаются невыплаченные суммы, которые можно отметить
как выплаченные (см. рис. 23 Приложение 2). На вкладке «Курьеры»
открывается окно AddEditCourierWindow с пустыми полями (см. рис. 25
Приложение 2)
В окне курьера заказы представлены в виде таблицы с кнопками
действий. При нажатии «В путь» статус заказа меняется на «В пути», при
нажатии «Доставлен» – на «Доставлен», и автоматически создаётся запись о
выплате (см. рис. 28 Приложение 2).
Проект
позволяет
удобно
управлять
заказами
и
курьерами,
автоматизировать расчёт зарплаты и получать аналитическую информацию,
что
значительно
упрощает
процессы
учёта
в
курьерской
службе.
Использование WPF обеспечивает современный интерфейс и удобство
взаимодействия с приложением.
С кодом программы можно ознакомиться в Приложении 3.
22
23.
ЗАКЛЮЧЕНИЕВ ходе выполнения курсовой работы достигнута поставленная цель:
спроектирована и разработана автоматизированная информационная система
для курьерской службы. Система обеспечивает учёт заказов, назначение
курьеров, расчёт заработной платы и формирование отчётности. Все задачи,
сформулированные во введении, решены в полном объёме.
В первой главе проведён анализ деятельности курьерской службы,
выявлены проблемы ручного управления и обоснована актуальность
разработки. Во второй главе спроектирована база данных, реализована
трёхуровневая архитектура приложения (WPF, .NET Framework, Entity
Framework, MS SQL Server) и разработан следующий функционал:
разграничение прав доступа (администратор/курьер);
ведение справочников и управление заказами;
автоматический расчёт зарплаты курьеров;
учёт выплат и формирование отчётов за период.
Практическая
значимость
работы
заключается
в
возможности
использования системы для автоматизации небольшой курьерской службы
без лицензионных затрат. Внедрение позволит сократить время обработки
заказов и повысить прозрачность учёта. Перспективы развития: онлайнотслеживание, интеграция с картами, мобильное приложение для курьеров.
Таким
образом,
разработанная
система
представляет
собой
полнофункциональный программный продукт. Курсовая работа выполнена в
полном объёме, цель достигнута.
23
24.
БИБЛИОГРАФИЧЕСКИЙ СПИСОК1. Алхимова, Д. С., Салпагаров, С. И. О программных средствах
реализации доставки товаров / Д. С. Алхимова, С. И. Салпагаров //
Современные наукоемкие технологии. – 2023. – № 2. – С. 9-16. – ISSN 18127320. – URL: http://www.gmachineinfo.com/qkdetail.aspx?id=203470455 (дата
обращения: 02.03.2026).
2. Власов М.В., Сидоров А.А. Цифровые решения в деятельности малых
логистических предприятий (на примере Свердловской области) // Цифровые
модели и решения. – 2024. – Т. 3. – № 3. – С. 60-67. DOI: 10.29141/2949-477X2024-3-3-4.
3. Гаджиибрагимов,
А.
Г.,
Володина,
Т.
Ю.
Разработка
информационной системы для организации службы доставки еды / А. Г.
Гаджиибрагимов, Т. Ю. Володина. — Текст : электронный // Математическое
и информационное моделирование : материалы Всероссийской конференции
молодых ученых, г. Тюмень, 22-30 апреля 2025 г. Вып. 23 / Министерство
науки
и
высшего
образования
Российской
Федерации,
Тюменский
государственный университет, Школа компьютерных наук ; ответственные
редакторы : Л. Н. Бакановская, М. С. Воробьева. — Тюмень : ТюмГУ-Press,
2025. — С. 290–296. — URL: https://elib.utmn.ru/jspui/handle/ru-tsu/38258 (дата
обращения: 02.03.2026).
4. Сластунов, Я. Г., Сюсин, И. А., Муравьева, Е. В. Исследование
методов и подходов автоматизации доставки посылки : вып. квалификац.
работа по направлению подгот. 09.04.01 "Информатика и вычислительная
техника" (уровень магистратуры) / Я. Г. Сластунов ; рук. работы И. А. Сюсин
; Самар. нац. исслед. ун-т им. С. П. Королева (Самар. ун-т). — Самара, 2025.
— 1 файл. — URL: http://repo.ssau.ru/jspui/handle/123456789/45748 (дата
обращения: 02.03.2026).
24
25.
5. Пушкин, П. А., Головнин, О. К., Соловьева, Я. В. Автоматизированнаясистема планирования маршрутов для служб такси и курьерской доставки :
вып. квалификац. работа по направлению подгот. 09.04.01 "Информатика и
вычислительная техника" (уровень магистратуры) / П. А. Пушкин ; рук.
работы О. К. Головнин ; Самар. нац. исслед. ун-т им. С. П. Королева (Самар.
ун-т).
—
Самара,
2022.
—
1
файл.
—
URL:
http://repo.ssau.ru/jspui/handle/123456789/55342 (дата обращения: 02.03.2026).
6. Христов А. Сравнителен анализ на code first и database first подходи
при работа с бази от данни в Entity Framework Core // Математика,
компютърни науки и образование. – 2025. – Т. 8. – № 2. – С. 123-127. DOI:
10.54664/GCBZ8176.
7. Автоматизация бизнес-процессов последней мили в компании
«Нефтьмагистраль» с помощью сервиса «Курьерика» // GlobalCIO. – 2024. –
[Электронный ресурс] Режим доступа: https://globalcio.ru/projectoftheyear/list/
45097/ (дата обращения: 01.03.2026).
8. Математика в логистике: алгоритмы, прогнозы и автоматизация //
CDEK.
–
[Электронный
2025.–
ресурс]
Режим
-
доступа:
https://www.cdek.ru/ru/press/view/2025-09-17-matematika-v-logistike-algoritmyprognozy-i-avtomatizacia/ (дата обращения: 01.03.2026).
9. Обзор Windows Presentation Foundation // Microsoft Learn. – 2025. –
[Электронный
ресурс]
–
Режим
доступа: https://learn.microsoft.com/ru-
ru/dotnet/desktop/wpf/overview/ (дата обращения: 28.02.2026).
10. Что
такое
СУБД? [Электронный
ресурс] –
доступа: https://practicum.yandex.ru/blog/chto-takoe-subd/ (дата
Режим
обращения:
02.03.2026).
11. Администратор
[Электронный
баз
данных:
ресурс]
кто
–
это
и
чем
занимается?
Режим
https://sky.pro/wiki/profession/administrator-baz-dannyh-kto-eto-i-chemzanimaetsya/ (дата обращения: 02.03.2026).
25
доступа:
26.
12. Администратор баз данных. [Электронный ресурс] – Режимдоступа: https://postupi.online/professiya/administrator-baz-dannyh/ (дата
обращения: 01.03.2026).
13. Доставьте это немедленно: исследование клиентского опыта малого
бизнеса
в
Boxberry
и
Dostavista
//
Markswebb.
–
2025.
–
URL: https://www.markswebb.ru/blog/delivery-ux-benchmark/ (дата обращения:
02.03.2026).
14. База данных MS SQL Server: что это такое, зачем она нужна, как
появилась
и
чем
хороша.
[Электронный
ресурс] —
Режим
доступа: https://skillbox.ru/media/code/baza-dannykh-ms-sql-server-chto-etozachem-nuzhna-kak-poyavilas-i-chem-khorosha/ (дата обращения: 01.03.2026).
15. «Цифра» для оптимизации. Versta.io автоматизировал работу с
курьерскими и транспортными компаниями // Mobilecomm. – 2025. –
[Электронный ресурс] – Режим доступа: https://www.mobilecomm.ru/tsifradlya-optimizatsii (дата обращения: 28.02.2026).
16. Яндекс Доставка: Вакансии и работа в курьерской службе. –
[Электронный ресурс] – Режим доступа: https://dostavka.yandex.ru/deliveryjobs/ (дата обращения: 02.03.2026).
17. «1С:Курьерика»: автоматизация доставки последней мили // 1С:
Торговля.
–
2025.
–
[Электронный
ресурс]
–
Режим
доступа: https://torg.1c.ru/articles/1s-kurerika-avtomatizatsiya-dostavkiposledney-mili/ (дата обращения: 02.03.2026).
18. C# codefirst vs database first // Yisu. – 2024. – [Электронный ресурс] –
Режим
доступа: http://yisuapi.yisu.com/ask/3196505.html (дата
обращения:
02.03.2026).
19. Créer une application de données de base avec WPF et Entity
Framework 6 // Microsoft Learn. – 2025. – [Электронный ресурс] – Режим
доступа: https://learn.microsoft.com/fr-fr/visualstudio/data-tools/create-a-simpledata-application-with-wpf-and-entity-framework-6 (дата обращения: 01.03.2026).
26
27.
20. Dokumentacja - Charakterystyka frameworków WPF i Qt // GitHub. ––
2024.
ресурс]
[Электронный
–
Режим
доступа: https://github.com/Bankowska/002_framework (дата
обращения:
01.03.2026).
21. GitHub - titorenko-rita/222_Titorenko: WPF проект с Entity Framework
//
GitHub.
–
2025.
–
[Электронный
ресурс]
–
доступа: https://github.com/titorenko-rita/222_Titorenko (дата
Режим
обращения:
28.02.2026).
22. SQL Server Editions // Gethyn Ellis. – 2024. – [Электронный ресурс] –
Режим
доступа:
https://www.gethynellis.com/2024/05/sql-server-
editions.html (дата обращения: 28.02.2026).
23. VisualStudio.
[Электронный
ресурс]
–
Режим
доступа:
https://visualstudio.microsoft.com/ru/ (дата обращения: 02.03.2026).
24. Using SQL Server Express in production // Microsoft Q&A. – 2024. –
[Электронный
ресурс]
–
Режим
доступа: https://learn.microsoft.com/en-
us/answers/questions/1660915/using-sql-server-espress-in-production (дата
обращения: 02.03.2026).
25. Windows
Presentation
[Электронный
Foundation
ресурс]
–
//
Википедия.
–
2024.
–
Режим
доступа:
https://ru.wikipedia.org/wiki/Windows_Presentation_Foundation
(дата
обращения: 02.03.2026).
27
28.
ПРИЛОЖЕНИЯПриложение 1
SQL код для создания и заполнения базы данных CourierServiceDB
USE [CourierServiceDB]
GO
/****** Object: Table [dbo].[Clients] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Clients](
[ClientId] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](100) NOT NULL,
[Phone] [nvarchar](20) NOT NULL,
[Address] [nvarchar](200) NULL,
PRIMARY KEY CLUSTERED
(
[ClientId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Couriers] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Couriers](
[CourierId] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](100) NOT NULL,
[Phone] [nvarchar](20) NOT NULL,
[Email] [nvarchar](100) NULL,
[BaseRate] [decimal](10, 2) NOT NULL,
[IsActive] [bit] NOT NULL,
PRIMARY KEY CLUSTERED
(
[CourierId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Orders] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
29.
SET QUOTED_IDENTIFIER ONGO
CREATE TABLE [dbo].[Orders](
[OrderId] [int] IDENTITY(1,1) NOT NULL,
[OrderDate] [datetime] NOT NULL,
[ClientId] [int] NOT NULL,
[CourierId] [int] NULL,
[StatusId] [int] NOT NULL,
[Description] [nvarchar](500) NULL,
[DeliveryAddress] [nvarchar](200) NOT NULL,
[IsComplex] [bit] NOT NULL,
[BonusAmount] [decimal](10, 2) NULL,
[TotalAmount] [decimal](10, 2) NOT NULL,
[CreatedBy] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[OrderId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[OrderStatuses] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[OrderStatuses](
[StatusId] [int] IDENTITY(1,1) NOT NULL,
[StatusName] [nvarchar](50) NOT NULL,
PRIMARY KEY CLUSTERED
(
[StatusId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Payments] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Payments](
[PaymentId] [int] IDENTITY(1,1) NOT NULL,
[CourierId] [int] NOT NULL,
[OrderId] [int] NOT NULL,
[PaymentDate] [datetime] NOT NULL,
[Amount] [decimal](10, 2) NOT NULL,
[IsPaid] [bit] NOT NULL,
PRIMARY KEY CLUSTERED
(
30.
[PaymentId] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Users] Script Date: 28.02.2026 0:14:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Users](
[UserId] [int] IDENTITY(1,1) NOT NULL,
[Login] [nvarchar](50) NOT NULL,
[Password] [nvarchar](50) NOT NULL,
[Role] [nvarchar](20) NOT NULL,
[CourierId] [int] NULL,
[IsActive] [bit] NOT NULL,
PRIMARY KEY CLUSTERED
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Clients] ON
INSERT [dbo].[Clients] ([ClientId], [FullName], [Phone], [Address]) VALUES (1,
N'ООО "Ромашка"', N'+7(495)123-45-67', N'г. Москва, ул. Ленина, д. 10')
INSERT [dbo].[Clients] ([ClientId], [FullName], [Phone], [Address]) VALUES (2, N'ИП
Смирнов А.А.', N'+7(495)234-56-78', N'г. Москва, ул. Гагарина, д. 5, кв. 20')
INSERT [dbo].[Clients] ([ClientId], [FullName], [Phone], [Address]) VALUES (3,
N'Петрова Елена Викторовна', N'+7(495)345-67-89', N'г. Москва, ул. Тверская, д. 15, кв. 5')
INSERT [dbo].[Clients] ([ClientId], [FullName], [Phone], [Address]) VALUES (4,
N'Кафе "Уют"', N'+7(495)456-78-90', N'г. Москва, ул. Арбат, д. 25')
INSERT [dbo].[Clients] ([ClientId], [FullName], [Phone], [Address]) VALUES (5,
N'ЗАО "Технопарк"', N'+7(495)567-89-01', N'г. Москва, пр. Мира, д. 100')
SET IDENTITY_INSERT [dbo].[Clients] OFF
GO
SET IDENTITY_INSERT [dbo].[Couriers] ON
INSERT [dbo].[Couriers] ([CourierId], [FullName], [Phone], [Email], [BaseRate],
[IsActive]) VALUES (1, N'Иванов Иван Иванович', N'+7(999)111-22-33', N'ivanov@mail.ru',
CAST(150.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Couriers] ([CourierId], [FullName], [Phone], [Email], [BaseRate],
[IsActive]) VALUES (2, N'Петров Петр Петрович', N'+7(999)222-33-44', N'petrov@mail.ru',
CAST(150.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Couriers] ([CourierId], [FullName], [Phone], [Email], [BaseRate],
[IsActive]) VALUES (3, N'Сидорова Анна Сергеевна', N'+7(999)333-44-55',
N'sidorova@mail.ru', CAST(170.00 AS Decimal(10, 2)), 1)
31.
INSERT [dbo].[Couriers] ([CourierId], [FullName], [Phone], [Email], [BaseRate],[IsActive]) VALUES (4, N'Козлов Дмитрий Андреевич', N'+7(999)444-55-66',
N'kozlov@mail.ru', CAST(150.00 AS Decimal(10, 2)), 0)
SET IDENTITY_INSERT [dbo].[Couriers] OFF
GO
SET IDENTITY_INSERT [dbo].[Orders] ON
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (1, CAST(N'2026-01-02T10:30:00.000' AS DateTime), 1, 1, 4, N'Документы
(договор, счета)', N'г. Москва, ул. Ленина, д. 10', 0, CAST(0.00 AS Decimal(10, 2)),
CAST(200.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (2, CAST(N'2026-01-02T12:15:00.000' AS DateTime), 2, 2, 4, N'Цветы и коробка
конфет', N'г. Москва, ул. Гагарина, д. 5, кв. 20', 0, CAST(0.00 AS Decimal(10, 2)),
CAST(350.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (3, CAST(N'2026-02-02T09:00:00.000' AS DateTime), 3, 3, 4, N'Продуктовая
корзина', N'г. Москва, ул. Тверская, д. 15, кв. 5', 1, CAST(100.00 AS Decimal(10, 2)),
CAST(600.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (4, CAST(N'2026-03-02T14:30:00.000' AS DateTime), 4, 1, 3, N'Обеды для офиса
(10 порций)', N'г. Москва, ул. Арбат, д. 25', 0, CAST(0.00 AS Decimal(10, 2)),
CAST(2500.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (5, CAST(N'2026-04-02T16:45:00.000' AS DateTime), 5, 2, 2, N'Посылка с
техникой (хрупкий груз)', N'г. Москва, пр. Мира, д. 100', 1, CAST(200.00 AS Decimal(10,
2)), CAST(800.00 AS Decimal(10, 2)), 2)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (6, CAST(N'2026-05-02T11:20:00.000' AS DateTime), 1, NULL, 1, N'Канцелярские
товары', N'г. Москва, ул. Ленина, д. 10', 0, CAST(0.00 AS Decimal(10, 2)), CAST(450.00 AS
Decimal(10, 2)), 2)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (7, CAST(N'2026-06-02T13:10:00.000' AS DateTime), 3, 3, 1, N'Лекарства из
аптеки', N'г. Москва, ул. Тверская, д. 15, кв. 5', 0, CAST(0.00 AS Decimal(10, 2)),
CAST(300.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (8, CAST(N'2026-07-02T15:00:00.000' AS DateTime), 4, NULL, 1, N'Свежие
продукты (скоропорт)', N'г. Москва, ул. Арбат, д. 25', 1, CAST(150.00 AS Decimal(10, 2)),
CAST(1200.00 AS Decimal(10, 2)), 2)
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],
[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (9, CAST(N'2026-08-02T10:00:00.000' AS DateTime), 2, 1, 1, N'Запчасти для
автомобиля', N'г. Москва, ул. Гагарина, д. 5, кв. 20', 0, CAST(0.00 AS Decimal(10, 2)),
CAST(750.00 AS Decimal(10, 2)), 1)
32.
INSERT [dbo].[Orders] ([OrderId], [OrderDate], [ClientId], [CourierId], [StatusId],[Description], [DeliveryAddress], [IsComplex], [BonusAmount], [TotalAmount], [CreatedBy])
VALUES (10, CAST(N'2026-09-02T18:30:00.000' AS DateTime), 5, 2, 1, N'Срочная доставка
документов', N'г. Москва, пр. Мира, д. 100', 1, CAST(300.00 AS Decimal(10, 2)),
CAST(1000.00 AS Decimal(10, 2)), 2)
SET IDENTITY_INSERT [dbo].[Orders] OFF
GO
SET IDENTITY_INSERT [dbo].[OrderStatuses] ON
INSERT [dbo].[OrderStatuses] ([StatusId], [StatusName]) VALUES (3, N'В пути')
INSERT [dbo].[OrderStatuses] ([StatusId], [StatusName]) VALUES (4, N'Доставлен')
INSERT [dbo].[OrderStatuses] ([StatusId], [StatusName]) VALUES (2, N'Назначен
курьеру')
INSERT [dbo].[OrderStatuses] ([StatusId], [StatusName]) VALUES (5, N'Отменён')
INSERT [dbo].[OrderStatuses] ([StatusId], [StatusName]) VALUES (1, N'Принят')
SET IDENTITY_INSERT [dbo].[OrderStatuses] OFF
GO
SET IDENTITY_INSERT [dbo].[Payments] ON
INSERT [dbo].[Payments] ([PaymentId], [CourierId], [OrderId], [PaymentDate],
[Amount], [IsPaid]) VALUES (1, 1, 1, CAST(N'2026-01-02T18:00:00.000' AS DateTime),
CAST(150.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Payments] ([PaymentId], [CourierId], [OrderId], [PaymentDate],
[Amount], [IsPaid]) VALUES (2, 2, 2, CAST(N'2026-01-02T19:30:00.000' AS DateTime),
CAST(150.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Payments] ([PaymentId], [CourierId], [OrderId], [PaymentDate],
[Amount], [IsPaid]) VALUES (3, 3, 3, CAST(N'2026-02-02T15:20:00.000' AS DateTime),
CAST(270.00 AS Decimal(10, 2)), 1)
INSERT [dbo].[Payments] ([PaymentId], [CourierId], [OrderId], [PaymentDate],
[Amount], [IsPaid]) VALUES (4, 1, 4, CAST(N'2026-03-02T20:00:00.000' AS DateTime),
CAST(150.00 AS Decimal(10, 2)), 0)
INSERT [dbo].[Payments] ([PaymentId], [CourierId], [OrderId], [PaymentDate],
[Amount], [IsPaid]) VALUES (5, 2, 5, CAST(N'2026-04-02T20:10:00.000' AS DateTime),
CAST(350.00 AS Decimal(10, 2)), 0)
SET IDENTITY_INSERT [dbo].[Payments] OFF
GO
SET IDENTITY_INSERT [dbo].[Users] ON
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (1, N'admin', N'admin123', N'Admin', NULL, 1)
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (2, N'admin2', N'admin321', N'Admin', NULL, 1)
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (3, N'courier1', N'pass1', N'Courier', 1, 1)
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (4, N'courier2', N'pass2', N'Courier', 2, 1)
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (5, N'courier3', N'pass3', N'Courier', 3, 1)
INSERT [dbo].[Users] ([UserId], [Login], [Password], [Role], [CourierId], [IsActive])
VALUES (6, N'courier4', N'pass4', N'Courier', 4, 0)
SET IDENTITY_INSERT [dbo].[Users] OFF
GO
33.
SET ANSI_PADDING ONGO
/****** Object: Index [UQ__OrderSta__05E7698A2CE88274]
Script Date:
28.02.2026 0:14:10 ******/
ALTER TABLE [dbo].[OrderStatuses] ADD UNIQUE NONCLUSTERED
(
[StatusName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS
=
ON,
ALLOW_PAGE_LOCKS
=
ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
SET ANSI_PADDING ON
GO
/****** Object: Index [UQ__Users__5E55825B28B7A95D] Script Date: 28.02.2026
0:14:10 ******/
ALTER TABLE [dbo].[Users] ADD UNIQUE NONCLUSTERED
(
[Login] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS
=
ON,
ALLOW_PAGE_LOCKS
=
ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Couriers] ADD DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[Orders] ADD DEFAULT (getdate()) FOR [OrderDate]
GO
ALTER TABLE [dbo].[Orders] ADD DEFAULT ((0)) FOR [IsComplex]
GO
ALTER TABLE [dbo].[Orders] ADD DEFAULT ((0)) FOR [BonusAmount]
GO
ALTER TABLE [dbo].[Payments] ADD DEFAULT (getdate()) FOR [PaymentDate]
GO
ALTER TABLE [dbo].[Payments] ADD DEFAULT ((0)) FOR [IsPaid]
GO
ALTER TABLE [dbo].[Users] ADD DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([ClientId])
REFERENCES [dbo].[Clients] ([ClientId])
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([CourierId])
REFERENCES [dbo].[Couriers] ([CourierId])
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([CreatedBy])
REFERENCES [dbo].[Users] ([UserId])
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([StatusId])
REFERENCES [dbo].[OrderStatuses] ([StatusId])
GO
ALTER TABLE [dbo].[Payments] WITH CHECK ADD FOREIGN KEY([CourierId])
REFERENCES [dbo].[Couriers] ([CourierId])
34.
GOALTER TABLE [dbo].[Payments] WITH CHECK ADD FOREIGN KEY([OrderId])
REFERENCES [dbo].[Orders] ([OrderId])
GO
ALTER TABLE [dbo].[Users] WITH CHECK ADD FOREIGN KEY([CourierId])
REFERENCES [dbo].[Couriers] ([CourierId])
ON DELETE SET NULL
GO
ALTER TABLE [dbo].[Users] WITH CHECK ADD CHECK (([Role]='Courier' OR
[Role]='Admin'))
GO
35.
Приложение 2Интерфейс программы
Рис. 18. Окно авторизации (LoginWindow)
Рис. 19. Главное окно администратора (AdminWindow)
Рис. 20. Заказы
Рис. 21. Курьеры
Рис. 22. Отчёты
Рис. 23. Выплаты
36.
Рис. 24. Окно добавления заказа (AddEditOrderWindow)Рис. 25. Окно добавления курьера (AddEditCourierWindow)
Рис. 26. При редактировании «Выберите заказ»
Рис. 27. Окно редактирования заказа (AddEditOrderWindow)
Рис. 28. Окно курьера (CourierWindow)
Рис. 29. Ошибка «Неправильный логин или пароль»
37.
Приложение 3Коды программы
LoginWindow.xaml (Окно авторизации):
<!-- Окно входа в систему -->
<Window x:Class="CourierServiceApp.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CourierServiceApp.Views"
mc:Ignorable="d"
Title="Вход в систему" Height="250" Width="350"
WindowStartupLocation="CenterScreen">
<!-- Основная сетка для размещения элементов с отступами -->
<Grid Margin="10">
<!-- Определение строк: 4 строки, автоматическая высота -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- строка для логина -->
<RowDefinition Height="Auto"/> <!-- строка для пароля -->
<RowDefinition Height="Auto"/> <!-- строка для кнопки -->
<RowDefinition Height="Auto"/> <!-- строка для сообщения об ошибке -->
</Grid.RowDefinitions>
<!-- Определение столбцов: первый подписи, второй поля ввода -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <!-- столбец для подписей (Label) -->
<ColumnDefinition Width="*"/> <!-- столбец для полей ввода, занимает
оставшееся место -->
</Grid.ColumnDefinitions>
<!-- Подпись "Логин" -->
<Label Grid.Row="0" Grid.Column="0" Content="Логин:" Margin="5"/>
<!-- Поле ввода логина -->
<TextBox x:Name="txtLogin" Grid.Row="0" Grid.Column="1" Margin="5"/>
<!-- Подпись "Пароль" -->
<Label Grid.Row="1" Grid.Column="0" Content="Пароль:" Margin="5"/>
<!-- Поле ввода пароля (скрытое) -->
<PasswordBox x:Name="txtPassword" Grid.Row="1" Grid.Column="1" Margin="5"/>
<!-- Кнопка входа, занимает оба столбца -->
<Button x:Name="btnLogin" Grid.Row="2" Grid.ColumnSpan="2" Content="Войти"
Width="100" Height="30" Click="btnLogin_Click" Margin="5"/>
<!-- Текстовый блок для отображения ошибок (изначально пустой) -->
<TextBlock x:Name="lblError" Grid.Row="3" Grid.ColumnSpan="2" Foreground="Red"
TextAlignment="Center"/>
</Grid>
38.
</Window>LoginWindow.xaml.cs:
// Пространство имен, содержащее классы представлений (окон) приложения
namespace CourierServiceApp.Views
{
/// <summary>
/// Логика взаимодействия для LoginWindow.xaml
/// </summary>
// Класс окна входа, частичный (связь с XAML)
public partial class LoginWindow : Window
{
// Конструктор окна
public LoginWindow()
{
// Инициализация компонентов, объявленных в XAML (вызывается автоматически)
InitializeComponent();
}
// Обработчик события нажатия на кнопку "Войти"
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
// Получение текста из поля логина
string login = txtLogin.Text;
// Получение пароля из поля PasswordBox
string password = txtPassword.Password;
// Поиск пользователя в таблице Users через статический объект подключения
var user = Connected.connect.Users
.FirstOrDefault(u => u.Login == login && u.Password == password && u.IsActive);
// Если пользователь с такими данными и активный найден
if (user != null)
{
// Проверка роли пользователя
if (user.Role == "Admin")
{
// Создание и отображение окна администратора с передачей данных
пользователя
AdminWindow admin = new AdminWindow(user);
admin.Show();
}
else if (user.Role == "Courier")
{
// Создание и отображение окна курьера с передачей данных пользователя
CourierWindow courier = new CourierWindow(user);
courier.Show();
}
// Закрытие текущего окна входа
this.Close();
}
else
{
39.
// Вывод сообщения об ошибке в текстовый блок (если пользователь не найденили неактивен)
lblError.Text = "Неверный логин/пароль или пользователь неактивен";
}
}
}
}
AdminWindow.xaml (Главное окно администратора):
<!-- Окно администратора -->
<Window x:Class="CourierServiceApp.Views.AdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CourierServiceApp.Views"
mc:Ignorable="d"
Title="Администратор - Курьерская служба" Height="600" Width="900"
WindowStartupLocation="CenterScreen">
<!-- Основная сетка с отступами -->
<Grid Margin="10">
<!-- Определение строк: первая строка для приветствия/выхода, вторая для
содержимого -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Шапка -->
<RowDefinition Height="*"/> <!-- Основная область -->
</Grid.RowDefinitions>
<!-- Верхняя панель с приветствием и кнопкой выхода -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <!-- Левая часть: приветствие -->
<ColumnDefinition Width="Auto"/> <!-- Правая часть: кнопка -->
</Grid.ColumnDefinitions>
<TextBlock Text="Добро пожаловать, Администратор!" FontSize="16"
FontWeight="Bold" Margin="5" VerticalAlignment="Center"/>
<Button x:Name="btnLogout" Content="Выход" Width="80" Height="30"
Grid.Column="1" Margin="5" Click="btnLogout_Click"/>
</Grid>
<!-- Элемент управления вкладками -->
<TabControl Grid.Row="1" Name="tabControl">
<!-- Вкладка "Заказы" -->
<TabItem Header="Заказы">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Панель кнопок -->
<RowDefinition Height="*"/> <!-- Таблица заказов -->
</Grid.RowDefinitions>
<!-- Кнопки управления заказами -->
<StackPanel Orientation="Horizontal" Grid.Row="0" Margin="5">
40.
<Button x:Name="btnAddOrder" Content="Добавить заказ" Width="120"Height="25" Margin="5" Click="btnAddOrder_Click"/>
<Button x:Name="btnEditOrder" Content="Редактировать" Width="120"
Height="25" Margin="5" Click="btnEditOrder_Click"/>
<Button x:Name="btnRefreshOrders" Content="Обновить" Width="100"
Height="25" Margin="5" Click="btnRefreshOrders_Click"/>
</StackPanel>
<!-- Таблица (DataGrid) для отображения заказов -->
<DataGrid x:Name="dgOrders" Grid.Row="1" AutoGenerateColumns="False"
IsReadOnly="True" SelectionMode="Single" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="ID заказа" Binding="{Binding OrderId}"
Width="50"/>
<DataGridTextColumn Header="Дата" Binding="{Binding OrderDate,
StringFormat=dd.MM.yyyy HH:mm}" Width="120"/>
<DataGridTextColumn Header="Клиент" Binding="{Binding ClientName}"
Width="150"/>
<DataGridTextColumn Header="Курьер" Binding="{Binding CourierName}"
Width="150"/>
<DataGridTextColumn Header="Статус" Binding="{Binding StatusName}"
Width="100"/>
<DataGridTextColumn Header="Описание" Binding="{Binding
Description}" Width="150"/>
<DataGridTextColumn Header="Адрес" DeliveryAddress}"
Binding="{Binding DeliveryAddress}" Width="150"/>
<DataGridCheckBoxColumn Header="Сложный" Binding="{Binding
IsComplex, Mode=OneWay}" Width="70"/>
<DataGridTextColumn Header="Бонус" Binding="{Binding BonusAmount}"
Width="70"/>
<DataGridTextColumn Header="Сумма" Binding="{Binding TotalAmount}"
Width="80"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
<!-- Вкладка "Курьеры" -->
<TabItem Header="Курьеры">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Button x:Name="btnAddCourier" Content="Добавить курьера" Width="120"
Height="25" Margin="5" Click="btnAddCourier_Click"/>
<Button x:Name="btnRefreshCouriers" Content="Обновить" Width="100"
Height="25" Margin="5" Click="btnRefreshCouriers_Click"/>
</StackPanel>
<DataGrid x:Name="dgCouriers" Grid.Row="1" AutoGenerateColumns="False"
IsReadOnly="True" Margin="5">
41.
<DataGrid.Columns><DataGridTextColumn Header="ID" Binding="{Binding CourierId}"
Width="50"/>
<DataGridTextColumn Header="ФИО" Binding="{Binding FullName}"
Width="200"/>
<DataGridTextColumn Header="Телефон" Binding="{Binding Phone}"
Width="120"/>
<DataGridTextColumn Header="Email" Binding="{Binding Email}"
Width="150"/>
<DataGridTextColumn Header="Ставка" Binding="{Binding BaseRate}"
Width="80"/>
<DataGridCheckBoxColumn Header="Активен" Binding="{Binding IsActive,
Mode=OneWay}" Width="80"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
<!-- Вкладка "Отчёты" -->
<TabItem Header="Отчёты">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Панель выбора отчёта -->
<RowDefinition Height="*"/> <!-- Таблица с результатом -->
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Тип отчёта:"/>
<ComboBox x:Name="cmbReportType" Width="150" Margin="5"
SelectedIndex="0">
<ComboBoxItem Content="Количество заказов по дням"/>
<ComboBoxItem Content="Заработок курьеров за период"/>
</ComboBox>
<Label Content="Начало:"/>
<DatePicker x:Name="dpStart" Margin="5"/>
<Label Content="Конец:"/>
<DatePicker x:Name="dpEnd" Margin="5"/>
<Button x:Name="btnGenerateReport" Content="Сформировать" Width="100"
Height="25" Margin="5" Click="btnGenerateReport_Click"/>
</StackPanel>
<!-- DataGrid для отображения отчёта (столбцы будут генерироваться
динамически) -->
<DataGrid x:Name="dgReport" Grid.Row="1" AutoGenerateColumns="False"
Margin="5"/>
</Grid>
</TabItem>
<!-- Вкладка "Выплаты" -->
<TabItem Header="Выплаты">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Кнопки -->
<RowDefinition Height="*"/> <!-- Таблица выплат -->
42.
</Grid.RowDefinitions><StackPanel Orientation="Horizontal" Margin="5">
<Button x:Name="btnMarkPaid" Content="Отметить выплаченным"
Width="150" Height="25" Margin="5" Click="btnMarkPaid_Click"/>
<Button x:Name="btnRefreshPayments" Content="Обновить" Width="100"
Height="25" Margin="5" Click="btnRefreshPayments_Click"/>
</StackPanel>
<DataGrid x:Name="dgPayments" Grid.Row="1" AutoGenerateColumns="False"
IsReadOnly="True" Margin="5" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Header="ID выплаты" Binding="{Binding
PaymentId}" Width="70"/>
<DataGridTextColumn Header="Курьер" Binding="{Binding CourierName}"
Width="150"/>
<DataGridTextColumn Header="Заказ" Binding="{Binding OrderId}"
Width="70"/>
<DataGridTextColumn Header="Дата" Binding="{Binding PaymentDate,
StringFormat=dd.MM.yyyy HH:mm}" Width="120"/>
<DataGridTextColumn Header="Сумма" Binding="{Binding Amount}"
Width="80"/>
<DataGridCheckBoxColumn Header="Выплачено" Binding="{Binding
IsPaid, Mode=OneWay}" Width="80"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>
AdminWindow.xaml.cs:
// Пространство имен для представлений
namespace CourierServiceApp.Views
{
/// <summary>
/// Логика взаимодействия для AdminWindow.xaml
/// </summary>
// Класс окна администратора (частичный, связь с XAML)
public partial class AdminWindow : Window
{
// Текущий пользователь (администратор)
private Users currentUser;
// Конструктор окна, принимает объект пользователя
public AdminWindow(Users user)
{
InitializeComponent(); // Инициализация компонентов из XAML
currentUser = user; // Сохраняем ссылку на пользователя
// Устанавливаем сегодняшнюю дату в поля выбора дат для отчётов
dpStart.SelectedDate = DateTime.Today;
dpEnd.SelectedDate = DateTime.Today;
43.
// Загружаем данные для вкладокLoadOrders();
// Заказы
LoadCouriers(); // Курьеры
LoadPayments(); // Выплаты (только неоплаченные по умолчанию)
}
// Загрузка списка заказов с проекцией в анонимный тип
private void LoadOrders()
{
var orders = Connected.connect.Orders
.OrderBy(o => o.OrderId) // Сортировка по ID
.Select(o => new
{
o.OrderId,
OrderDate = o.OrderDate,
ClientName = o.Clients.FullName,
// Имя клиента из связанной таблицы
CourierName = o.Couriers != null ? o.Couriers.FullName : "Не назначен", // Имя
курьера или "Не назначен"
StatusName = o.OrderStatuses.StatusName, // Название статуса
o.Description,
o.DeliveryAddress,
o.IsComplex,
o.BonusAmount,
o.TotalAmount
})
.ToList();
dgOrders.ItemsSource = orders; // Привязка к DataGrid
}
// Обработчик кнопки "Обновить" для заказов (вызывает LoadOrders)
private void btnRefreshOrders_Click(object sender, RoutedEventArgs e) => LoadOrders();
// Обработчик кнопки "Добавить заказ"
private void btnAddOrder_Click(object sender, RoutedEventArgs e)
{
// Открываем окно добавления заказа, передаём текущего пользователя
var win = new AddEditOrderWindow(currentUser);
// Если окно закрыто с результатом true, обновляем список заказов
if (win.ShowDialog() == true)
LoadOrders();
}
// Обработчик кнопки "Редактировать заказ"
private void btnEditOrder_Click(object sender, RoutedEventArgs e)
{
// Проверка, выбран ли элемент в DataGrid
if (dgOrders.SelectedItem == null)
{
MessageBox.Show("Выберите заказ");
return;
}
44.
// Получаем ID выбранного заказа (используем dynamic для доступа к свойству)dynamic selected = dgOrders.SelectedItem;
int orderId = selected.OrderId;
// Открываем окно редактирования с передачей ID заказа
var win = new AddEditOrderWindow(currentUser, orderId);
if (win.ShowDialog() == true)
LoadOrders();
}
// Загрузка списка курьеров (просто все записи из таблицы Couriers)
private void LoadCouriers()
{
dgCouriers.ItemsSource = Connected.connect.Couriers.ToList();
}
// Обработчик кнопки "Обновить" для курьеров
private void btnRefreshCouriers_Click(object sender, RoutedEventArgs e) =>
LoadCouriers();
// Обработчик кнопки "Сформировать отчёт"
private void btnGenerateReport_Click(object sender, RoutedEventArgs e)
{
try
{
// Проверка выбора дат
if (dpStart.SelectedDate == null || dpEnd.SelectedDate == null)
{
MessageBox.Show("Выберите начальную и конечную даты");
return;
}
DateTime start = dpStart.SelectedDate.Value; // Начало периода
// Конец периода: добавляем один день и вычитаем секунду, чтобы включить
весь выбранный день
DateTime end = dpEnd.SelectedDate.Value.Date.AddDays(1).AddSeconds(-1);
int selectedIndex = cmbReportType.SelectedIndex; // 0 или 1
if (selectedIndex == 0) // Отчёт "Количество заказов по дням"
{
// Группировка заказов по дате (без времени)
var rawData = Connected.connect.Orders
.Where(o => o.OrderDate >= start && o.OrderDate <= end)
.GroupBy(o => DbFunctions.TruncateTime(o.OrderDate))
.Select(g => new
{
Date = g.Key,
OrderCount = g.Count(),
TotalSum = g.Sum(o => o.TotalAmount)
})
.OrderBy(r => r.Date)
45.
.ToList();// Преобразование для корректного отображения (Date не может быть null)
var report = rawData
.Select(r => new
{
Date = r.Date.Value,
r.OrderCount,
r.TotalSum
})
.ToList();
SetupReportColumnsForDays(); // Настройка колонок для отчёта по дням
dgReport.ItemsSource = report; // Вывод данных
}
else // Отчёт "Заработок курьеров за период"
{
var report = Connected.connect.Payments
.Where(p => p.PaymentDate >= start && p.PaymentDate <= end)
.GroupBy(p => p.Couriers.FullName)
.Select(g => new
{
CourierName = g.Key,
DeliveryCount = g.Count(),
TotalEarned = g.Sum(p => p.Amount)
})
.OrderBy(r => r.CourierName)
.ToList();
SetupReportColumnsForCouriers(); // Настройка колонок для отчёта по курьерам
dgReport.ItemsSource = report;
}
}
catch (Exception ex)
{
MessageBox.Show("Ошибка формирования отчёта: " + ex.Message);
}
}
// Настройка колонок DataGrid для отчёта по дням
private void SetupReportColumnsForDays()
{
dgReport.Columns.Clear();
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Дата",
Binding = new System.Windows.Data.Binding("Date") { StringFormat =
"dd.MM.yyyy" }
});
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Количество заказов",
46.
Binding = new System.Windows.Data.Binding("OrderCount")});
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Сумма доставок",
Binding = new System.Windows.Data.Binding("TotalSum") { StringFormat = "F2" }
});
}
// Настройка колонок DataGrid для отчёта по курьерам
private void SetupReportColumnsForCouriers()
{
dgReport.Columns.Clear();
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Курьер",
Binding = new System.Windows.Data.Binding("CourierName")
});
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Количество доставок",
Binding = new System.Windows.Data.Binding("DeliveryCount")
});
dgReport.Columns.Add(new DataGridTextColumn
{
Header = "Сумма заработка",
Binding = new System.Windows.Data.Binding("TotalEarned") { StringFormat = "F2"
}
});
}
// Загрузка выплат, с возможностью фильтра только неоплаченных
private void LoadPayments(bool onlyUnpaid = true)
{
var query = Connected.connect.Payments.AsQueryable();
if (onlyUnpaid)
query = query.Where(p => !p.IsPaid); // Фильтр по неоплаченным
var payments = query
.Select(p => new
{
p.PaymentId,
CourierName = p.Couriers.FullName,
p.OrderId,
p.PaymentDate,
p.Amount,
p.IsPaid
})
.OrderByDescending(p => p.PaymentDate) // Сортировка: новые сверху
.ToList();
dgPayments.ItemsSource = payments;
47.
}// Обработчик кнопки "Обновить" для выплат (загружает только неоплаченные)
private void btnRefreshPayments_Click(object sender, RoutedEventArgs e) =>
LoadPayments(true);
// Обработчик кнопки "Отметить выплаченным"
private void btnMarkPaid_Click(object sender, RoutedEventArgs e)
{
if (dgPayments.SelectedItem == null)
{
MessageBox.Show("Выберите выплату");
return;
}
// Получаем ID выбранной выплаты
dynamic selected = dgPayments.SelectedItem;
int paymentId = selected.PaymentId;
// Находим запись в базе и помечаем как выплаченную
var payment = Connected.connect.Payments.Find(paymentId);
if (payment != null)
{
payment.IsPaid = true;
Connected.connect.SaveChanges(); // Сохраняем изменения
LoadPayments(true); // Обновляем список (только неоплаченные)
}
}
// Обработчик кнопки "Выход"
private void btnLogout_Click(object sender, RoutedEventArgs e)
{
// Создаём новое окно входа
LoginWindow login = new LoginWindow();
// Подписываемся на событие загрузки окна входа,
// чтобы закрыть текущее окно после того, как новое отобразится
login.Loaded += (s, args) =>
{
this.Close(); // Закрываем окно администратора
};
// Показываем окно входа
login.Show();
}
// Обработчик нажатия на кнопку "Добавить курьера"
private void btnAddCourier_Click(object sender, RoutedEventArgs e)
{
// Создание экземпляра окна добавления/редактирования курьера (новый курьер)
var win = new AddEditCourierWindow();
48.
// Отображение окна модально (блокирует родительское окно) и проверка результата// ShowDialog() возвращает true, если пользователь подтвердил сохранение (нажал
"Сохранить")
if (win.ShowDialog() == true)
{
// Если данные были сохранены, обновляем список курьеров в DataGrid на главном
окне
LoadCouriers();
}
}
}
CourierWindow.xaml (Окно курьера):
<!-- Окно курьера для просмотра и управления своими заказами -->
<Window x:Class="CourierServiceApp.Views.CourierWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CourierServiceApp.Views"
mc:Ignorable="d"
Title="Курьер - Мои заказы" Height="450" Width="800"
WindowStartupLocation="CenterScreen">
<!-- Основная сетка с отступами -->
<Grid Margin="10">
<!-- Определение строк: первая для шапки, вторая для таблицы -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Верхняя панель -->
<RowDefinition Height="*"/> <!-- Таблица заказов -->
</Grid.RowDefinitions>
<!-- Верхняя панель с заголовком и кнопкой выхода -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <!-- Левая часть: заголовок -->
<ColumnDefinition Width="Auto"/> <!-- Правая часть: кнопка выхода -->
</Grid.ColumnDefinitions>
<TextBlock Text="Ваши текущие заказы" FontSize="16" FontWeight="Bold"
Margin="5" VerticalAlignment="Center"/>
<Button x:Name="btnLogout" Content="Выход" Width="80" Height="30"
Grid.Column="1" Margin="5" Click="btnLogout_Click"/>
</Grid>
<!-- Таблица (DataGrid) для отображения заказов, назначенных текущему курьеру -->
<DataGrid x:Name="dgMyOrders" Grid.Row="1" AutoGenerateColumns="False"
IsReadOnly="True" SelectionMode="Single" Margin="5">
<DataGrid.Columns>
<!-- Колонка с идентификатором заказа -->
<DataGridTextColumn Header="ID" Binding="{Binding OrderId}" Width="50"/>
<!-- Дата и время заказа с форматированием -->
<DataGridTextColumn Header="Дата" Binding="{Binding OrderDate,
StringFormat=dd.MM.yyyy HH:mm}" Width="120"/>
49.
<!-- Имя клиента --><DataGridTextColumn Header="Клиент" Binding="{Binding ClientName}"
Width="150"/>
<!-- Адрес доставки -->
<DataGridTextColumn Header="Адрес" Binding="{Binding DeliveryAddress}"
Width="200"/>
<!-- Текущий статус заказа -->
<DataGridTextColumn Header="Статус" Binding="{Binding StatusName}"
Width="100"/>
<!-- Описание заказа -->
<DataGridTextColumn Header="Описание" Binding="{Binding Description}"
Width="150"/>
<!-- Шаблонная колонка с кнопками действий для каждой строки -->
<DataGridTemplateColumn Header="Действия">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- Кнопка "В путь" меняет статус заказа на "В пути" -->
<Button Content="В путь" Width="75" Margin="2"
Click="BtnEnRoute_Click" Tag="{Binding OrderId}"/>
<!-- Кнопка "Доставлен" меняет статус на "Доставлен" и начисляет
выплату -->
<Button Content="Доставлен" Width="85" Margin="2"
Click="BtnDelivered_Click" Tag="{Binding OrderId}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
CourierWindow.xaml.cs:
// Пространство имен для представлений (окон)
namespace CourierServiceApp.Views
{
/// <summary>
/// Логика взаимодействия для CourierWindow.xaml
/// </summary>
// Класс окна курьера, частичный (связь с XAML)
public partial class CourierWindow : Window
{
// Текущий пользователь (курьер), вошедший в систему
private Users currentUser;
// Конструктор окна, принимает объект пользователя-курьера
public CourierWindow(Users user)
{
InitializeComponent(); // Инициализация компонентов из XAML
currentUser = user; // Сохраняем ссылку на курьера
LoadMyOrders();
// Загружаем список его заказов
50.
}// Загрузка заказов, назначенных текущему курьеру (кроме доставленных и
отменённых)
private void LoadMyOrders()
{
var orders = Connected.connect.Orders
// Фильтр: только заказы текущего курьера
.Where(o => o.CourierId == currentUser.CourierId &&
// Исключаем заказы со статусом "Доставлен" или "Отменён"
!(o.OrderStatuses.StatusName == "Доставлен" || o.OrderStatuses.StatusName ==
"Отменён"))
// Проекция в анонимный тип с нужными полями
.Select(o => new
{
o.OrderId,
o.OrderDate,
ClientName = o.Clients.FullName, // Имя клиента из связанной таблицы
o.DeliveryAddress,
StatusName = o.OrderStatuses.StatusName, // Название статуса
o.Description
})
.OrderBy(o => o.OrderDate) // Сортировка по дате
.ToList();
// Привязка данных к DataGrid на форме
dgMyOrders.ItemsSource = orders;
}
// Обработчик нажатия кнопки "В путь"
private void BtnEnRoute_Click(object sender, RoutedEventArgs e)
{
// Получаем ID заказа из свойства Tag кнопки
int orderId = (int)((Button)sender).Tag;
// Обновляем статус заказа на "В пути" (без создания выплаты)
UpdateOrderStatus(orderId, "В пути");
}
// Обработчик нажатия кнопки "Доставлен"
private void BtnDelivered_Click(object sender, RoutedEventArgs e)
{
// Получаем ID заказа из свойства Tag кнопки
int orderId = (int)((Button)sender).Tag;
// Обновляем статус на "Доставлен" и создаём выплату курьеру
UpdateOrderStatus(orderId, "Доставлен", createPayment: true);
}
// Общий метод обновления статуса заказа и, при необходимости, создания выплаты
private void UpdateOrderStatus(int orderId, string statusName, bool createPayment = false)
{
// Загружаем заказ вместе с данными курьера (нужно для расчёта выплаты)
var order = Connected.connect.Orders
51.
.Include(o => o.Couriers) // Жадная загрузка связанной сущности Courier.FirstOrDefault(o => o.OrderId == orderId);
if (order == null) return; // Если заказ не найден, выходим
// Ищем статус по его названию
var status = Connected.connect.OrderStatuses.FirstOrDefault(s => s.StatusName ==
statusName);
if (status == null)
{
MessageBox.Show("Статус не найден");
return;
}
// Меняем идентификатор статуса у заказа
order.StatusId = status.StatusId;
// Если нужно создать выплату и новый статус "Доставлен"
if (createPayment && statusName == "Доставлен")
{
// Проверяем, что курьер назначен (на всякий случай)
if (order.Couriers == null)
{
MessageBox.Show("У заказа не назначен курьер");
return;
}
// Расчёт суммы выплаты: базовая ставка курьера + бонус (если есть)
decimal amount = order.Couriers.BaseRate + (order.BonusAmount ?? 0);
// Создаём новую запись о выплате
var payment = new Payments
{
CourierId = order.CourierId.Value,
OrderId = order.OrderId,
PaymentDate = DateTime.Now,
Amount = amount,
IsPaid = false // Изначально не выплачено
};
// Добавляем выплату в контекст базы данных
Connected.connect.Payments.Add(payment);
}
// Сохраняем все изменения в базе данных
Connected.connect.SaveChanges();
// Обновляем список заказов на форме
LoadMyOrders();
// Информируем пользователя об успешном обновлении
MessageBox.Show("Статус обновлён");
52.
}// Обработчик кнопки "Выход"
private void btnLogout_Click(object sender, RoutedEventArgs e)
{
// Создаём окно входа
LoginWindow login = new LoginWindow();
// Подписываемся на событие загрузки окна входа,
// чтобы закрыть текущее окно только после того, как новое отобразится
login.Loaded += (s, args) =>
{
this.Close(); // Закрываем окно курьера
};
// Показываем окно входа
login.Show();
}
}
}
AddEditOrderWindow.xaml (Окно добавления/редактирования заказа):
<!-- Окно добавления/редактирования заказа (открывается из AdminWindow) -->
<Window x:Class="CourierServiceApp.Views.AddEditOrderWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CourierServiceApp.Views"
mc:Ignorable="d"
Title="Редактирование заказа" Height="450" Width="500"
WindowStartupLocation="CenterOwner">
<!-- Основная сетка с отступами -->
<Grid Margin="10">
<!-- Определение 9 строк для размещения полей ввода -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Клиент -->
<RowDefinition Height="Auto"/> <!-- Курьер -->
<RowDefinition Height="Auto"/> <!-- Статус -->
<RowDefinition Height="Auto"/> <!-- Описание -->
<RowDefinition Height="Auto"/> <!-- Адрес доставки -->
<RowDefinition Height="Auto"/> <!-- Сложный заказ? -->
<RowDefinition Height="Auto"/> <!-- Бонус -->
<RowDefinition Height="Auto"/> <!-- Сумма доставки -->
<RowDefinition Height="Auto"/> <!-- Кнопки Сохранить/Отмена -->
</Grid.RowDefinitions>
<!-- Два столбца: слева подписи, справа поля ввода -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <!-- Столбец для подписей (Label) -->
<ColumnDefinition Width="*"/> <!-- Столбец для полей ввода, занимает
оставшееся место -->
53.
</Grid.ColumnDefinitions><!-- Подпись "Клиент" и выпадающий список клиентов -->
<Label Grid.Row="0" Grid.Column="0" Content="Клиент:" Margin="5"/>
<ComboBox x:Name="cmbClient" Grid.Row="0" Grid.Column="1"
DisplayMemberPath="FullName" SelectedValuePath="ClientId" Margin="5"/>
<!-- Подпись "Курьер" и выпадающий список курьеров -->
<Label Grid.Row="1" Grid.Column="0" Content="Курьер:" Margin="5"/>
<ComboBox x:Name="cmbCourier" Grid.Row="1" Grid.Column="1"
DisplayMemberPath="FullName" SelectedValuePath="CourierId" Margin="5"/>
<!-- Подпись "Статус" и выпадающий список статусов заказа -->
<Label Grid.Row="2" Grid.Column="0" Content="Статус:" Margin="5"/>
<ComboBox x:Name="cmbStatus" Grid.Row="2" Grid.Column="1"
DisplayMemberPath="StatusName" SelectedValuePath="StatusId" Margin="5"/>
<!-- Подпись "Описание" и многострочное текстовое поле для описания заказа -->
<Label Grid.Row="3" Grid.Column="0" Content="Описание:" Margin="5"/>
<TextBox x:Name="txtDescription" Grid.Row="3" Grid.Column="1" Margin="5"
Height="60" TextWrapping="Wrap"/>
<!-- Подпись "Адрес доставки" и текстовое поле -->
<Label Grid.Row="4" Grid.Column="0" Content="Адрес доставки:" Margin="5"/>
<TextBox x:Name="txtAddress" Grid.Row="4" Grid.Column="1" Margin="5"/>
<!-- Подпись "Сложный заказ?" и чекбокс (флаг) -->
<Label Grid.Row="5" Grid.Column="0" Content="Сложный заказ?" Margin="5"/>
<CheckBox x:Name="chkIsComplex" Grid.Row="5" Grid.Column="1" Margin="5"
VerticalAlignment="Center"/>
<!-- Подпись "Бонус:" и текстовое поле для ввода суммы бонуса -->
<Label Grid.Row="6" Grid.Column="0" Content="Бонус:" Margin="5"/>
<TextBox x:Name="txtBonus" Grid.Row="6" Grid.Column="1" Margin="5" Text="0"/>
<!-- Подпись "Сумма доставки:" и текстовое поле для общей суммы -->
<Label Grid.Row="7" Grid.Column="0" Content="Сумма доставки:" Margin="5"/>
<TextBox x:Name="txtTotal" Grid.Row="7" Grid.Column="1" Margin="5"/>
<!-- Панель с кнопками "Сохранить" и "Отмена", расположенная по центру -->
<StackPanel Grid.Row="8" Grid.ColumnSpan="2" Orientation="Horizontal"
HorizontalAlignment="Center" Margin="10">
<Button x:Name="btnSave" Content="Сохранить" Width="100" Height="30"
Margin="5" Click="btnSave_Click"/>
<Button x:Name="btnCancel" Content="Отмена" Width="100" Height="30"
Margin="5" Click="btnCancel_Click"/>
</StackPanel>
</Grid>
</Window>
AddEditOrderWindow.xaml.cs:
// Пространство имен для представлений (окон)
namespace CourierServiceApp.Views
54.
{/// <summary>
/// Логика взаимодействия для AddEditOrderWindow.xaml
/// </summary>
// Класс окна добавления/редактирования заказа (частичный, связь с XAML)
public partial class AddEditOrderWindow : Window
{
// Хранит ID заказа, если редактируем (иначе null)
private int? editOrderId;
// Текущий пользователь (администратор), который открыл окно
private Users currentUser;
// Объект заказа при редактировании
private Orders currentOrder;
// Конструктор: принимает пользователя и опционально ID заказа
public AddEditOrderWindow(Users user, int? orderId = null)
{
InitializeComponent(); // Инициализация компонентов из XAML
currentUser = user; // Сохраняем пользователя
editOrderId = orderId; // Сохраняем ID заказа (null для нового)
LoadComboBoxes(); // Заполнение выпадающих списков
// Если передан ID (режим редактирования), загружаем данные заказа
if (editOrderId.HasValue)
{
currentOrder = Connected.connect.Orders.Find(editOrderId.Value);
if (currentOrder != null)
LoadOrderData(); // Заполняем поля формы данными из БД
}
}
// Загрузка данных в выпадающие списки (клиенты, курьеры, статусы)
private void LoadComboBoxes()
{
// Загрузка списка клиентов
cmbClient.ItemsSource = Connected.connect.Clients.ToList();
cmbClient.DisplayMemberPath = "FullName";
cmbClient.SelectedValuePath = "ClientId";
// Загрузка активных курьеров + добавляем пустую запись "Не назначен"
var couriers = Connected.connect.Couriers.Where(c => c.IsActive).ToList();
couriers.Insert(0, new Couriers { CourierId = 0, FullName = "Не назначен" });
cmbCourier.ItemsSource = couriers;
cmbCourier.DisplayMemberPath = "FullName";
cmbCourier.SelectedValuePath = "CourierId";
// Загрузка списка статусов заказа
cmbStatus.ItemsSource = Connected.connect.OrderStatuses.ToList();
cmbStatus.DisplayMemberPath = "StatusName";
cmbStatus.SelectedValuePath = "StatusId";
}
55.
// Заполнение полей формы данными редактируемого заказаprivate void LoadOrderData()
{
cmbClient.SelectedValue = currentOrder.ClientId;
// Если курьер не назначен (null), выбираем запись с ID=0 ("Не назначен")
cmbCourier.SelectedValue = currentOrder.CourierId ?? 0;
cmbStatus.SelectedValue = currentOrder.StatusId;
txtDescription.Text = currentOrder.Description;
txtAddress.Text = currentOrder.DeliveryAddress;
chkIsComplex.IsChecked = currentOrder.IsComplex;
txtBonus.Text = currentOrder.BonusAmount?.ToString() ?? "0";
txtTotal.Text = currentOrder.TotalAmount.ToString();
}
// Обработчик кнопки "Сохранить"
private void btnSave_Click(object sender, RoutedEventArgs e)
{
// Валидация обязательных полей
if (cmbClient.SelectedValue == null || cmbStatus.SelectedValue == null ||
string.IsNullOrWhiteSpace(txtAddress.Text) ||
string.IsNullOrWhiteSpace(txtTotal.Text))
{
MessageBox.Show("Заполните обязательные поля: Клиент, Статус, Адрес,
Сумма");
return;
}
try
{
if (editOrderId.HasValue) // Режим редактирования
{
// Обновляем поля существующего заказа
currentOrder.ClientId = (int)cmbClient.SelectedValue;
currentOrder.CourierId = (int?)cmbCourier.SelectedValue;
if (currentOrder.CourierId == 0) currentOrder.CourierId = null; // Преобразуем 0 в
null
currentOrder.StatusId = (int)cmbStatus.SelectedValue;
currentOrder.Description = txtDescription.Text;
currentOrder.DeliveryAddress = txtAddress.Text;
currentOrder.IsComplex = chkIsComplex.IsChecked ?? false;
currentOrder.BonusAmount = decimal.TryParse(txtBonus.Text, out decimal bonus)
? bonus : 0;
currentOrder.TotalAmount = decimal.Parse(txtTotal.Text);
}
else // Режим добавления нового заказа
{
var newOrder = new Orders
{
OrderDate = DateTime.Now, // Устанавливаем текущую дату
ClientId = (int)cmbClient.SelectedValue,
CourierId = (int?)cmbCourier.SelectedValue,
56.
StatusId = (int)cmbStatus.SelectedValue,Description = txtDescription.Text,
DeliveryAddress = txtAddress.Text,
IsComplex = chkIsComplex.IsChecked ?? false,
BonusAmount = decimal.TryParse(txtBonus.Text, out decimal bonus) ? bonus :
0,
TotalAmount = decimal.Parse(txtTotal.Text),
CreatedBy = currentUser.UserId // Кто создал заказ
};
if (newOrder.CourierId == 0) newOrder.CourierId = null; // Преобразуем 0 в null
Connected.connect.Orders.Add(newOrder); // Добавляем в контекст
}
// Сохраняем изменения в БД
Connected.connect.SaveChanges();
DialogResult = true; // Указываем, что результат успешный
Close(); // Закрываем окно
}
catch (Exception ex)
{
MessageBox.Show("Ошибка сохранения: " + ex.Message);
}
}
// Обработчик кнопки "Отмена" - закрывает окно без сохранения
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
}
}
AddEditCourierWindow.xaml (Окно добавления курьера):
<!-- Окно добавления/редактирования курьера (открывается из AdminWindow) -->
<Window x:Class="CourierServiceApp.Views.AddEditCourierWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CourierServiceApp.Views"
mc:Ignorable="d"
Title="Добавление/Редактирование курьера" Height="350" Width="400"
WindowStartupLocation="CenterOwner">
<!-- Основная сетка для размещения элементов -->
<Grid Margin="10">
<!-- Определение 7 строк: 5 строк для полей + строка для флага активности + строка
для кнопок -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- ФИО -->
<RowDefinition Height="Auto"/> <!-- Телефон -->
<RowDefinition Height="Auto"/> <!-- Email -->
57.
<RowDefinition Height="Auto"/> <!-- Базовая ставка --><RowDefinition Height="Auto"/> <!-- Активен -->
<RowDefinition Height="Auto"/> <!-- Кнопки -->
</Grid.RowDefinitions>
<!-- Два столбца: подписи (слева) и поля ввода (справа) -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Подпись для поля ФИО -->
<Label Grid.Row="0" Grid.Column="0" Content="ФИО:" Margin="5"/>
<!-- Поле ввода ФИО курьера -->
<TextBox x:Name="txtFullName" Grid.Row="0" Grid.Column="1" Margin="5"
Width="200"/>
<!-- Подпись для поля телефона -->
<Label Grid.Row="1" Grid.Column="0" Content="Телефон:" Margin="5"/>
<!-- Поле ввода номера телефона -->
<TextBox x:Name="txtPhone" Grid.Row="1" Grid.Column="1" Margin="5"/>
<!-- Подпись для поля email -->
<Label Grid.Row="2" Grid.Column="0" Content="Email:" Margin="5"/>
<!-- Поле ввода электронной почты -->
<TextBox x:Name="txtEmail" Grid.Row="2" Grid.Column="1" Margin="5"/>
<!-- Подпись для базовой ставки -->
<Label Grid.Row="3" Grid.Column="0" Content="Базовая ставка:" Margin="5"/>
<!-- Поле ввода базовой ставки (по умолчанию 150.00) -->
<TextBox x:Name="txtBaseRate" Grid.Row="3" Grid.Column="1" Margin="5"
Text="150.00"/>
<!-- Подпись для флага активности -->
<Label Grid.Row="4" Grid.Column="0" Content="Активен:" Margin="5"/>
<!-- Флажок, указывающий, активен ли курьер (по умолчанию true) -->
<CheckBox x:Name="chkIsActive" Grid.Row="4" Grid.Column="1" Margin="5"
IsChecked="True" VerticalAlignment="Center"/>
<!-- Горизонтальная панель с кнопками управления (растянута на оба столбца) -->
<StackPanel Grid.Row="5" Grid.ColumnSpan="2" Orientation="Horizontal"
HorizontalAlignment="Center" Margin="10">
<!-- Кнопка сохранения данных (добавление или обновление) -->
<Button x:Name="btnSave" Content="Сохранить" Width="100" Height="30"
Margin="5" Click="btnSave_Click"/>
<!-- Кнопка отмены без сохранения -->
<Button x:Name="btnCancel" Content="Отмена" Width="100" Height="30"
Margin="5" Click="btnCancel_Click"/>
</StackPanel>
</Grid>
</Window>
AddEditCourierWindow.xaml.cs:
58.
// Пространство имен для представлений (окон)namespace CourierServiceApp.Views
{
/// <summary>
/// Логика взаимодействия для AddEditCourierWindow.xaml
/// </summary>
// Класс окна добавления/редактирования курьера (частичный, связь с XAML)
public partial class AddEditCourierWindow : Window
{
// Хранит ID курьера при редактировании (null для нового курьера)
private int? editCourierId;
// Объект курьера при редактировании
private Couriers currentCourier;
// Конструктор окна, принимает опциональный ID курьера
public AddEditCourierWindow(int? courierId = null)
{
InitializeComponent(); // Инициализация компонентов из XAML
editCourierId = courierId; // Сохраняем переданный ID
// Если ID передан (режим редактирования), загружаем данные курьера из БД
if (editCourierId.HasValue)
{
currentCourier = Connected.connect.Couriers.Find(editCourierId.Value);
if (currentCourier != null)
LoadCourierData(); // Заполняем поля формы данными из БД
}
}
// Загрузка данных редактируемого курьера в поля формы
private void LoadCourierData()
{
txtFullName.Text = currentCourier.FullName;
txtPhone.Text = currentCourier.Phone;
txtEmail.Text = currentCourier.Email;
txtBaseRate.Text = currentCourier.BaseRate.ToString(); // Преобразуем число в строку
chkIsActive.IsChecked = currentCourier.IsActive;
}
// Обработчик нажатия кнопки "Сохранить"
private void btnSave_Click(object sender, RoutedEventArgs e)
{
// Валидация обязательных полей (ФИО, телефон, ставка)
if (string.IsNullOrWhiteSpace(txtFullName.Text) ||
string.IsNullOrWhiteSpace(txtPhone.Text) ||
string.IsNullOrWhiteSpace(txtBaseRate.Text))
{
MessageBox.Show("Заполните все обязательные поля (ФИО, телефон, ставка)");
return;
}
// Попытка преобразовать текст ставки в число (decimal)
59.
if (!decimal.TryParse(txtBaseRate.Text, out decimal baseRate)){
MessageBox.Show("Некорректное значение ставки");
return;
}
try
{
if (editCourierId.HasValue) // Режим редактирования
{
// Обновляем поля существующего курьера
currentCourier.FullName = txtFullName.Text;
currentCourier.Phone = txtPhone.Text;
currentCourier.Email = txtEmail.Text;
currentCourier.BaseRate = baseRate;
currentCourier.IsActive = chkIsActive.IsChecked ?? true; // Если null,
устанавливаем true
}
else // Режим добавления нового курьера
{
// Создаём новый объект курьера
var newCourier = new Couriers
{
FullName = txtFullName.Text,
Phone = txtPhone.Text,
Email = txtEmail.Text,
BaseRate = baseRate,
IsActive = chkIsActive.IsChecked ?? true
};
// Добавляем в контекст базы данных
Connected.connect.Couriers.Add(newCourier);
}
// Сохраняем изменения в базе данных
Connected.connect.SaveChanges();
// Устанавливаем результат диалога как успешный (true)
DialogResult = true;
// Закрываем окно
Close();
}
catch (Exception ex)
{
// Показываем сообщение об ошибке при сохранении
MessageBox.Show("Ошибка сохранения: " + ex.Message);
}
}
// Обработчик кнопки "Отмена" - закрывает окно без сохранения
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false; // Результат отмены
Close();
60.
}}
}
App.xaml
<!-- Корневой элемент приложения WPF -->
<Application x:Class="CourierServiceApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Views/LoginWindow.xaml"> <!-- Указывает стартовое окно, которое
будет открыто при запуске приложения -->
<!-- Ресурсы приложения, доступные глобально во всём приложении -->
<Application.Resources>
<!-- Словарь ресурсов, содержащий все стили, шаблоны и т.д. -->
<ResourceDictionary>
<!-- Коллекция объединённых словарей ресурсов (можно подключать внешние
файлы) -->
<ResourceDictionary.MergedDictionaries>
<!-- Подключаем внешний файл ресурсов из папки Styles -->
<ResourceDictionary Source="Styles/UIResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- Здесь можно было бы добавить и локальные ресурсы, но в данном случае их
нет -->
</ResourceDictionary>
</Application.Resources>
</Application>
App.xaml.cs:
// Пространство имен корневого уровня приложения
namespace CourierServiceApp
{
/// <summary>
/// Логика взаимодействия для App.xaml
/// </summary>
// Класс приложения, частичный (связь с XAML), наследуется от Application
public partial class App : Application
{
// Переопределяем метод OnStartup, который вызывается при запуске приложения
protected override void OnStartup(StartupEventArgs e)
{
// Вызов базового метода (важно для корректной инициализации)
base.OnStartup(e);
// Подписка на глобальные необработанные исключения в домене приложения
// (ловит исключения из потоков не UI, например фоновые задачи)
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
// Получаем исключение из объекта args
Exception ex = (Exception)args.ExceptionObject;
// Показываем сообщение об ошибке с деталями
MessageBox.Show($"Критическая ошибка: {ex.Message}\n\n{ex.StackTrace}",
"Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
// Завершаем приложение с кодом ошибки 1 (аварийное завершение)
61.
Environment.Exit(1);};
// Подписка на необработанные исключения в диспетчере UI (основной поток)
DispatcherUnhandledException += (sender, args) =>
{
// Показываем сообщение об ошибке, но не завершаем приложение
MessageBox.Show($"Ошибка в UI: {args.Exception.Message}", "Ошибка",
MessageBoxButton.OK, MessageBoxImage.Error);
// Помечаем исключение как обработанное, чтобы приложение не закрылось
args.Handled = true;
};
}
}
}
UIResources.xaml (Интерфейс):
<!-- Ресурсный словарь, содержащий цвета, кисти и стили для всего приложения -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- === Цветовая палитра (определения цветов) === -->
<Color x:Key="PrimaryColor">#3498db</Color>
<!-- Основной цвет (синий) -->
<Color x:Key="PrimaryDarkColor">#2980b9</Color> <!-- Тёмный вариант основного
цвета -->
<Color x:Key="AccentColor">#2ecc71</Color>
<!-- Акцентный цвет (зелёный) -->
<Color x:Key="BackgroundColor">#f5f5f5</Color>
<!-- Цвет фона окон -->
<Color x:Key="HeaderColor">#34495e</Color>
<!-- Цвет заголовков (тёмно-синий)
-->
<Color x:Key="TextColor">#2c3e50</Color>
<!-- Цвет текста -->
<Color x:Key="ErrorColor">#e74c3c</Color>
<!-- Цвет ошибок (красный) -->
<!-- === Кисти (на основе определённых цветов) для использования в стилях === -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="PrimaryDarkBrush" Color="{StaticResource
PrimaryDarkColor}"/>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/>
<SolidColorBrush x:Key="BackgroundBrush" Color="{StaticResource BackgroundColor}"/>
<SolidColorBrush x:Key="HeaderBrush" Color="{StaticResource HeaderColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}"/>
<!-- === Стиль для всех окон (Window) === -->
<Style TargetType="Window">
<Setter Property="Background" Value="{StaticResource BackgroundBrush}"/> <!-- Фон
окна -->
<Setter Property="FontFamily" Value="Segoe UI"/>
<!-- Шрифт по
умолчанию -->
<Setter Property="FontSize" Value="18"/>
<!-- Размер шрифта -->
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<!-- Цвет
текста -->
</Style>
62.
<!-- === Стиль для всех кнопок (Button) === --><Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/> <!-- Фон
кнопки (основной цвет) -->
<Setter Property="Foreground" Value="White"/>
<!-- Цвет текста
(белый) -->
<Setter Property="BorderThickness" Value="0"/>
<!-- Без рамки -->
<Setter Property="Padding" Value="10,5"/>
<!-- Внутренние отступы
-->
<Setter Property="FontWeight" Value="SemiBold"/>
<!-- Полужирное
начертание -->
<Setter Property="Cursor" Value="Hand"/>
<!-- Курсор в виде руки ->
<!-- Переопределение визуального шаблона кнопки (убираем стандартный, делаем
скруглённый фон) -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- Триггеры для изменения внешнего вида при наведении и нажатии -->
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource PrimaryDarkBrush}"/> <!-Темнеет при наведении -->
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
<!-Становится зелёной при нажатии -->
</Trigger>
</Style.Triggers>
</Style>
<!-- === Стиль для всех текстовых полей (TextBox) === -->
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="#ccc"/>
<!-- Цвет рамки (серый) -->
<Setter Property="BorderThickness" Value="1"/> <!-- Толщина рамки -->
<Setter Property="Padding" Value="5"/>
<!-- Внутренние отступы -->
<Setter Property="VerticalContentAlignment" Value="Center"/> <!-- Выравнивание
текста по вертикали -->
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBrush}"/> <!-Синяя рамка при фокусе -->
<Setter Property="BorderThickness" Value="2"/>
<!-- Утолщается
рамка -->
63.
</Trigger></Style.Triggers>
</Style>
<!-- === Стиль для поля ввода пароля (PasswordBox) — аналогично TextBox === -->
<Style TargetType="PasswordBox">
<Setter Property="BorderBrush" Value="#ccc"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- === Стиль для выпадающих списков (ComboBox) — простая серая рамка === -->
<Style TargetType="ComboBox">
<Setter Property="BorderBrush" Value="#ccc"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
</Style>
<!-- === Стиль для выбора даты (DatePicker) — серая рамка === -->
<Style TargetType="DatePicker">
<Setter Property="BorderBrush" Value="#ccc"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<!-- === Стиль для вкладок (TabItem) — настраиваем внешний вид заголовков === -->
<Style TargetType="TabItem">
<!-- Шаблон заголовка для невыбранной вкладки (серый фон) -->
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Border x:Name="Border" Padding="15,5" Background="#f0f0f0"
CornerRadius="4,4,0,0">
<ContentPresenter Content="{Binding}"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- Триггер: для выбранной вкладки меняем шаблон на синий фон с белым текстом ->
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
64.
<Border Padding="15,5" Background="{StaticResource PrimaryBrush}"CornerRadius="4,4,0,0">
<TextBlock Text="{Binding}" Foreground="White" FontWeight="Bold"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<!-- === Стиль для DataGrid (таблица) === -->
<Style TargetType="DataGrid">
<Setter Property="AutoGenerateColumns" Value="False"/>
<!-- Отключаем автогенерацию колонок -->
<Setter Property="HeadersVisibility" Value="Column"/>
<!-- Показываем только
заголовки столбцов -->
<Setter Property="GridLinesVisibility" Value="None"/>
<!-- Без линий сетки -->
<Setter Property="AlternatingRowBackground" Value="#f9f9f9"/> <!-- Фон для чётных
строк (светло-серый) -->
<Setter Property="RowBackground" Value="White"/>
<!-- Фон для нечётных
строк (белый) -->
<Setter Property="BorderBrush" Value="#ddd"/>
<!-- Цвет рамки таблицы -->
<Setter Property="BorderThickness" Value="1"/>
<!-- Толщина рамки -->
<Setter Property="RowHeight" Value="50"/>
<!-- Высота строк -->
<Setter Property="FontSize" Value="14"/>
<!-- Размер шрифта в ячейках ->
</Style>
<!-- === Стиль для заголовков столбцов DataGrid === -->
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="{StaticResource HeaderBrush}"/> <!-- Тёмносиний фон -->
<Setter Property="Foreground" Value="White"/>
<!-- Белый текст -->
<Setter Property="FontWeight" Value="Bold"/>
<!-- Жирный шрифт -->
<Setter Property="Height" Value="50"/>
<!-- Высота заголовка -->
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<!-Выравнивание по центру -->
</Style>
<!-- === Стиль для подписей (Label) === -->
<Style TargetType="Label">
<Setter Property="FontWeight" Value="SemiBold"/>
<!-- Полужирный -->
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/> <!-- Цвет текста
из палитры -->
<Setter Property="Margin" Value="0,5,5,5"/>
<!-- Отступы: сверху/снизу 5,
справа 5 -->
<Setter Property="VerticalContentAlignment" Value="Center"/> <!-- Выравнивание по
вертикали -->
</Style>
</ResourceDictionary>
65.
Connected.cs (Подключение базы данных):// Пространство имен для работы с базой данных
namespace CourierServiceApp.BD
{
// Внутренний класс, обеспечивающий глобальный доступ к контексту базы данных
internal class Connected
{
// Статическое поле, хранящее единственный экземпляр контекста Entity Framework
// (упрощённая реализация доступа к БД через общий объект)
public static CourierServiceDBEntities connect = new CourierServiceDBEntities();
}
}
66.
РЕЦЕНЗИЯна курсовую работу
Ф.И.О. обучающегося_______________________________________________
Дисциплина _______________________________________________________
Курс 3 Группа 931
Специальность 09.02.07. «Информационные системы и программирование»
Тема курсовой работы _______________________________________________
__________________________________________________________________
Руководитель ______________________________________________________
Работа к защите допущена / не допущена (нужное подчеркнуть)
Оценочный лист курсовой работы
№
Качественные характеристики работы
I.
1
2
Оценка работы по формальным критериям
Соответствие работы требованиям по оформлению
Соответствие приведенных в оглавлении названий заголовков и
нумерация страниц тексту работы
Наличие иллюстративного материала в работе
Соблюдение сроков сдачи работы по этапам написания
Оценка работы по содержанию
Соответствие содержания работы заявленной теме
Соответствие содержания разделов их названию
Во введении отражена актуальность, цели, задачи, объект
исследования
Ясность, четкость, последовательность и обоснованность
изложения
Осуществлен глубокий, последовательный анализ литературы
Использовались Интернет-ресурсы, периодические издания
Разработан программный продукт
Определены рекомендации использования программного
продукта
Выполнен анализ созданного программного средства
Представлен качественный и количественный анализ данных
В заключении представлены основные выводы по теме работы
ИТОГО:
3
4
II.
1
2
3
4
5
6
7
8
9
11
12
отлично
30-26
хорошо
25-21
Подпись руководителя
КРИТЕРИИ ОЦЕНКИ
удовлетворительно
20-16
Макс.
балл
8
2
2
Факт.
балл
2
2
22
2
2
2
2
2
2
2
2
2
2
2
30
неудовлетворительно
Менее 15
programming