#ifndef TICKET_SALES_REPORT_WIDGET_H #define TICKET_SALES_REPORT_WIDGET_H #include "ui_ticket_sales_report_widget.h" #include #include #include #include // 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 startDate, std::optional endDate, QVector &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 SeriesType * createSeriesTemplate(const QMap &accumulatedSalesData, QDateTime &minDate, QDateTime &maxDate, T (SalesData::*getter)() const, const QString &seriesName); template QAbstractSeries * createLineSeries(const QMap &accumulatedSalesData, QDateTime &minDate, QDateTime &maxDate, T (SalesData::*getter)() const, const QString &seriesName); template QAbstractSeries * createScatterSeries(const QMap &accumulatedSalesData, QDateTime &minDate, QDateTime &maxDate, T (SalesData::*getter)() const, const QString &seriesName); template T maxValue(const QMap &data, T (SalesData::*getter)() const); private: QMap aggregateByDateTime( const QVector &sessions, std::function 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 &sessions); bool dateRange(QDateTime &startOut, QDateTime &endOut); bool validateData(const QMap &data) const; void createChart(const QMap &data); void setupChart(QChart *chart, QAbstractSeries *series, QAbstractAxis *axisX, QAbstractAxis *axisY) const; void updateChartView(QChart *chart) const; }; template SeriesType *TicketSalesReportWidget::createSeriesTemplate( const QMap &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 QAbstractSeries *TicketSalesReportWidget::createScatterSeries( const QMap &accumulatedSalesData, QDateTime &minDate, QDateTime &maxDate, T (SalesData::*getter)() const, const QString &seriesName) { return createSeriesTemplate(accumulatedSalesData, minDate, maxDate, getter, seriesName); } template QAbstractSeries *TicketSalesReportWidget::createLineSeries( const QMap &accumulatedSalesData, QDateTime &minDate, QDateTime &maxDate, T (SalesData::*getter)() const, const QString &seriesName) { return createSeriesTemplate(accumulatedSalesData, minDate, maxDate, getter, seriesName); } template T TicketSalesReportWidget::maxValue(const QMap &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