Initial commit
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
#include "directories_management_widget.h"
|
||||
|
||||
// Genre
|
||||
#include "create_genre_request.h"
|
||||
#include "genres_model.h"
|
||||
#include "genres_repository.h"
|
||||
|
||||
// Hall
|
||||
#include "create_hall_request.h"
|
||||
#include "halls_model.h"
|
||||
#include "halls_repository.h"
|
||||
|
||||
// Movie
|
||||
#include "create_movie_request.h"
|
||||
#include "movies_model.h"
|
||||
#include "movies_repository.h"
|
||||
|
||||
// Common
|
||||
#include "error_dialog.h"
|
||||
#include "model_manager.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include <QMessageBox>
|
||||
|
||||
DirectoriesManagementWidget::DirectoriesManagementWidget(QWidget *parent)
|
||||
: QWidget(parent), selectedGenreId_(-1), selectedHallId_(-1),
|
||||
selectedMovieId_(-1) {
|
||||
setupUi(this);
|
||||
|
||||
initializeRepositories(); // First of second
|
||||
initializeModels(); // First of second
|
||||
setupTableViews(); // After initialization of models
|
||||
setupGenreFinder();
|
||||
setupConnections();
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
genresRepository_ = repositoryManager.getRepositoryAs<GenresRepository>(
|
||||
metadata.repositoryTableName("genres"));
|
||||
|
||||
hallsRepository_ = repositoryManager.getRepositoryAs<HallsRepository>(
|
||||
metadata.repositoryTableName("halls"));
|
||||
|
||||
moviesRepository_ = repositoryManager.getRepositoryAs<MoviesRepository>(
|
||||
metadata.repositoryTableName("movies"));
|
||||
|
||||
if (!bool(genresRepository_ && hallsRepository_ && moviesRepository_)) {
|
||||
qFatal()
|
||||
<< "DirectoriesManagementWidget::initializeRepositories: Failed to "
|
||||
"initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::initializeModels() {
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
genresModel_ =
|
||||
modelManager.getModelAs<GenresModel>(metadata.modelTableName("genres"));
|
||||
|
||||
hallsModel_ =
|
||||
modelManager.getModelAs<HallsModel>(metadata.modelTableName("halls"));
|
||||
|
||||
moviesModel_ =
|
||||
modelManager.getModelAs<MoviesModel>(metadata.modelTableName("movies"));
|
||||
|
||||
if (!bool(genresModel_ && hallsModel_ && moviesModel_)) {
|
||||
qFatal() << "DirectoriesManagementWidget::initializeModels: Failed to "
|
||||
"initialize models";
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::setupGenreFinder() {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
genreFinder->setDefaultRelation("genres");
|
||||
genreFinder->setLabelText(EntityFinderWidget::tr("Select genre"));
|
||||
genreFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected genre"));
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::setupTableViews() {
|
||||
tableHalls->setModel(hallsModel_);
|
||||
|
||||
tableMovies->setModel(moviesModel_);
|
||||
|
||||
tableGenres->setModel(genresModel_);
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::setupConnections() {
|
||||
connect(btnAddHall, &QPushButton::clicked, this,
|
||||
&DirectoriesManagementWidget::onPushButtonAddHallClicked);
|
||||
|
||||
connect(btnAddMovie, &QPushButton::clicked, this,
|
||||
&DirectoriesManagementWidget::onPushButtonAddMovieClicked);
|
||||
|
||||
connect(btnAddGenre, &QPushButton::clicked, this,
|
||||
&DirectoriesManagementWidget::onPushButtonAddGenreClicked);
|
||||
|
||||
connect(genreFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&DirectoriesManagementWidget::onGenreFieldRetrieved);
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::onPushButtonAddMovieClicked() {
|
||||
if (selectedGenreId_ == -1) {
|
||||
QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
QMessageBox::tr("No genre selected"));
|
||||
}
|
||||
|
||||
QString title = lineEditMovieName->text();
|
||||
int duration = spinBoxMovieDuration->value();
|
||||
|
||||
CreateMovieRequest request;
|
||||
request.setTitle(title);
|
||||
request.setDuration(duration);
|
||||
request.setGenreId(selectedGenreId_);
|
||||
|
||||
RepositoryResult result = moviesRepository_->createMovie(request);
|
||||
if (!result.success) {
|
||||
qWarning() << "Failed to create movie:" << result.errorMessage;
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::onPushButtonAddHallClicked() {
|
||||
QString name = lineEditHallName->text();
|
||||
int capacity = spinBoxNumberOfSeats->value();
|
||||
|
||||
CreateHallRequest request;
|
||||
request.setHallName(name);
|
||||
request.setCapacity(capacity);
|
||||
|
||||
RepositoryResult result = hallsRepository_->createHall(request);
|
||||
if (!result.success) {
|
||||
qWarning() << "Failed to create hall:" << result.errorMessage;
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::onGenreFieldRetrieved(
|
||||
const QSqlField &field) {
|
||||
selectedGenreId_ = field.value().toInt();
|
||||
}
|
||||
|
||||
void DirectoriesManagementWidget::onPushButtonAddGenreClicked() {
|
||||
QString name = lineEditGenreName->text();
|
||||
|
||||
CreateGenreRequest request;
|
||||
request.setGenreName(name);
|
||||
|
||||
RepositoryResult result = genresRepository_->createGenre(request);
|
||||
if (!result.success) {
|
||||
qWarning() << "Failed to create genre:" << result.errorMessage;
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef DIRECTORIES_MANAGEMENT_WIDGET_H
|
||||
#define DIRECTORIES_MANAGEMENT_WIDGET_H
|
||||
|
||||
#include "ui_directories_management_widget.h"
|
||||
#include <QWidget>
|
||||
|
||||
// Forward declaration
|
||||
class HallsModel;
|
||||
class MoviesModel;
|
||||
class GenresModel;
|
||||
class HallsRepositoryInterface;
|
||||
class MoviesRepositoryInterface;
|
||||
class GenresRepositoryInterface;
|
||||
|
||||
class DirectoriesManagementWidget : public QWidget,
|
||||
private Ui::DirectoriesManagementWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
GenresModel *genresModel_;
|
||||
GenresRepositoryInterface *genresRepository_;
|
||||
HallsModel *hallsModel_;
|
||||
HallsRepositoryInterface *hallsRepository_;
|
||||
MoviesModel *moviesModel_;
|
||||
MoviesRepositoryInterface *moviesRepository_;
|
||||
qint32 selectedGenreId_;
|
||||
qint32 selectedHallId_;
|
||||
qint32 selectedMovieId_;
|
||||
|
||||
public:
|
||||
explicit DirectoriesManagementWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void initializeModels();
|
||||
void initializeRepositories();
|
||||
void setupConnections();
|
||||
void setupGenreFinder();
|
||||
void setupTableViews();
|
||||
|
||||
private slots:
|
||||
void onGenreFieldRetrieved(const QSqlField &field);
|
||||
void onPushButtonAddGenreClicked();
|
||||
void onPushButtonAddHallClicked();
|
||||
void onPushButtonAddMovieClicked();
|
||||
};
|
||||
|
||||
#endif // DIRECTORIES_MANAGEMENT_WIDGET_H
|
||||
@@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DirectoriesManagementWidget</class>
|
||||
<widget class="QWidget" name="DirectoriesManagementWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>484</width>
|
||||
<height>410</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Directory management</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabGenres">
|
||||
<attribute name="title">
|
||||
<string>Genres</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutGenres">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableGenres">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelGenreName">
|
||||
<property name="text">
|
||||
<string>Enter the name of the genre:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditGenreName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnAddGenre">
|
||||
<property name="text">
|
||||
<string>Add genre</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabHalls">
|
||||
<attribute name="title">
|
||||
<string>Halls</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableHalls">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelHallName">
|
||||
<property name="text">
|
||||
<string>Enter hall name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditHallName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelNumberOfSeats">
|
||||
<property name="text">
|
||||
<string>Enter the number of seats in the hall:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxNumberOfSeats">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnAddHall">
|
||||
<property name="text">
|
||||
<string>Add hall</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabMovies">
|
||||
<attribute name="title">
|
||||
<string>Movies</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableMovies">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="genreFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelMovieName">
|
||||
<property name="text">
|
||||
<string>Enter the name of the movie:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditMovieName"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelMovieDuration">
|
||||
<property name="text">
|
||||
<string>Enter the duration of the movie:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxMovieDuration">
|
||||
<property name="suffix">
|
||||
<string> minute(s)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1440</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnAddMovie">
|
||||
<property name="text">
|
||||
<string>Add movie</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "entity_deleter_widget.h"
|
||||
|
||||
#include "error_dialog.h"
|
||||
#include "genres_repository_interface.h"
|
||||
#include "halls_repository_interface.h"
|
||||
#include "movies_repository_interface.h"
|
||||
#include "refunds_repository_interface.h"
|
||||
#include "repository_result.h"
|
||||
#include "sessions_repository_interface.h"
|
||||
#include "tickets_repository_interface.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QSqlTableModel>
|
||||
|
||||
EntityDeleterWidget::EntityDeleterWidget(QWidget *parent) : QWidget(parent) {
|
||||
setupUi(this);
|
||||
|
||||
setupComboBoxCurrentTable();
|
||||
setupEntityFinder();
|
||||
setupConnections();
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::setupComboBoxCurrentTable() {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
QStringList databaseTableNames = {"genres", "halls", "movies",
|
||||
"refunds", "sessions", "tickets"};
|
||||
|
||||
for (const QString currentDTN : databaseTableNames) {
|
||||
comboBoxCurrentTable->addItem(metadata.translatedTableName(currentDTN),
|
||||
currentDTN);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::setupConnections() {
|
||||
connect(comboBoxCurrentTable, &QComboBox::currentIndexChanged, this,
|
||||
&EntityDeleterWidget::onComboBoxCurrentTableCurrentIndexChanged);
|
||||
|
||||
connect(entityFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&EntityDeleterWidget::onFieldRetrieved);
|
||||
|
||||
connect(entityFinder, &EntityFinderWidget::recordRetrieved, this,
|
||||
&EntityDeleterWidget::onRecordRetrieved);
|
||||
|
||||
connect(pushButtonDeleteEntity, &QPushButton::clicked, this,
|
||||
&EntityDeleterWidget::onPushButtonDeleteEntityClicked);
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::setupEntityFinder() {
|
||||
entityFinder->setLabelText(tr("Select record of current table to delete"));
|
||||
refreshEntityFinder();
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::refreshEntityFinder() {
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
entityFinder->setDefaultRelation(databaseTableName);
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::onComboBoxCurrentTableCurrentIndexChanged(int index) {
|
||||
refreshEntityFinder();
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
qInfo() << "Current table was changed:" << "CurrentComboBoxIndex:" << index
|
||||
<< "databaseTableName:" << databaseTableName;
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::onFieldRetrieved(const QSqlField &field) {
|
||||
retrievedField_ = field;
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::onRecordRetrieved(const QSqlRecord &record) {
|
||||
// NOTE: It is possible to reduce the behavior of each of the tables if the
|
||||
// removal is in different ways with other data
|
||||
selectedRecord_ = record;
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<GenresRepositoryInterface>() {
|
||||
return currentRepository<GenresRepositoryInterface>()->deleteGenreById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<HallsRepositoryInterface>() {
|
||||
return currentRepository<HallsRepositoryInterface>()->deleteHallById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<MoviesRepositoryInterface>() {
|
||||
return currentRepository<MoviesRepositoryInterface>()->deleteMovieById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<RefundsRepositoryInterface>() {
|
||||
return currentRepository<RefundsRepositoryInterface>()->deleteRefundById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<SessionsRepositoryInterface>() {
|
||||
return currentRepository<SessionsRepositoryInterface>()->deleteSessionById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityDeleterWidget::deleteEntityById<TicketsRepositoryInterface>() {
|
||||
return currentRepository<TicketsRepositoryInterface>()->deleteTicketById(
|
||||
retrievedField_.value().toInt());
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::onPushButtonDeleteEntityClicked() {
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
RepositoryResult result;
|
||||
|
||||
if (databaseTableName == "genres") {
|
||||
|
||||
result = deleteEntityById<GenresRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Genre deleted successfully"));
|
||||
|
||||
} else if (databaseTableName == "halls") {
|
||||
|
||||
result = deleteEntityById<HallsRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Hall deleted successfully"));
|
||||
|
||||
} else if (databaseTableName == "movies") {
|
||||
|
||||
result = deleteEntityById<MoviesRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Movie deleted successfully"));
|
||||
|
||||
} else if (databaseTableName == "refunds") {
|
||||
|
||||
result = deleteEntityById<RefundsRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Refund deleted successfully"));
|
||||
|
||||
} else if (databaseTableName == "sessions") {
|
||||
|
||||
result = deleteEntityById<SessionsRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Session deleted successfully"));
|
||||
|
||||
} else if (databaseTableName == "tickets") {
|
||||
|
||||
result = deleteEntityById<TicketsRepositoryInterface>();
|
||||
showMessageDialog(result, tr("Ticket deleted successfully"));
|
||||
|
||||
} else {
|
||||
qCritical() << "EntityDeleterWidget::onPressPushButtonDeleteEntity: "
|
||||
"Unknown database table name:"
|
||||
<< databaseTableName;
|
||||
}
|
||||
|
||||
// NOTE: Application connector will reset entity finder
|
||||
|
||||
// if (result.success) {
|
||||
// entityFinder->resetToDefault();
|
||||
// }
|
||||
}
|
||||
|
||||
void EntityDeleterWidget::showMessageDialog(const RepositoryResult &result,
|
||||
const QString &successMessage) {
|
||||
if (!result.success) {
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
} else {
|
||||
QMessageBox::information(this, QMessageBox::tr("Success"), successMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef ENTITY_DELETER_WIDGET_H
|
||||
#define ENTITY_DELETER_WIDGET_H
|
||||
|
||||
#include "repository_manager.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "ui_entity_deleter_widget.h"
|
||||
|
||||
// Forward declaration
|
||||
class RepositoryResult;
|
||||
|
||||
class EntityDeleterWidget : public QWidget, private Ui::EntityDeleterWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QSqlRecord selectedRecord_;
|
||||
QSqlField retrievedField_;
|
||||
|
||||
public:
|
||||
explicit EntityDeleterWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setupComboBoxCurrentTable();
|
||||
void setupConnections();
|
||||
void setupEntityFinder();
|
||||
|
||||
private slots:
|
||||
void onComboBoxCurrentTableCurrentIndexChanged(int index);
|
||||
void onFieldRetrieved(const QSqlField &field);
|
||||
void onPushButtonDeleteEntityClicked();
|
||||
void onRecordRetrieved(const QSqlRecord &rowValues);
|
||||
|
||||
private:
|
||||
void refreshEntityFinder();
|
||||
|
||||
void showMessageDialog(const RepositoryResult &result,
|
||||
const QString &successMessage);
|
||||
|
||||
template <typename Repository> Repository *currentRepository();
|
||||
template <typename Repository> RepositoryResult deleteEntityById();
|
||||
};
|
||||
|
||||
template <typename Repository>
|
||||
Repository *EntityDeleterWidget::currentRepository() {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
return RepositoryManager::instance().getRepositoryAs<Repository>(
|
||||
metadata.repositoryTableName(databaseTableName));
|
||||
}
|
||||
|
||||
#endif // ENTITY_DELETER_WIDGET_H
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EntityDeleterWidget</class>
|
||||
<widget class="QWidget" name="EntityDeleterWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>208</width>
|
||||
<height>143</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QGroupBox#groupBox {
|
||||
border: 2px solid #C00000;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
QGroupBox#groupBox::title {
|
||||
background: transparent;
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top center;
|
||||
padding: 5px;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Database recording deletion</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCurrentTableName">
|
||||
<property name="text">
|
||||
<string>Current table:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxCurrentTable"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="entityFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonDeleteEntity">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "entity_finder_widget.h"
|
||||
|
||||
#include "base_sql_table_model.h"
|
||||
#include "model_manager.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "search_dialog.h"
|
||||
|
||||
EntityFinderWidget::EntityFinderWidget(QWidget *parent) : QWidget(parent) {
|
||||
setupUi(this);
|
||||
|
||||
setupConnections();
|
||||
setupFrameStyle();
|
||||
}
|
||||
|
||||
EntityFinderWidget::EntityFinderWidget(const QString &tableName,
|
||||
const QString &fieldName,
|
||||
QWidget *parent)
|
||||
: EntityFinderWidget(parent) {
|
||||
setModelTableName(tableName);
|
||||
setFieldName(fieldName);
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setHiddenFrame(bool hidden) {
|
||||
QLayout *frameLayout = frame->layout();
|
||||
bool isHidden = frame->frameStyle() == QFrame::NoFrame;
|
||||
|
||||
if (isHidden == hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
frame->setFrameStyle(QFrame::NoFrame);
|
||||
frameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
} else {
|
||||
frame->setFrameStyle(originalFrameStyle_);
|
||||
frameLayout->setContentsMargins(9, 9, 9, 9);
|
||||
}
|
||||
|
||||
parentWidget()->adjustSize();
|
||||
}
|
||||
|
||||
void EntityFinderWidget::refreshLineEditText(const QSqlRecord &record) {
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
|
||||
BaseSqlTableModel *model =
|
||||
modelManager.getModelAs<BaseSqlTableModel>(modelTableName_);
|
||||
|
||||
QStringList columnDataList;
|
||||
|
||||
for (int i = 0; i < record.count(); ++i) {
|
||||
QString columnName = model->headerData(i, Qt::Horizontal).toString();
|
||||
QString columnValue = record.value(i).toString();
|
||||
columnDataList.append(QString("%1: %2").arg(columnName, columnValue));
|
||||
}
|
||||
|
||||
lineEditEntityInformation->setText(columnDataList.join(" | "));
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setDefaultRelation(
|
||||
const QString &databaseRelationName) {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
setModelTableName(metadata.modelTableName(databaseRelationName));
|
||||
setFieldName(metadata.primaryIdColumnName(databaseRelationName));
|
||||
|
||||
qInfo() << "Default relation name set to:" << databaseRelationName;
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setupConnections() {
|
||||
connect(pushButtonFindEntity, &QPushButton::clicked, this,
|
||||
&EntityFinderWidget::onPushButtonFindEntityClicked);
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setupFrameStyle() {
|
||||
originalFrameStyle_ = frame->frameStyle();
|
||||
}
|
||||
|
||||
void EntityFinderWidget::resetToDefault() {
|
||||
qInfo() << "Resetting entity finder to default:"
|
||||
<< "Name of EntityFinderWidget:" << objectName();
|
||||
lineEditEntityInformation->setText("");
|
||||
|
||||
emit reseted();
|
||||
}
|
||||
|
||||
void EntityFinderWidget::onPushButtonFindEntityClicked() {
|
||||
SearchDialog searchDialog(modelTableName_, fieldName_, this);
|
||||
|
||||
// All its signals are automatically disconnected from the slots
|
||||
|
||||
connect(&searchDialog, &SearchDialog::rowSelected, this,
|
||||
&EntityFinderWidget::onRowSelected);
|
||||
|
||||
connect(&searchDialog, &SearchDialog::fieldRetrieved, this,
|
||||
&EntityFinderWidget::onFieldRetrieved);
|
||||
|
||||
connect(&searchDialog, &SearchDialog::recordRetrieved, this,
|
||||
&EntityFinderWidget::onRecordRetrieved);
|
||||
|
||||
searchDialog.exec();
|
||||
}
|
||||
|
||||
void EntityFinderWidget::onRowSelected(int rowIndex) {
|
||||
emit rowSelected(rowIndex);
|
||||
}
|
||||
|
||||
void EntityFinderWidget::onRecordRetrieved(const QSqlRecord &rowValues) {
|
||||
refreshLineEditText(rowValues);
|
||||
emit recordRetrieved(rowValues);
|
||||
}
|
||||
|
||||
void EntityFinderWidget::onFieldRetrieved(const QSqlField &field) {
|
||||
emit fieldRetrieved(field);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef ENTITY_FINDER_WIDGET_H
|
||||
#define ENTITY_FINDER_WIDGET_H
|
||||
|
||||
#include "ui_entity_finder_widget.h"
|
||||
#include <QSqlField>
|
||||
#include <QSqlRecord>
|
||||
|
||||
// Forward declaration
|
||||
class SearchDialog;
|
||||
|
||||
class EntityFinderWidget : public QWidget, private Ui::EntityFinderWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QString fieldName_;
|
||||
QString modelTableName_;
|
||||
int originalFrameStyle_;
|
||||
|
||||
public:
|
||||
EntityFinderWidget(QWidget *parent = nullptr);
|
||||
EntityFinderWidget(const QString &modelTableName, const QString &fieldName,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setupConnections();
|
||||
void setupFrameStyle();
|
||||
|
||||
public:
|
||||
void setHiddenFrame(bool hidden);
|
||||
|
||||
void refreshLineEditText(const QSqlRecord &fieldValue);
|
||||
void setDefaultRelation(const QString &databaseRelationName);
|
||||
|
||||
inline void resetLineEditText();
|
||||
inline void setLabelText(const QString &text);
|
||||
inline void setLineEditPlaceholder(const QString &placeholder);
|
||||
|
||||
inline QString fieldName() const;
|
||||
inline QString modelTableName() const;
|
||||
|
||||
signals:
|
||||
void rowSelected(int rowIndex);
|
||||
void recordRetrieved(QSqlRecord rowValues);
|
||||
void fieldRetrieved(QSqlField fieldValue);
|
||||
void reseted();
|
||||
|
||||
public slots:
|
||||
void resetToDefault();
|
||||
|
||||
private slots:
|
||||
void onPushButtonFindEntityClicked();
|
||||
void onRowSelected(int rowIndex);
|
||||
void onRecordRetrieved(const QSqlRecord &rowValues);
|
||||
void onFieldRetrieved(const QSqlField &field);
|
||||
|
||||
private:
|
||||
inline void setFieldName(const QString &fieldName);
|
||||
inline void setModelTableName(const QString &modelTableName);
|
||||
};
|
||||
|
||||
void EntityFinderWidget::resetLineEditText() {
|
||||
lineEditEntityInformation->clear();
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setModelTableName(const QString &modelTableName) {
|
||||
modelTableName_ = modelTableName;
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setFieldName(const QString &fieldName) {
|
||||
fieldName_ = fieldName;
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setLabelText(const QString &text) {
|
||||
labelEntityInformation->setText(text + ":");
|
||||
}
|
||||
|
||||
void EntityFinderWidget::setLineEditPlaceholder(const QString &placeholder) {
|
||||
lineEditEntityInformation->setPlaceholderText(placeholder);
|
||||
}
|
||||
|
||||
QString EntityFinderWidget::fieldName() const { return fieldName_; }
|
||||
|
||||
QString EntityFinderWidget::modelTableName() const { return modelTableName_; }
|
||||
|
||||
#endif // ENTITY_FINDER_WIDGET_H
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EntityFinderWidget</class>
|
||||
<widget class="QWidget" name="EntityFinderWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>160</width>
|
||||
<height>102</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelEntityInformation"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditEntityInformation">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::FocusPolicy::NoFocus</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonFindEntity">
|
||||
<property name="text">
|
||||
<string>Find...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,491 @@
|
||||
#include "entity_updater_widget.h"
|
||||
|
||||
#include "genres_repository_interface.h"
|
||||
#include "halls_repository_interface.h"
|
||||
#include "movies_repository_interface.h"
|
||||
#include "refunds_repository_interface.h"
|
||||
#include "sessions_repository_interface.h"
|
||||
#include "tickets_repository_interface.h"
|
||||
|
||||
#include "update_genre_request.h"
|
||||
#include "update_hall_request.h"
|
||||
#include "update_movie_request.h"
|
||||
#include "update_refund_request.h"
|
||||
#include "update_session_request.h"
|
||||
#include "update_ticket_request.h"
|
||||
|
||||
#include "error_dialog.h"
|
||||
#include "model_manager.h"
|
||||
|
||||
#include "base_sql_table_model.h"
|
||||
#include <QMessageBox>
|
||||
#include <qsqlrelationaltablemodel.h>
|
||||
|
||||
namespace DatabaseTableNames {
|
||||
static constexpr char Genres[] = "genres";
|
||||
static constexpr char Halls[] = "halls";
|
||||
static constexpr char Movies[] = "movies";
|
||||
static constexpr char Refunds[] = "refunds";
|
||||
static constexpr char Sessions[] = "sessions";
|
||||
static constexpr char Tickets[] = "tickets";
|
||||
} // namespace DatabaseTableNames
|
||||
|
||||
EntityUpdaterWidget::EntityUpdaterWidget(QWidget *parent)
|
||||
: QWidget(parent), primaryKey_(-1) {
|
||||
setupUi(this);
|
||||
|
||||
setupConnections();
|
||||
setupComboBoxCurrentTable(); // After setup connections
|
||||
setupParentEntityFinderWidget();
|
||||
setupChildEntityFinderWidget();
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::setupComboBoxCurrentTable() {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
QList<QString> databaseTableNames = {
|
||||
DatabaseTableNames::Genres, DatabaseTableNames::Halls,
|
||||
DatabaseTableNames::Movies, DatabaseTableNames::Refunds,
|
||||
DatabaseTableNames::Sessions, DatabaseTableNames::Tickets};
|
||||
|
||||
for (const QString &tableName : databaseTableNames) {
|
||||
comboBoxCurrentTable->addItem(metadata.translatedTableName(tableName),
|
||||
tableName);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::setupConnections() {
|
||||
connect(foreignKeyFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&EntityUpdaterWidget::onForeignFieldRetrieved);
|
||||
|
||||
connect(primaryKeyFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&EntityUpdaterWidget::onPrimaryFieldRetrieved);
|
||||
|
||||
connect(primaryKeyFinder, &EntityFinderWidget::recordRetrieved, this,
|
||||
&EntityUpdaterWidget::onPrimaryEntityRetrieved);
|
||||
|
||||
connect(comboBoxCurrentTable, &QComboBox::currentIndexChanged, this,
|
||||
&EntityUpdaterWidget::onTableSelected);
|
||||
|
||||
connect(comboBoxCurrentColumn, &QComboBox::currentIndexChanged, this,
|
||||
&EntityUpdaterWidget::onColumnSelected);
|
||||
|
||||
connect(pushButtonUpdate, &QPushButton::clicked, this,
|
||||
&EntityUpdaterWidget::onPushButtonUpdateClicked);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::setupChildEntityFinderWidget() {
|
||||
foreignKeyFinder->setLabelText(
|
||||
EntityFinderWidget::tr("Select entity for replacement"));
|
||||
foreignKeyFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected entity"));
|
||||
foreignKeyFinder->setHiddenFrame(true);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::setupParentEntityFinderWidget() {
|
||||
primaryKeyFinder->setLabelText(
|
||||
EntityFinderWidget::tr("Select entity for updating"));
|
||||
primaryKeyFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected entry"));
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::configureForeignKeyFinder(
|
||||
const QString &columnName, const QString &databaseTableName) {
|
||||
TableInfoSQLite tableInfo(databaseTableName);
|
||||
|
||||
if (tableInfo.isPrimaryKey(columnName)) {
|
||||
// NOTE: Change Primary Key is prohibited
|
||||
|
||||
// configurePrimaryKeyFinder(databaseTableName);
|
||||
return;
|
||||
}
|
||||
|
||||
QString foreignKeyDatabaseTableName = tableInfo.foreignKeyTable(columnName);
|
||||
if (foreignKeyDatabaseTableName.isEmpty()) {
|
||||
qCritical()
|
||||
<< "Could not find the database table name for the key column, which "
|
||||
"contains \"_id\":"
|
||||
<< columnName;
|
||||
return;
|
||||
}
|
||||
|
||||
configureForeignKeyFinder(foreignKeyDatabaseTableName);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::configureForeignKeyFinder(
|
||||
const QString &databaseTableName) {
|
||||
foreignKeyFinder->resetToDefault();
|
||||
foreignKeyFinder->setDefaultRelation(databaseTableName);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::configurePrimaryKeyFinder(
|
||||
const QString &databaseTableName) {
|
||||
primaryKeyFinder->resetToDefault();
|
||||
primaryKeyFinder->setDefaultRelation(databaseTableName);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onPrimaryFieldRetrieved(const QSqlField &field) {
|
||||
qInfo() << "Selected primary field: Name:" << field.name()
|
||||
<< "Value:" << field.value();
|
||||
primaryKey_ = field.value().toInt();
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onForeignFieldRetrieved(const QSqlField &field) {
|
||||
qInfo() << "Selected foreign field: Name:" << field.name()
|
||||
<< "Value:" << field.value();
|
||||
selectedForeignField_ = field;
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onPrimaryEntityRetrieved(const QSqlRecord &entity) {
|
||||
qInfo() << "Retrieved foreign entity:" << entity;
|
||||
selectedPrimaryEntity_ = entity;
|
||||
}
|
||||
|
||||
template <>
|
||||
QString EntityUpdaterWidget::actualColumnValue(const QString &columnName) {
|
||||
QString value = isColumnsEqual(columnName)
|
||||
? lineEdit->text()
|
||||
: selectedPrimaryEntity_.value(columnName).toString();
|
||||
|
||||
qInfo() << "Actual column QString value:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <>
|
||||
QDateTime EntityUpdaterWidget::actualColumnValue(const QString &columnName) {
|
||||
QDateTime value = isColumnsEqual(columnName)
|
||||
? dateTimeEdit->dateTime()
|
||||
: selectedPrimaryEntity_.value(columnName).toDateTime();
|
||||
|
||||
qInfo() << "Actual column QDateTime value:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <>
|
||||
int EntityUpdaterWidget::actualColumnValue(const QString &columnName) {
|
||||
int value = isColumnsEqual(columnName)
|
||||
? spinBox->value()
|
||||
: selectedPrimaryEntity_.value(columnName).toInt();
|
||||
|
||||
qInfo() << "Actual column int value:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <>
|
||||
double EntityUpdaterWidget::actualColumnValue(const QString &columnName) {
|
||||
double value = isColumnsEqual(columnName)
|
||||
? doubleSpinBox->value()
|
||||
: selectedPrimaryEntity_.value(columnName).toDouble();
|
||||
|
||||
qInfo() << "Actual column double value:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
qint32 EntityUpdaterWidget::actualForeignKey(const QString &columnName) {
|
||||
qint32 value = isColumnsEqual(columnName)
|
||||
? selectedForeignField_.value().toInt()
|
||||
: selectedPrimaryEntity_.value(columnName).toInt();
|
||||
|
||||
qInfo() << "Actual foreign key:" << value;
|
||||
return value;
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityUpdaterWidget::updateEntity<GenresRepositoryInterface>() {
|
||||
UpdateGenreRequest request;
|
||||
request.setGenreId(primaryKey_);
|
||||
request.setGenreName(actualColumnValue<QString>("genre_name"));
|
||||
|
||||
return currentRepository<GenresRepositoryInterface>()->updateGenre(request);
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult EntityUpdaterWidget::updateEntity<HallsRepositoryInterface>() {
|
||||
UpdateHallRequest request;
|
||||
request.setHallId(primaryKey_);
|
||||
request.setCapacity(actualColumnValue<int>("capacity"));
|
||||
request.setHallName(actualColumnValue<QString>("hall_name"));
|
||||
|
||||
return currentRepository<HallsRepositoryInterface>()->updateHall(request);
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityUpdaterWidget::updateEntity<MoviesRepositoryInterface>() {
|
||||
UpdateMovieRequest request;
|
||||
request.setMovieId(primaryKey_);
|
||||
request.setDuration(actualColumnValue<int>("duration"));
|
||||
request.setGenreId(actualForeignKey("genre_id"));
|
||||
request.setTitle(actualColumnValue<QString>("title"));
|
||||
|
||||
return currentRepository<MoviesRepositoryInterface>()->updateMovie(request);
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityUpdaterWidget::updateEntity<RefundsRepositoryInterface>() {
|
||||
UpdateRefundRequest request;
|
||||
request.setRefundId(primaryKey_);
|
||||
request.setRefundAmount(actualColumnValue<double>("refund_amount"));
|
||||
request.setRefundAt(actualColumnValue<QDateTime>("refund_at"));
|
||||
request.setTicketId(actualForeignKey("ticket_id"));
|
||||
|
||||
return currentRepository<RefundsRepositoryInterface>()->updateRefund(request);
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityUpdaterWidget::updateEntity<SessionsRepositoryInterface>() {
|
||||
UpdateSessionRequest request;
|
||||
request.setSessionId(primaryKey_);
|
||||
request.setHallId(actualForeignKey("hall_id"));
|
||||
request.setMovieId(actualForeignKey("movie_id"));
|
||||
request.setBeginAt(actualColumnValue<QDateTime>("begin_at"));
|
||||
request.setTicketPrice(actualColumnValue<double>("ticket_price"));
|
||||
|
||||
return currentRepository<SessionsRepositoryInterface>()->updateSession(
|
||||
request);
|
||||
}
|
||||
|
||||
template <>
|
||||
RepositoryResult
|
||||
EntityUpdaterWidget::updateEntity<TicketsRepositoryInterface>() {
|
||||
UpdateTicketRequest request;
|
||||
request.setTicketId(primaryKey_);
|
||||
request.setSeatNumber(actualColumnValue<int>("seat_number"));
|
||||
request.setSessionId(actualForeignKey("session_id"));
|
||||
request.setSoldAt(actualColumnValue<QDateTime>("sold_at"));
|
||||
|
||||
return currentRepository<TicketsRepositoryInterface>()->updateTicket(request);
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Genres>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<GenresRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Genre updated successfully"));
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Halls>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<HallsRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Hall updated successfully"));
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Movies>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<MoviesRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Movie updated successfully"));
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Refunds>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<RefundsRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Refund updated successfully"));
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Sessions>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<SessionsRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Session updated successfully"));
|
||||
}
|
||||
|
||||
template <>
|
||||
void EntityUpdaterWidget::processEntityUpdate<DatabaseTableNames::Tickets>(
|
||||
RepositoryResult &resultOut) {
|
||||
resultOut = updateEntity<TicketsRepositoryInterface>();
|
||||
if (!resultOut.success) {
|
||||
ErrorDialog::displayError(this, resultOut.errorMessage);
|
||||
return;
|
||||
}
|
||||
showMessageDialog(resultOut,
|
||||
EntityUpdaterWidget::tr("Ticket updated successfully"));
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onPushButtonUpdateClicked() {
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
RepositoryResult result;
|
||||
|
||||
if (databaseTableName == DatabaseTableNames::Genres) {
|
||||
processEntityUpdate<DatabaseTableNames::Genres>(result);
|
||||
|
||||
} else if (databaseTableName == DatabaseTableNames::Halls) {
|
||||
processEntityUpdate<DatabaseTableNames::Halls>(result);
|
||||
|
||||
} else if (databaseTableName == DatabaseTableNames::Movies) {
|
||||
processEntityUpdate<DatabaseTableNames::Movies>(result);
|
||||
|
||||
} else if (databaseTableName == DatabaseTableNames::Refunds) {
|
||||
processEntityUpdate<DatabaseTableNames::Refunds>(result);
|
||||
|
||||
} else if (databaseTableName == DatabaseTableNames::Sessions) {
|
||||
processEntityUpdate<DatabaseTableNames::Sessions>(result);
|
||||
|
||||
} else if (databaseTableName == DatabaseTableNames::Tickets) {
|
||||
processEntityUpdate<DatabaseTableNames::Tickets>(result);
|
||||
|
||||
} else {
|
||||
qFatal() << "EntityDeleterWidget::onPressPushButtonDeleteEntity: "
|
||||
"Unknown database table name:"
|
||||
<< databaseTableName;
|
||||
}
|
||||
|
||||
processUpdateResult(result);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onTableSelected(int tableIndex) {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->itemData(tableIndex).toString();
|
||||
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
BaseSqlTableModel *model = modelManager.getModelAs<BaseSqlTableModel>(
|
||||
metadata.modelTableName(databaseTableName));
|
||||
|
||||
comboBoxCurrentColumn->clear();
|
||||
for (const auto &columnName : model->columnNames()) {
|
||||
if (metadata.primaryIdColumnName(databaseTableName) == columnName) {
|
||||
// NOTE: Change Primary Key is prohibited
|
||||
continue;
|
||||
}
|
||||
|
||||
int headerIndex = model->columnIndex(columnName);
|
||||
QString displayName =
|
||||
model->headerData(headerIndex, Qt::Horizontal).toString();
|
||||
comboBoxCurrentColumn->addItem(displayName, columnName);
|
||||
}
|
||||
|
||||
configurePrimaryKeyFinder(databaseTableName);
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::onColumnSelected(int columnIndex) {
|
||||
Q_UNUSED(columnIndex);
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
TableInfoSQLite tableInfoSQLite(databaseTableName);
|
||||
|
||||
QString columnName = comboBoxCurrentColumn->currentData().toString();
|
||||
bool isKey = columnName.contains("_id", Qt::CaseInsensitive);
|
||||
|
||||
QWidget *page;
|
||||
switch (tableInfoSQLite.columnMetaType(columnName)) {
|
||||
case QMetaType::Int: {
|
||||
page =
|
||||
findChild<QWidget *>(isKey ? "pageChildFinderWidget" : "pageSpinBox");
|
||||
} break;
|
||||
case QMetaType::Double:
|
||||
page = findChild<QWidget *>("pageDoubleSpinBox");
|
||||
break;
|
||||
case QMetaType::QString:
|
||||
page = findChild<QWidget *>("pageLineEdit");
|
||||
break;
|
||||
case QMetaType::QDateTime:
|
||||
page = findChild<QWidget *>("pageDateTimeEdit");
|
||||
break;
|
||||
default:
|
||||
qCritical() << "Unknown column selected";
|
||||
return;
|
||||
}
|
||||
stackedWidget->setCurrentWidget(page);
|
||||
|
||||
if (isKey) {
|
||||
configureForeignKeyFinder(columnName, databaseTableName);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex EntityUpdaterWidget::findIndexByColumn(QAbstractItemModel *model,
|
||||
int column,
|
||||
const QVariant &value) {
|
||||
if (!model || column < 0 || column >= model->columnCount())
|
||||
return QModelIndex();
|
||||
|
||||
for (int row = 0; row < model->rowCount(); ++row) {
|
||||
QModelIndex index = model->index(row, column);
|
||||
if (model->data(index) == value) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
bool EntityUpdaterWidget::isColumnsEqual(const QString &other) {
|
||||
TableInfoSQLite tableInfo(comboBoxCurrentTable->currentData().toString());
|
||||
QString current = comboBoxCurrentColumn->currentData().toString();
|
||||
|
||||
bool isEqual = tableInfo.columnIndex(current) == tableInfo.columnIndex(other);
|
||||
|
||||
const static QString billet("Column \"%1\" and column \"%2\": %3");
|
||||
qInfo().noquote() << billet.arg(current).arg(other).arg(
|
||||
isEqual ? "Equal" : "Not equal");
|
||||
|
||||
return isEqual;
|
||||
};
|
||||
|
||||
void EntityUpdaterWidget::processUpdateResult(const RepositoryResult &result) {
|
||||
if (!result.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
|
||||
QSqlTableModel *model =
|
||||
modelManager.getModel(metadata.modelTableName(databaseTableName));
|
||||
|
||||
QString primaryKeyColumnName =
|
||||
metadata.primaryIdColumnName(databaseTableName);
|
||||
|
||||
int columnIndex = selectedPrimaryEntity_.indexOf(primaryKeyColumnName);
|
||||
QModelIndex rowIndex = findIndexByColumn(model, columnIndex, primaryKey_);
|
||||
selectedPrimaryEntity_ = model->record(rowIndex.row());
|
||||
|
||||
primaryKeyFinder->refreshLineEditText(selectedPrimaryEntity_);
|
||||
foreignKeyFinder->resetToDefault();
|
||||
}
|
||||
|
||||
void EntityUpdaterWidget::showMessageDialog(const RepositoryResult &result,
|
||||
const QString &successMessage) {
|
||||
if (!result.success) {
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
} else {
|
||||
QMessageBox::information(this, QMessageBox::tr("Success"), successMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef ENTITY_UPDATER_WIDGET_H
|
||||
#define ENTITY_UPDATER_WIDGET_H
|
||||
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "ui_entity_updater_widget.h"
|
||||
#include <QSqlField>
|
||||
|
||||
// Forward declaration
|
||||
class RepositoryResult;
|
||||
class TableInfo;
|
||||
|
||||
class EntityUpdaterWidget : public QWidget, public Ui::EntityUpdaterWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QSqlField selectedForeignField_;
|
||||
QSqlRecord selectedPrimaryEntity_;
|
||||
qint32 primaryKey_;
|
||||
|
||||
public:
|
||||
EntityUpdaterWidget(QWidget *parent = nullptr);
|
||||
|
||||
void setupChildEntityFinderWidget();
|
||||
void setupComboBoxCurrentTable();
|
||||
void setupConnections();
|
||||
void setupLabels();
|
||||
void setupParentEntityFinderWidget();
|
||||
|
||||
void configureForeignKeyFinder(const QString &columnName,
|
||||
const QString &databaseTableName);
|
||||
void configureForeignKeyFinder(const QString &databaseTableName);
|
||||
void configurePrimaryKeyFinder(const QString &databaseTableName);
|
||||
|
||||
private slots:
|
||||
void onColumnSelected(int columnIndex);
|
||||
void onForeignFieldRetrieved(const QSqlField &field);
|
||||
void onPrimaryEntityRetrieved(const QSqlRecord &entity);
|
||||
void onPrimaryFieldRetrieved(const QSqlField &field);
|
||||
void onPushButtonUpdateClicked();
|
||||
void onTableSelected(int tableIndex);
|
||||
|
||||
private:
|
||||
QModelIndex findIndexByColumn(QAbstractItemModel *model, int column,
|
||||
const QVariant &value);
|
||||
bool isColumnsEqual(const QString &column);
|
||||
qint32 actualForeignKey(const QString &columnName);
|
||||
void processUpdateResult(const RepositoryResult &result);
|
||||
void showMessageDialog(const RepositoryResult &result,
|
||||
const QString &successMessage);
|
||||
|
||||
private:
|
||||
template <const char *DatabaseTableName>
|
||||
void processEntityUpdate(RepositoryResult &resultOut);
|
||||
|
||||
template <typename Repository> Repository *currentRepository();
|
||||
|
||||
template <typename Repository> RepositoryResult updateEntity();
|
||||
|
||||
template <typename ValueType>
|
||||
ValueType actualColumnValue(const QString &columnName);
|
||||
};
|
||||
|
||||
template <typename Repository>
|
||||
Repository *EntityUpdaterWidget::currentRepository() {
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
const QString &databaseTableName =
|
||||
comboBoxCurrentTable->currentData().toString();
|
||||
|
||||
return RepositoryManager::instance().getRepositoryAs<Repository>(
|
||||
metadata.repositoryTableName(databaseTableName));
|
||||
}
|
||||
|
||||
#endif // ENTITY_UPDATER_WIDGET_H
|
||||
@@ -0,0 +1,228 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EntityUpdaterWidget</class>
|
||||
<widget class="QWidget" name="EntityUpdaterWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>275</width>
|
||||
<height>322</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QGroupBox#groupBox {
|
||||
border: 2px solid #FF6400;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
QGroupBox#groupBox::title {
|
||||
background: transparent;
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top center;
|
||||
padding: 5px;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Database records update</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCurrentTable">
|
||||
<property name="text">
|
||||
<string>Current table:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxCurrentTable"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="primaryKeyFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelCurrentColumn">
|
||||
<property name="text">
|
||||
<string>Current column:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBoxCurrentColumn"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="pageSpinBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSpinBox">
|
||||
<property name="text">
|
||||
<string>Enter integer value:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageDoubleSpinBox">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelDoubleSpinBox">
|
||||
<property name="text">
|
||||
<string>Enter a value with a floating point:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageLineEdit">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelLineEdit">
|
||||
<property name="text">
|
||||
<string>Enter text data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageDateTimeEdit">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelDateTimeEdit">
|
||||
<property name="text">
|
||||
<string>Enter the date and time:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="dateTimeEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pageChildFinderWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="foreignKeyFinder" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonUpdate">
|
||||
<property name="text">
|
||||
<string>Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "reports_widget.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QtCharts/QBarCategoryAxis>
|
||||
#include <QtCharts/QDateTimeAxis>
|
||||
#include <QtCharts/QLineSeries>
|
||||
#include <QtCharts/QValueAxis>
|
||||
|
||||
ReportsWidget::ReportsWidget(QWidget *parent) : QWidget(parent) {
|
||||
setupUi(this);
|
||||
|
||||
setupTicketSalesReportWidgets();
|
||||
}
|
||||
|
||||
void ReportsWidget::setupTicketSalesReportWidgets() {
|
||||
genreReportWidget->configureReportForGenre();
|
||||
hallReportWidget->configureReportForHall();
|
||||
movieReportWidget->configureReportForMovie();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef REPORTS_WIDGET_H
|
||||
#define REPORTS_WIDGET_H
|
||||
|
||||
#include "ui_reports_widget.h"
|
||||
#include <QWidget>
|
||||
#include <QtCharts/QChartView>
|
||||
|
||||
// Forward declaration
|
||||
class GenresModel;
|
||||
class GenresRepositoryInterface;
|
||||
class HallsModel;
|
||||
class HallsRepositoryInterface;
|
||||
class MoviesModel;
|
||||
class MoviesRepositoryInterface;
|
||||
class SessionInfoDTO;
|
||||
class SessionsInfoRepositoryInterface;
|
||||
|
||||
class ReportsWidget : public QWidget, Ui::ReportsWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ReportsWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setupTicketSalesReportWidgets();
|
||||
};
|
||||
|
||||
#endif // REPORTS_WIDGET_H
|
||||
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ReportsWidget</class>
|
||||
<widget class="QWidget" name="ReportsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>222</width>
|
||||
<height>80</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Reports</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidgetReports">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tabGenresPopularity">
|
||||
<attribute name="title">
|
||||
<string>Popularity of genres</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutGenres">
|
||||
<item>
|
||||
<widget class="TicketSalesReportWidget" name="genreReportWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabAttendance">
|
||||
<attribute name="title">
|
||||
<string>Attendance</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutAttendance">
|
||||
<item>
|
||||
<widget class="TicketSalesReportWidget" name="hallReportWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tabMoviesPopularity">
|
||||
<attribute name="title">
|
||||
<string>The popularity of movies</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayoutMovies">
|
||||
<item>
|
||||
<widget class="TicketSalesReportWidget" name="movieReportWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TicketSalesReportWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>ticket_sales_report_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "session_management_widget.h"
|
||||
|
||||
#include "create_session_request.h"
|
||||
#include "error_dialog.h"
|
||||
#include "model_manager.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "sessions_model.h"
|
||||
#include "sessions_repository.h"
|
||||
#include <qnamespace.h>
|
||||
|
||||
SessionManagementWidget::SessionManagementWidget(QWidget *parent)
|
||||
: QWidget(parent) {
|
||||
setupUi(this);
|
||||
|
||||
initializeRepositories(); // First of second
|
||||
initializeModels(); // First of second
|
||||
setupTableView(); // After initialization of repositories
|
||||
setupHallFinder();
|
||||
setupMovieFinder();
|
||||
setupConnections();
|
||||
}
|
||||
|
||||
void SessionManagementWidget::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
sessionsRepository_ = repositoryManager.getRepositoryAs<SessionsRepository>(
|
||||
metadata.repositoryTableName("sessions"));
|
||||
|
||||
if (!sessionsRepository_) {
|
||||
qFatal() << "SessionManagementWidget::initializeRepositories: Failed to "
|
||||
"initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void SessionManagementWidget::initializeModels() {
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
sessionsModel_ = modelManager.getModelAs<SessionsModel>(
|
||||
metadata.modelTableName("sessions"));
|
||||
|
||||
if (!bool(sessionsModel_)) {
|
||||
qFatal() << "SessionManagementWidget::initializeModels: Failed to "
|
||||
"initialize models";
|
||||
}
|
||||
}
|
||||
|
||||
void SessionManagementWidget::setupTableView() {
|
||||
tableSessions->setModel(sessionsModel_);
|
||||
}
|
||||
|
||||
void SessionManagementWidget::setupConnections() {
|
||||
connect(btnAddSession, &QPushButton::clicked, this,
|
||||
&SessionManagementWidget::onPushButtonAddSessionClicked);
|
||||
|
||||
connect(hallFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&SessionManagementWidget::onHallFieldRetrieved);
|
||||
|
||||
connect(movieFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&SessionManagementWidget::onMovieFieldRetrieved);
|
||||
}
|
||||
|
||||
void SessionManagementWidget::setupHallFinder() {
|
||||
hallFinder->setDefaultRelation("halls");
|
||||
hallFinder->setLabelText(EntityFinderWidget::tr("Select hall"));
|
||||
hallFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected hall"));
|
||||
}
|
||||
|
||||
void SessionManagementWidget::setupMovieFinder() {
|
||||
movieFinder->setDefaultRelation("movies");
|
||||
movieFinder->setLabelText(EntityFinderWidget::tr("Select movie"));
|
||||
movieFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected movie"));
|
||||
}
|
||||
|
||||
void SessionManagementWidget::onPushButtonAddSessionClicked() {
|
||||
QDateTime beginAt = dateTimeEditBeginAt->dateTime();
|
||||
double ticketPrice = spinBoxTicketPrice->value();
|
||||
|
||||
CreateSessionRequest request;
|
||||
request.setBeginAt(beginAt);
|
||||
request.setHallId(selectedHallId_);
|
||||
request.setMovieId(selectedMovieId_);
|
||||
request.setTicketPrice(ticketPrice);
|
||||
|
||||
RepositoryResult result = sessionsRepository_->createSession(request);
|
||||
if (!result.success) {
|
||||
qWarning() << "Failed to create session:" << result.errorMessage;
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
void SessionManagementWidget::onHallFieldRetrieved(const QSqlField &field) {
|
||||
selectedHallId_ = field.value().toInt();
|
||||
}
|
||||
|
||||
void SessionManagementWidget::onMovieFieldRetrieved(const QSqlField &field) {
|
||||
selectedMovieId_ = field.value().toInt();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef SESSION_MANAGEMENT_WIDGET_H
|
||||
#define SESSION_MANAGEMENT_WIDGET_H
|
||||
|
||||
#include "ui_session_management_widget.h"
|
||||
#include <QWidget>
|
||||
|
||||
// Forward declaration
|
||||
class SessionsModel;
|
||||
class SessionsRepositoryInterface;
|
||||
|
||||
class SessionManagementWidget : public QWidget,
|
||||
private Ui::SessionManagementWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
SessionsModel *sessionsModel_;
|
||||
SessionsRepositoryInterface *sessionsRepository_;
|
||||
qint32 selectedHallId_;
|
||||
qint32 selectedMovieId_;
|
||||
|
||||
public:
|
||||
explicit SessionManagementWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void initializeRepositories();
|
||||
void initializeModels();
|
||||
void setupConnections();
|
||||
void setupHallFinder();
|
||||
void setupMovieFinder();
|
||||
void setupTableView();
|
||||
|
||||
private slots:
|
||||
void onPushButtonAddSessionClicked();
|
||||
void onHallFieldRetrieved(const QSqlField &field);
|
||||
void onMovieFieldRetrieved(const QSqlField &field);
|
||||
};
|
||||
|
||||
#endif // SESSION_MANAGEMENT_WIDGET_H
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SessionManagementWidget</class>
|
||||
<widget class="QWidget" name="SessionManagementWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>325</width>
|
||||
<height>388</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Session management</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableSessions">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="hallFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="movieFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelDateTimeBeginAt">
|
||||
<property name="text">
|
||||
<string>Specify the date and time of the session start:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditBeginAt"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTicketPrice">
|
||||
<property name="text">
|
||||
<string>Specify the cost of watching the session:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxTicketPrice"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnAddSession">
|
||||
<property name="text">
|
||||
<string>Add session</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "single_record_time_range_widget.h"
|
||||
|
||||
SingleRecordTimeRangeWidget::SingleRecordTimeRangeWidget(QWidget *parent)
|
||||
: QWidget(parent) {
|
||||
setupUi(this);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef SINGLE_RECORD_TIME_RANGE_WIDGET_H
|
||||
#define SINGLE_RECORD_TIME_RANGE_WIDGET_H
|
||||
|
||||
#include "ui_single_record_time_range_widget.h"
|
||||
|
||||
class SingleRecordTimeRangeWidget : public QWidget,
|
||||
private Ui::SingleRecordTimeRangeWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SingleRecordTimeRangeWidget(QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
#endif // SINGLE_RECORD_TIME_RANGE_WIDGET_H
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SingleRecordTimeRangeWidget</class>
|
||||
<widget class="QWidget" name="SingleRecordTimeRangeWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>316</width>
|
||||
<height>85</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Reports</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="EntityFinderWidget" name="entityFinder" native="true"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelStartOfPeriodAttendance">
|
||||
<property name="text">
|
||||
<string>Start of the period:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="labelEndOfPeriodAttendance">
|
||||
<property name="text">
|
||||
<string>End of the period:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditStartOfPeriodAttendance"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditEndOfPeriodAttendance"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "ticket_return_widget.h"
|
||||
|
||||
#include "create_refund_request.h"
|
||||
#include "error_dialog.h"
|
||||
#include "model_manager.h"
|
||||
#include "refunds_model.h"
|
||||
#include "refunds_repository.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include <QMessageBox>
|
||||
|
||||
TicketReturnWidget::TicketReturnWidget(QWidget *parent)
|
||||
: QWidget(parent), retrievedTicketField_(-1) {
|
||||
setupUi(this);
|
||||
|
||||
initializeRepositories();
|
||||
initializeModels();
|
||||
setupTableView(); // After initialization of models
|
||||
setupTicketFinder();
|
||||
setupConnections();
|
||||
}
|
||||
|
||||
// Setup widgets
|
||||
void TicketReturnWidget::setupTableView() {
|
||||
tableRefunds->setModel(refundsModel_);
|
||||
}
|
||||
|
||||
void TicketReturnWidget::setupTicketFinder() {
|
||||
ticketFinder->setDefaultRelation("tickets");
|
||||
ticketFinder->setLabelText(EntityFinderWidget::tr("Select ticket"));
|
||||
ticketFinder->setLineEditPlaceholder(
|
||||
EntityFinderWidget::tr("Here will be information about selected ticket"));
|
||||
}
|
||||
|
||||
void TicketReturnWidget::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
refundsRepository_ = repositoryManager.getRepositoryAs<RefundsRepository>(
|
||||
metadata.repositoryTableName("refunds"));
|
||||
|
||||
if (!bool(refundsRepository_)) {
|
||||
qFatal() << "TicketReturnWidget::initializeRepositories: Failed to "
|
||||
"initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketReturnWidget::initializeModels() {
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
refundsModel_ =
|
||||
modelManager.getModelAs<RefundsModel>(metadata.modelTableName("refunds"));
|
||||
|
||||
if (!bool(refundsModel_)) {
|
||||
qFatal() << "TicketReturnWidget::initializeModels: Failed to initialize "
|
||||
"models";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketReturnWidget::setupConnections() {
|
||||
connect(pushButtonRefundTicket, &QPushButton::clicked, this,
|
||||
&TicketReturnWidget::onPushButtonRefundTicketClicked);
|
||||
|
||||
connect(ticketFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&TicketReturnWidget::onTicketFieldRetrieved);
|
||||
}
|
||||
|
||||
void TicketReturnWidget::onPushButtonRefundTicketClicked() {
|
||||
if (retrievedTicketField_ == -1) {
|
||||
QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
QMessageBox::tr("No ticket selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime refundAt;
|
||||
if (dateTimeEditRefund->isEnabled()) {
|
||||
refundAt = dateTimeEditRefund->dateTime();
|
||||
} else {
|
||||
refundAt = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
double refundAmount = spinBoxRefundAmount->value();
|
||||
|
||||
CreateRefundRequest request;
|
||||
request.setTicketId(retrievedTicketField_);
|
||||
request.setRefundAt(refundAt);
|
||||
request.setRefundAmount(refundAmount);
|
||||
|
||||
RepositoryResult refundResult = refundsRepository_->createRefund(request);
|
||||
|
||||
if (!refundResult.success) {
|
||||
qInfo() << "Failed to refund ticket by id:" << retrievedTicketField_;
|
||||
ErrorDialog::displayError(this, refundResult.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
QString successMessage =
|
||||
QMessageBox::tr("Ticket with number %1 has been successfully refunded.")
|
||||
.arg(retrievedTicketField_);
|
||||
QMessageBox::information(this, QMessageBox::tr("Success"), successMessage);
|
||||
|
||||
ticketFinder->resetLineEditText();
|
||||
}
|
||||
|
||||
void TicketReturnWidget::onTicketFieldRetrieved(const QSqlField &field) {
|
||||
retrievedTicketField_ = field.value().toInt();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef TICKET_RETURN_WIDGET_H
|
||||
#define TICKET_RETURN_WIDGET_H
|
||||
|
||||
#include "ui_ticket_return_widget.h"
|
||||
#include <QWidget>
|
||||
|
||||
// Forward declaration
|
||||
class RefundsModel;
|
||||
class RefundsRepositoryInterface;
|
||||
|
||||
class TicketReturnWidget : public QWidget, Ui::TicketReturnWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
RefundsModel *refundsModel_;
|
||||
RefundsRepositoryInterface *refundsRepository_;
|
||||
qint32 retrievedTicketField_;
|
||||
|
||||
public:
|
||||
explicit TicketReturnWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void initializeRepositories();
|
||||
void initializeModels();
|
||||
void setupConnections();
|
||||
void setupTableView();
|
||||
void setupTicketFinder();
|
||||
|
||||
private slots:
|
||||
void onPushButtonRefundTicketClicked();
|
||||
void onTicketFieldRetrieved(const QSqlField &field);
|
||||
};
|
||||
|
||||
#endif // TICKET_RETURN_WIDGET_H
|
||||
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TicketReturnWidget</class>
|
||||
<widget class="QWidget" name="TicketReturnWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>311</width>
|
||||
<height>375</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Ticket refund</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableRefunds">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="ticketFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelRefundAmount">
|
||||
<property name="text">
|
||||
<string>Enter the refund amount:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxRefundAmount"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelDateTimeEditRefund">
|
||||
<property name="text">
|
||||
<string>Date and time of ticket refund (debugging):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditRefund">
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonRefundTicket">
|
||||
<property name="text">
|
||||
<string>Refund</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "ticket_sale_widget.h"
|
||||
|
||||
#include "create_ticket_request.h"
|
||||
#include "error_dialog.h"
|
||||
#include "model_manager.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "tickets_model.h"
|
||||
#include "tickets_repository.h"
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QSqlError>
|
||||
|
||||
TicketSaleWidget::TicketSaleWidget(QWidget *parent)
|
||||
: QWidget(parent), selectedSessionId_(-1) {
|
||||
setupUi(this);
|
||||
|
||||
initializeRepositories(); // First or second
|
||||
initializeModels(); // First ir second
|
||||
setupTableView(); // After initialization of models
|
||||
setupConnections();
|
||||
setupSessionFinder();
|
||||
}
|
||||
|
||||
void TicketSaleWidget::setupTableView() {
|
||||
tableTickets->setModel(ticketsModel_);
|
||||
}
|
||||
|
||||
void TicketSaleWidget::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
ticketsRepository_ = repositoryManager.getRepositoryAs<TicketsRepository>(
|
||||
metadata.repositoryTableName("tickets"));
|
||||
|
||||
if (!ticketsRepository_) {
|
||||
qFatal() << "TicketSaleWidget::initializeRepositories: Failed to "
|
||||
"initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketSaleWidget::initializeModels() {
|
||||
ModelManager &modelManager = ModelManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
ticketsModel_ =
|
||||
modelManager.getModelAs<TicketsModel>(metadata.modelTableName("tickets"));
|
||||
|
||||
if (!bool(ticketsModel_)) {
|
||||
qFatal() << "TicketSaleWidget::initializeModels: Failed to initialize "
|
||||
"models";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketSaleWidget::setupConnections() {
|
||||
connect(btnSellTicket, &QPushButton::clicked, this,
|
||||
&TicketSaleWidget::onPushButtonSellTicketClicked);
|
||||
|
||||
connect(sessionFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&TicketSaleWidget::onFieldRetrieved);
|
||||
}
|
||||
|
||||
void TicketSaleWidget::setupSessionFinder() {
|
||||
sessionFinder->setDefaultRelation("sessions");
|
||||
sessionFinder->setLabelText(EntityFinderWidget::tr("Select session"));
|
||||
sessionFinder->setLineEditPlaceholder(EntityFinderWidget::tr(
|
||||
"Here will be information about selected session"));
|
||||
}
|
||||
|
||||
void TicketSaleWidget::onPushButtonSellTicketClicked() {
|
||||
if (selectedSessionId_ == -1) {
|
||||
QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
QMessageBox::tr("No session selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
int seatNumber = spinBoxSeatNumber->value();
|
||||
QDateTime soldAt = dateTimeEditSell->isEnabled()
|
||||
? dateTimeEditSell->dateTime()
|
||||
: QDateTime::currentDateTime();
|
||||
|
||||
CreateTicketRequest request;
|
||||
request.setSeatNumber(seatNumber);
|
||||
request.setSessionId(selectedSessionId_);
|
||||
request.setSoldAt(soldAt);
|
||||
|
||||
RepositoryResult result = ticketsRepository_->createTicket(request);
|
||||
|
||||
if (!result.success) {
|
||||
qInfo() << "Failed to create ticket:" << result.errorMessage;
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TicketSaleWidget::onFieldRetrieved(const QSqlField &field) {
|
||||
selectedSessionId_ = field.value().toInt();
|
||||
}
|
||||
|
||||
// Check if all values in initializer list are valid
|
||||
void TicketSaleWidget::validateSessionValues(
|
||||
const std::initializer_list<QVariant> &valuesList) {
|
||||
for (const auto &value : valuesList) {
|
||||
if (!value.isValid()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef TICKET_SALE_WIDGET_H
|
||||
#define TICKET_SALE_WIDGET_H
|
||||
|
||||
#include "ui_ticket_sale_widget.h"
|
||||
#include <QSqlTableModel>
|
||||
#include <QWidget>
|
||||
|
||||
// Forward declaration
|
||||
class TicketsModel;
|
||||
class TicketsRepositoryInterface;
|
||||
|
||||
class TicketSaleWidget : public QWidget, Ui::TicketSaleWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
TicketsModel *ticketsModel_;
|
||||
TicketsRepositoryInterface *ticketsRepository_;
|
||||
qint32 selectedSessionId_;
|
||||
|
||||
public:
|
||||
explicit TicketSaleWidget(QWidget *parent = nullptr);
|
||||
|
||||
private:
|
||||
void initializeRepositories();
|
||||
void initializeModels();
|
||||
void setupConnections();
|
||||
void setupSessionFinder();
|
||||
void setupTableView();
|
||||
|
||||
void validateSessionValues(const std::initializer_list<QVariant> &valuesList);
|
||||
|
||||
private slots:
|
||||
void onPushButtonSellTicketClicked();
|
||||
void onFieldRetrieved(const QSqlField &field);
|
||||
};
|
||||
|
||||
#endif // TICKET_SALE_WIDGET_H
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TicketSaleWidget</class>
|
||||
<widget class="QWidget" name="TicketSaleWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>292</width>
|
||||
<height>375</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Ticket sales</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableTickets">
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="EntityFinderWidget" name="sessionFinder" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSeatNumber">
|
||||
<property name="text">
|
||||
<string>Seat number:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>spinBoxSeatNumber</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxSeatNumber">
|
||||
<property name="prefix">
|
||||
<string>Seat number №</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelDateTimeEditSell">
|
||||
<property name="text">
|
||||
<string>Date and time of ticket sale (debugging):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditSell">
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnSellTicket">
|
||||
<property name="text">
|
||||
<string>Sell this ticket</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,362 @@
|
||||
#include "ticket_sales_report_widget.h"
|
||||
|
||||
#include "error_dialog.h"
|
||||
#include "repository_manager.h"
|
||||
#include "repository_result.h"
|
||||
#include "schema_metadata.h"
|
||||
#include "session_statistics_dto.h"
|
||||
#include "sessions_statistics_repository.h"
|
||||
#include "table_info_sqlite.h"
|
||||
#include <QDateTimeAxis>
|
||||
#include <QMessageBox>
|
||||
#include <QScatterSeries>
|
||||
#include <QValueAxis>
|
||||
|
||||
TicketSalesReportWidget::SalesData::SalesData()
|
||||
: refundExpenses_(0), refundedTickets_(0), salesRevenue_(0),
|
||||
soldTickets_(0), maximumTickets_(0) {}
|
||||
|
||||
TicketSalesReportWidget::SalesData::SalesData(double refundExpenses,
|
||||
double salesRevenue,
|
||||
qint32 refundedTickets,
|
||||
qint32 soldTickets,
|
||||
qint32 maxTickets)
|
||||
: refundExpenses_(refundExpenses), refundedTickets_(refundedTickets),
|
||||
salesRevenue_(salesRevenue), soldTickets_(soldTickets),
|
||||
maximumTickets_(maxTickets) {}
|
||||
|
||||
TicketSalesReportWidget::SalesData
|
||||
TicketSalesReportWidget::SalesData::operator+(const SalesData &other) const {
|
||||
SalesData result;
|
||||
result.setMaximumTickets(maximumTickets() + other.maximumTickets());
|
||||
result.setSoldTickets(soldTickets() + other.soldTickets());
|
||||
result.setRefundedTickets(refundedTickets() + other.refundedTickets());
|
||||
result.setSalesRevenue(salesRevenue() + other.salesRevenue());
|
||||
result.setRefundExpenses(refundExpenses() + other.refundExpenses());
|
||||
return result;
|
||||
}
|
||||
|
||||
TicketSalesReportWidget::SalesData &
|
||||
TicketSalesReportWidget::SalesData::operator+=(const SalesData &other) {
|
||||
*this = std::move(*this + other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
TicketSalesReportWidget::TicketSalesReportWidget(QWidget *parent)
|
||||
: QWidget(parent) {
|
||||
setupUi(this);
|
||||
|
||||
initializeRepositories();
|
||||
initializeChart();
|
||||
setupChart(); // After initialize chart
|
||||
setupConnections();
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::configureReportForGenre() {
|
||||
TableInfoSQLite tableInfo("genres");
|
||||
columnInfoIndex_ = tableInfo.columnIndex("genre_name");
|
||||
|
||||
configureEntityFinder(
|
||||
"genres", EntityFinderWidget::tr("Select genre"),
|
||||
EntityFinderWidget::tr("Here will be information about selected genre"));
|
||||
|
||||
sessionsStatisticsMethod_ =
|
||||
std::bind(&SessionsStatisticsRepository::getSessionsForGenre,
|
||||
sessionsStatisticsRepository,
|
||||
std::placeholders::_1, // entityId
|
||||
std::placeholders::_2, // startDate
|
||||
std::placeholders::_3, // endDate
|
||||
std::placeholders::_4 // sessions
|
||||
);
|
||||
|
||||
billetChartTitle_ = tr("Popularity of Genre: Name: \"%1\"");
|
||||
seriesName_ = tr("Tickets Sold");
|
||||
yAxisTitle_ = tr("Number of Tickets Sold");
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::configureReportForHall() {
|
||||
TableInfoSQLite tableInfo("halls");
|
||||
columnInfoIndex_ = tableInfo.columnIndex("hall_name");
|
||||
|
||||
configureEntityFinder(
|
||||
"halls", EntityFinderWidget::tr("Select hall"),
|
||||
EntityFinderWidget::tr("Here will be information about selected hall"));
|
||||
|
||||
sessionsStatisticsMethod_ =
|
||||
std::bind(&SessionsStatisticsRepository::getSessionsForHall,
|
||||
sessionsStatisticsRepository,
|
||||
std::placeholders::_1, // entityId
|
||||
std::placeholders::_2, // startDate
|
||||
std::placeholders::_3, // endDate
|
||||
std::placeholders::_4 // sessions
|
||||
);
|
||||
|
||||
billetChartTitle_ = tr("Attendance for Hall: Name: \"%1\"");
|
||||
seriesName_ = tr("Visitors");
|
||||
yAxisTitle_ = tr("Number of Visitors");
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::configureReportForMovie() {
|
||||
TableInfoSQLite tableInfo("movies");
|
||||
columnInfoIndex_ = tableInfo.columnIndex("title");
|
||||
|
||||
configureEntityFinder(
|
||||
"movies", EntityFinderWidget::tr("Select movie"),
|
||||
EntityFinderWidget::tr("Here will be information about selected movie"));
|
||||
|
||||
sessionsStatisticsMethod_ =
|
||||
std::bind(&SessionsStatisticsRepository::getSessionsForMovie,
|
||||
sessionsStatisticsRepository,
|
||||
std::placeholders::_1, // entityId
|
||||
std::placeholders::_2, // startDate
|
||||
std::placeholders::_3, // endDate
|
||||
std::placeholders::_4 // sessions
|
||||
);
|
||||
|
||||
billetChartTitle_ = tr("Tickets Sold for Movie: Title: \"%1\"");
|
||||
seriesName_ = tr("Tickets Sold");
|
||||
yAxisTitle_ = tr("Number of Tickets Sold");
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::initializeRepositories() {
|
||||
RepositoryManager &repositoryManager = RepositoryManager::instance();
|
||||
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
|
||||
|
||||
sessionsStatisticsRepository =
|
||||
repositoryManager.getRepositoryAs<SessionsStatisticsRepository>(
|
||||
metadata.repositoryTableName("sessions_statistics"));
|
||||
|
||||
if (!bool(sessionsStatisticsRepository)) {
|
||||
qFatal() << "TicketSalesReportWidget::initializeRepositories: Failed to "
|
||||
"initialize repositories";
|
||||
}
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::initializeChart() {
|
||||
chartView_ = new QChartView(this);
|
||||
chartView_->setRenderHint(QPainter::Antialiasing);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::setupChart() {
|
||||
frameChart->layout()->addWidget(chartView_);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::setupConnections() {
|
||||
connect(btnGenerate, &QPushButton::clicked, this,
|
||||
&TicketSalesReportWidget::onPushButtonGenerateClicked);
|
||||
|
||||
connect(entityFinder, &EntityFinderWidget::fieldRetrieved, this,
|
||||
&TicketSalesReportWidget::onFieldRetrieved);
|
||||
|
||||
connect(entityFinder, &EntityFinderWidget::reseted, this,
|
||||
&TicketSalesReportWidget::onEntityFinderReseted);
|
||||
|
||||
connect(entityFinder, &EntityFinderWidget::recordRetrieved, this,
|
||||
&TicketSalesReportWidget::onRecordRetrieved);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::configureEntityFinder(
|
||||
const QString &databaseTableName, const QString &labelText,
|
||||
const QString &placeholder) {
|
||||
entityFinder->setDefaultRelation(databaseTableName);
|
||||
entityFinder->setLabelText(labelText);
|
||||
entityFinder->setLineEditPlaceholder(placeholder);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::onEntityFinderReseted() {
|
||||
selectedEntityId_ = -1;
|
||||
chartView_->setChart(new QChart());
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::onPushButtonGenerateClicked() {
|
||||
if (!dateRange(startRange_, endRange_)) {
|
||||
QMessageBox::QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
QMessageBox::tr("Invalid date range"));
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<SessionStatisticsDTO> sessions;
|
||||
RepositoryResult result = retrieveSessions(sessions);
|
||||
if (!result.success) {
|
||||
ErrorDialog::displayError(this, result.errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
QMessageBox::tr("No sessions found between %1 and %2.")
|
||||
.arg(startRange_.toString("dd.MM.yyyy"))
|
||||
.arg(endRange_.toString("dd.MM.yyyy")));
|
||||
return;
|
||||
}
|
||||
|
||||
createChart(aggregateByDateTime(sessions));
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::onFieldRetrieved(const QSqlField &field) {
|
||||
selectedEntityId_ = field.value().toInt();
|
||||
chartView_->setChart(new QChart());
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::onRecordRetrieved(const QSqlRecord &rowValues) {
|
||||
QString columnInfoValue = rowValues.value(columnInfoIndex_).toString();
|
||||
chartTitle_ = billetChartTitle_.arg(columnInfoValue);
|
||||
}
|
||||
|
||||
QMap<QDateTime, TicketSalesReportWidget::SalesData>
|
||||
TicketSalesReportWidget::aggregateByDateTime(
|
||||
const QVector<SessionStatisticsDTO> &sessions,
|
||||
std::function<bool(const SessionStatisticsDTO &)> filter) {
|
||||
|
||||
QMap<QDateTime, SalesData> accamulatedSalesData;
|
||||
|
||||
for (const SessionStatisticsDTO &session : sessions) {
|
||||
if (!filter || filter(session)) {
|
||||
QDateTime dateTime = session.beginAt();
|
||||
|
||||
SalesData buffer;
|
||||
buffer.setRefundExpenses(session.refundExpenses());
|
||||
buffer.setRefundedTickets(session.refundedTickets());
|
||||
buffer.setSalesRevenue(session.salesRevenue());
|
||||
buffer.setSoldTickets(session.soldTickets());
|
||||
buffer.setMaximumTickets(session.totalSeats());
|
||||
|
||||
accamulatedSalesData[dateTime] += buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return accamulatedSalesData;
|
||||
};
|
||||
|
||||
QAbstractAxis *TicketSalesReportWidget::createDateTimeAxis(
|
||||
int tickCount, const QString &axisTitle, const QDateTime &minDate,
|
||||
const QDateTime &maxDate) const {
|
||||
QDateTimeAxis *axisX = new QDateTimeAxis();
|
||||
axisX->setTitleText(axisTitle);
|
||||
axisX->setFormat("dd.MM.yyyy");
|
||||
axisX->setTickCount(tickCount);
|
||||
axisX->setRange(minDate, maxDate);
|
||||
return axisX;
|
||||
}
|
||||
|
||||
QAbstractAxis *
|
||||
TicketSalesReportWidget::createValueAxis(int maxValue,
|
||||
const QString &axisTitle) const {
|
||||
QValueAxis *axisY = new QValueAxis();
|
||||
axisY->setTitleText(axisTitle);
|
||||
axisY->setLabelFormat("%d");
|
||||
axisY->setRange(0, maxValue);
|
||||
return axisY;
|
||||
}
|
||||
|
||||
bool TicketSalesReportWidget::validateData(
|
||||
const QMap<QDateTime, SalesData> &data) const {
|
||||
if (!data.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "No data for chart:" << chartTitle_;
|
||||
|
||||
// QMessageBox::warning(this, QMessageBox::tr("Error"),
|
||||
// QMessageBox::tr("No data for chart"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RepositoryResult TicketSalesReportWidget::retrieveSessions(
|
||||
QVector<SessionStatisticsDTO> &sessions) {
|
||||
RepositoryResult result =
|
||||
sessionsStatisticsMethod_(selectedEntityId_, std::cref(startRange_),
|
||||
std::cref(endRange_), std::ref(sessions));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TicketSalesReportWidget::dateRange(QDateTime &startOut,
|
||||
QDateTime &endOut) {
|
||||
startOut = dateTimeEditStartOfPeriod->dateTime();
|
||||
endOut = dateTimeEditEndOfPeriod->dateTime();
|
||||
return endOut > startOut;
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::createChart(
|
||||
const QMap<QDateTime, SalesData> &data) {
|
||||
if (!validateData(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime minDate, maxDate;
|
||||
int tickCount = data.size();
|
||||
if (tickCount > 10) {
|
||||
tickCount = 10;
|
||||
}
|
||||
|
||||
QChart *chart = new QChart();
|
||||
chart->setTitle(chartTitle_);
|
||||
|
||||
QAbstractSeries *maximumTicketsSeries =
|
||||
createScatterSeries(data, minDate, maxDate, &SalesData::maximumTickets,
|
||||
tr("Maximum Tickets"));
|
||||
QAbstractSeries *soldTicketsSeries = createScatterSeries(
|
||||
data, minDate, maxDate, &SalesData::soldTickets, tr("Sold Tickets"));
|
||||
QAbstractSeries *refundedTicketsSeries =
|
||||
createScatterSeries(data, minDate, maxDate, &SalesData::refundedTickets,
|
||||
tr("Refunded Tickets"));
|
||||
QAbstractSeries *salesRevenueSeries = createScatterSeries(
|
||||
data, minDate, maxDate, &SalesData::salesRevenue, tr("Sales Revenue"));
|
||||
QAbstractSeries *refundExpensesSeries =
|
||||
createScatterSeries(data, minDate, maxDate, &SalesData::refundExpenses,
|
||||
tr("Refund Expenses"));
|
||||
|
||||
chart->addSeries(maximumTicketsSeries);
|
||||
chart->addSeries(soldTicketsSeries);
|
||||
chart->addSeries(refundedTicketsSeries);
|
||||
chart->addSeries(salesRevenueSeries);
|
||||
chart->addSeries(refundExpensesSeries);
|
||||
|
||||
QAbstractAxis *axisX =
|
||||
createDateTimeAxis(tickCount, tr("Date"), minDate, maxDate);
|
||||
chart->addAxis(axisX, Qt::AlignBottom);
|
||||
|
||||
double maxMoneyValue = std::max({maxValue(data, &SalesData::refundExpenses),
|
||||
maxValue(data, &SalesData::salesRevenue)});
|
||||
QAbstractAxis *axisYMoney = createValueAxis(maxMoneyValue, tr("Money"));
|
||||
chart->addAxis(axisYMoney, Qt::AlignLeft);
|
||||
|
||||
int maxTickets = maxValue(data, &SalesData::maximumTickets);
|
||||
|
||||
QAbstractAxis *axisYTickets = createValueAxis(maxTickets, tr("Tickets"));
|
||||
chart->addAxis(axisYTickets, Qt::AlignRight);
|
||||
|
||||
qDebug() << "Maximum movey Value" << maxMoneyValue << "maxTickets"
|
||||
<< maxTickets;
|
||||
|
||||
maximumTicketsSeries->attachAxis(axisYTickets);
|
||||
refundedTicketsSeries->attachAxis(axisYTickets);
|
||||
soldTicketsSeries->attachAxis(axisYTickets);
|
||||
|
||||
refundExpensesSeries->attachAxis(axisYMoney);
|
||||
salesRevenueSeries->attachAxis(axisYMoney);
|
||||
|
||||
maximumTicketsSeries->attachAxis(axisX);
|
||||
refundExpensesSeries->attachAxis(axisX);
|
||||
refundedTicketsSeries->attachAxis(axisX);
|
||||
salesRevenueSeries->attachAxis(axisX);
|
||||
soldTicketsSeries->attachAxis(axisX);
|
||||
|
||||
updateChartView(chart);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::setupChart(QChart *chart, QAbstractSeries *series,
|
||||
QAbstractAxis *axisX,
|
||||
QAbstractAxis *axisY) const {
|
||||
chart->addSeries(series);
|
||||
chart->addAxis(axisX, Qt::AlignBottom);
|
||||
chart->addAxis(axisY, Qt::AlignLeft);
|
||||
series->attachAxis(axisX);
|
||||
series->attachAxis(axisY);
|
||||
}
|
||||
|
||||
void TicketSalesReportWidget::updateChartView(QChart *chart) const {
|
||||
chartView_->setChart(chart);
|
||||
chartView_->setRenderHint(QPainter::Antialiasing);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
#ifndef TICKET_SALES_REPORT_WIDGET_H
|
||||
#define TICKET_SALES_REPORT_WIDGET_H
|
||||
|
||||
#include "ui_ticket_sales_report_widget.h"
|
||||
|
||||
#include <QChartView>
|
||||
#include <QLineSeries>
|
||||
#include <QScatterSeries>
|
||||
#include <functional>
|
||||
|
||||
// Forward declaration
|
||||
class RepositoryResult;
|
||||
class SessionStatisticsDTO;
|
||||
class RepositoriesInterface;
|
||||
class SessionsStatisticsRepository;
|
||||
|
||||
class TicketSalesReportWidget : public QWidget,
|
||||
private Ui::TicketSalesReportWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
class SalesData {
|
||||
private:
|
||||
double refundExpenses_;
|
||||
double salesRevenue_;
|
||||
qint32 refundedTickets_;
|
||||
qint32 soldTickets_;
|
||||
qint32 maximumTickets_;
|
||||
|
||||
public:
|
||||
SalesData();
|
||||
explicit SalesData(double refundExpenses, double salesRevenue,
|
||||
qint32 refundedTickets, qint32 soldTickets,
|
||||
qint32 maximumTickets);
|
||||
|
||||
SalesData &operator+=(const SalesData &other);
|
||||
SalesData operator+(const SalesData &other) const;
|
||||
|
||||
// clang-format off
|
||||
inline double refundExpenses() const { return refundExpenses_; };
|
||||
inline double salesRevenue() const { return salesRevenue_; };
|
||||
inline qint32 refundedTickets() const { return refundedTickets_; };
|
||||
inline qint32 soldTickets() const { return soldTickets_; };
|
||||
inline qint32 maximumTickets() const { return maximumTickets_; };
|
||||
inline void setRefundExpenses(double refundExpenses) { refundExpenses_ = refundExpenses; };
|
||||
inline void setRefundedTickets(qint32 refundedTickets) { refundedTickets_ = refundedTickets; };
|
||||
inline void setSalesRevenue(double salesRevenue) { salesRevenue_ = salesRevenue; };
|
||||
inline void setSoldTickets(qint32 soldTickets) { soldTickets_ = soldTickets; };
|
||||
inline void setMaximumTickets(qint32 maximumTickets) { maximumTickets_ = maximumTickets; };
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
private:
|
||||
using GetSessionsForEntityByIdMethod = std::function<RepositoryResult(
|
||||
qint32 entityId, std::optional<QDateTime> startDate,
|
||||
std::optional<QDateTime> endDate,
|
||||
QVector<SessionStatisticsDTO> &sessions)>;
|
||||
|
||||
private:
|
||||
GetSessionsForEntityByIdMethod sessionsStatisticsMethod_;
|
||||
QChartView *chartView_;
|
||||
QDateTime startRange_, endRange_;
|
||||
QString billetChartTitle_, chartTitle_, seriesName_, yAxisTitle_;
|
||||
SessionsStatisticsRepository *sessionsStatisticsRepository;
|
||||
int columnInfoIndex_;
|
||||
qint32 selectedEntityId_;
|
||||
|
||||
public:
|
||||
explicit TicketSalesReportWidget(QWidget *parent = nullptr);
|
||||
|
||||
void configureReportForGenre();
|
||||
void configureReportForHall();
|
||||
void configureReportForMovie();
|
||||
|
||||
private:
|
||||
void initializeChart();
|
||||
void initializeRepositories();
|
||||
void setupChart();
|
||||
void setupConnections();
|
||||
|
||||
void configureEntityFinder(const QString &databaseTableName,
|
||||
const QString &labelText,
|
||||
const QString &placeholder);
|
||||
|
||||
private slots:
|
||||
void onEntityFinderReseted();
|
||||
void onPushButtonGenerateClicked();
|
||||
void onFieldRetrieved(const QSqlField &field);
|
||||
void onRecordRetrieved(const QSqlRecord &rowValues);
|
||||
|
||||
private:
|
||||
template <typename T, typename SeriesType>
|
||||
SeriesType *
|
||||
createSeriesTemplate(const QMap<QDateTime, SalesData> &accumulatedSalesData,
|
||||
QDateTime &minDate, QDateTime &maxDate,
|
||||
T (SalesData::*getter)() const,
|
||||
const QString &seriesName);
|
||||
|
||||
template <typename T>
|
||||
QAbstractSeries *
|
||||
createLineSeries(const QMap<QDateTime, SalesData> &accumulatedSalesData,
|
||||
QDateTime &minDate, QDateTime &maxDate,
|
||||
T (SalesData::*getter)() const, const QString &seriesName);
|
||||
|
||||
template <typename T>
|
||||
QAbstractSeries *
|
||||
createScatterSeries(const QMap<QDateTime, SalesData> &accumulatedSalesData,
|
||||
QDateTime &minDate, QDateTime &maxDate,
|
||||
T (SalesData::*getter)() const,
|
||||
const QString &seriesName);
|
||||
|
||||
template <typename T>
|
||||
T maxValue(const QMap<QDateTime, SalesData> &data,
|
||||
T (SalesData::*getter)() const);
|
||||
|
||||
private:
|
||||
QMap<QDateTime, TicketSalesReportWidget::SalesData> aggregateByDateTime(
|
||||
const QVector<SessionStatisticsDTO> &sessions,
|
||||
std::function<bool(const SessionStatisticsDTO &)> filter = {});
|
||||
|
||||
QAbstractAxis *createDateTimeAxis(int tickCount, const QString &axisTitle,
|
||||
const QDateTime &minDate,
|
||||
const QDateTime &maxDate) const;
|
||||
|
||||
QAbstractAxis *createValueAxis(int maxValue, const QString &axisTitle) const;
|
||||
|
||||
RepositoryResult retrieveSessions(QVector<SessionStatisticsDTO> &sessions);
|
||||
|
||||
bool dateRange(QDateTime &startOut, QDateTime &endOut);
|
||||
|
||||
bool validateData(const QMap<QDateTime, SalesData> &data) const;
|
||||
|
||||
void createChart(const QMap<QDateTime, SalesData> &data);
|
||||
|
||||
void setupChart(QChart *chart, QAbstractSeries *series, QAbstractAxis *axisX,
|
||||
QAbstractAxis *axisY) const;
|
||||
|
||||
void updateChartView(QChart *chart) const;
|
||||
};
|
||||
|
||||
template <typename T, typename SeriesType>
|
||||
SeriesType *TicketSalesReportWidget::createSeriesTemplate(
|
||||
const QMap<QDateTime, SalesData> &accumulatedSalesData, QDateTime &minDate,
|
||||
QDateTime &maxDate, T (SalesData::*getter)() const,
|
||||
const QString &seriesName) {
|
||||
|
||||
auto *series = new SeriesType();
|
||||
series->setName(seriesName);
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (auto it = accumulatedSalesData.cbegin();
|
||||
it != accumulatedSalesData.cend(); ++it) {
|
||||
const QDateTime &dateTime = it.key();
|
||||
|
||||
series->append(dateTime.toMSecsSinceEpoch(), (it.value().*getter)());
|
||||
|
||||
if (first) {
|
||||
minDate = dateTime;
|
||||
maxDate = dateTime;
|
||||
first = false;
|
||||
} else {
|
||||
if (dateTime < minDate)
|
||||
minDate = dateTime;
|
||||
if (dateTime > maxDate)
|
||||
maxDate = dateTime;
|
||||
}
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QAbstractSeries *TicketSalesReportWidget::createScatterSeries(
|
||||
const QMap<QDateTime, SalesData> &accumulatedSalesData, QDateTime &minDate,
|
||||
QDateTime &maxDate, T (SalesData::*getter)() const,
|
||||
const QString &seriesName) {
|
||||
|
||||
return createSeriesTemplate<T, QScatterSeries>(accumulatedSalesData, minDate,
|
||||
maxDate, getter, seriesName);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QAbstractSeries *TicketSalesReportWidget::createLineSeries(
|
||||
const QMap<QDateTime, SalesData> &accumulatedSalesData, QDateTime &minDate,
|
||||
QDateTime &maxDate, T (SalesData::*getter)() const,
|
||||
const QString &seriesName) {
|
||||
|
||||
return createSeriesTemplate<T, QLineSeries>(accumulatedSalesData, minDate,
|
||||
maxDate, getter, seriesName);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T TicketSalesReportWidget::maxValue(const QMap<QDateTime, SalesData> &data,
|
||||
T (SalesData::*getter)() const) {
|
||||
if (data.isEmpty()) {
|
||||
return T{};
|
||||
}
|
||||
|
||||
T maxValue = (data.cbegin().value().*getter)();
|
||||
|
||||
for (auto it = data.cbegin(); it != data.cend(); ++it) {
|
||||
maxValue = std::max(maxValue, (it.value().*getter)());
|
||||
}
|
||||
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
#endif // TICKET_SALES_REPORT_WIDGET_H
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TicketSalesReportWidget</class>
|
||||
<widget class="QWidget" name="TicketSalesReportWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>316</width>
|
||||
<height>125</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QFrame" name="frameChart">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Shape::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="EntityFinderWidget" name="entityFinder" native="true"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="labelStartOfPeriod">
|
||||
<property name="text">
|
||||
<string>Start of the period:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="labelEndOfPeriod">
|
||||
<property name="text">
|
||||
<string>End of the period:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditStartOfPeriod"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDateTimeEdit" name="dateTimeEditEndOfPeriod"/>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="btnGenerate">
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>EntityFinderWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>entity_finder_widget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user