diff options
author | rtk0c <[email protected]> | 2021-05-09 01:32:56 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2021-05-09 01:36:36 -0700 |
commit | e637e165ebc5bf14e7a9b12d6b518203eb02759e (patch) | |
tree | 9443f2d6e9d543acd5312f228ba92ebef007afa7 /core/src | |
parent | 850067a6db2443c1ff1ced793135796ef7ac490f (diff) |
Fix rowid 1 vs 0 indexing offset problem (by using a custom Id column with 0 indexing), switch child-class-providing-entry-specific-impls to a if constexpr() based template approach
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/Model/TransactionsModel.cpp | 4 | ||||
-rw-r--r-- | core/src/UI/UI_DatabaseView.cpp | 705 |
2 files changed, 311 insertions, 398 deletions
diff --git a/core/src/Model/TransactionsModel.cpp b/core/src/Model/TransactionsModel.cpp index 3df7990..7d53f89 100644 --- a/core/src/Model/TransactionsModel.cpp +++ b/core/src/Model/TransactionsModel.cpp @@ -11,7 +11,7 @@ SalesTable::SalesTable(TransactionModel& db) // language=SQLite : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Sales") // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Sales WHERE Id >= ? AND Id < ?") + , GetRows(db.GetSQLite(), "SELECT * FROM Sales LIMIT ? OFFSET ?") // language=SQLite , GetItems(db.GetSQLite(), "SELECT * FROM SalesItems WHERE SaleId == ?") { @@ -21,7 +21,7 @@ PurchasesTable::PurchasesTable(TransactionModel& db) // language=SQLite : GetRowCount(db.GetSQLite(), "SELECT Count(*) FROM Purchases") // language=SQLite - , GetRows(db.GetSQLite(), "SELECT * FROM Purchases WHERE Id >= ? AND Id < ?") + , GetRows(db.GetSQLite(), "SELECT * FROM Purchases LIMIT ? OFFSET ?") // language=SQLite , GetItems(db.GetSQLite(), "SELECT * FROM PurchasesItems WHERE PurchaseId == ?") { diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp index b9d9d81..bc65080 100644 --- a/core/src/UI/UI_DatabaseView.cpp +++ b/core/src/UI/UI_DatabaseView.cpp @@ -19,17 +19,24 @@ namespace { // TODO move to Settings -constexpr int kMaxEntriesPerPage = 20; +constexpr int kMaxEntriesPerPage = 32; constexpr int kSummaryItemCount = 3; constexpr int kSummaryMaxLength = 25; +std::pair<int, int> SplitEntryIndex(int entryIdx) +{ + int page = entryIdx / kMaxEntriesPerPage; + int row = entryIdx % kMaxEntriesPerPage; + return { page, row }; +} + enum class DeliveryDirection { FactoryToWarehouse, WarehouseToCustomer, }; -struct ItemEntry +struct Item { int ItemId; int Count; @@ -37,7 +44,7 @@ struct ItemEntry struct DeliveryEntry { - std::vector<ItemEntry> Items; + std::vector<Item> Items; std::string ItemsSummary; std::string ShipmentTime; std::string ArriveTime; @@ -52,55 +59,93 @@ struct DeliveryEntry } }; +struct SaleEntry +{ + static constexpr auto kType = DeliveryDirection::WarehouseToCustomer; + + std::vector<DeliveryEntry> AssociatedDeliveries; + std::vector<Item> Items; + std::string ItemsSummary; + std::string Customer; + std::string Deadline; + std::string DeliveryTime; + int Id; + bool DeliveriesCached = false; +}; + +struct PurchaseEntry +{ + static constexpr auto kType = DeliveryDirection::FactoryToWarehouse; + + std::vector<DeliveryEntry> AssociatedDeliveries; + std::vector<Item> Items; + std::string ItemsSummary; + std::string Factory; + std::string OrderTime; + std::string DeliveryTime; + int Id; + bool DeliveriesCached; +}; + +template <class T> class GenericTableView { +public: + // clang-format off + static constexpr bool kHasItems = requires(T t) + { + t.Items; + t.ItemsSummary; + }; + static constexpr bool kHasCustomer = requires(T t) { t.Customer; }; + static constexpr bool kHasDeadline = requires(T t) { t.Deadline; }; + static constexpr bool kHasFactory = requires(T t) { t.Factory; }; + static constexpr bool kHasOrderTime = requires(T t) { t.OrderTime; }; + static constexpr bool kHasCompletionTime = requires(T t) { t.DeliveryTime; }; + static constexpr int kColumnCount = kHasItems + kHasCustomer + kHasDeadline + kHasFactory + kHasOrderTime + kHasCompletionTime; + // clang-format on + + using Page = std::vector<T>; + + struct QueryStatements + { + SQLite::Statement* GetRowCount; + SQLite::Statement* GetRows; + SQLite::Statement* GetItems; + SQLite::Statement* FilterRows; + } Statements; + protected: // Translation entries for implementer to fill out const char* mEditDialogTitle; - SQLite::Statement* mGetRowCountStatement; - SQLite::Statement* mGetRowsStatement; - SQLite::Statement* mFilterRowsStatement; - Project* mProject; + Page* mCurrentPage = nullptr; /// Current active filter object, or \c nullptr. std::unique_ptr<TableRowsFilter> mActiveFilter; - /// Inclusive. - /// \see mLastCachedRowId - int64_t mFirstCachedRowId; - /// Inclusive. - /// \see mFirstCachedRowId - int64_t mLastCachedRowId; + tsl::robin_map<int, Page> mPages; - /// A vector of row ids of entries (in \c mEntries) that are visible under the current filter. To use these indices, the elements should be mapped to - /// index of the list of entries by adding \c mFirstCachedRowId. - /// The list of entries is a cached, contiguous (row id of each entry is monotonically increasing, but not necessarily starts at 0) list - /// of ready-to-be-presented entries, held by the implementer. + /// A vector of entry indices (in \c mEntries) that are visible under the current filter. std::vector<int> mActiveEntries; /// Number of rows in the table. int mRowCount; /// Last possible page for the current set table and filter (inclusive). int mLastPage; + /// The current page the user is on. - int mCurrentPage; + int mCurrentPageNumber; - int mSelectedEntryRowId; + /// Row index of the select entry + int mSelectRow; public: - static int CalcPageForRowId(int64_t rowId) + /// Calculate the first visible row's entry index. + int GetFirstVisibleRowIdx() const { - return rowId / kMaxEntriesPerPage; - } - - /// Calculate range [begin, end) of index for the list of entries that are currently visible that the path-th page would show. - /// i.e. when there is a filter, look into \c mActiveEntryIndices; when there is no filter, use directly. - static std::pair<int64_t, int64_t> CalcRangeForPage(int page) - { - int begin = page * kMaxEntriesPerPage; - return { begin, begin + kMaxEntriesPerPage }; + return mCurrentPageNumber * kMaxEntriesPerPage; } Project* GetProject() const @@ -108,27 +153,27 @@ public: return mProject; } - virtual void OnProjectChanged(Project* newProject) + void OnProjectChanged(Project* newProject) { mProject = newProject; - if (mGetRowCountStatement->executeStep()) { - mRowCount = mGetRowCountStatement->getColumn(0).getInt(); + auto& stmt = *Statements.GetRowCount; + if (stmt.executeStep()) { + mRowCount = stmt.getColumn(0).getInt(); } else { std::cerr << "Failed to fetch row count from SQLite.\n"; mRowCount = 0; } - mFirstCachedRowId = 0; - mLastCachedRowId = 0; - - ClearEntries(); + mActiveFilter = nullptr; mActiveEntries.clear(); + mPages.clear(); + mCurrentPage = nullptr; UpdateLastPage(); SetPage(0); - mSelectedEntryRowId = -1; + mSelectRow = -1; } TableRowsFilter* GetFilter() const @@ -136,9 +181,9 @@ public: return mActiveFilter.get(); } - virtual void OnFilterChanged() + void OnFilterChanged() { - auto& stmt = *mFilterRowsStatement; + auto& stmt = *Statements.FilterRows; // clang-format off DEFER { stmt.reset(); }; // clang-format on @@ -153,7 +198,7 @@ public: UpdateLastPage(); SetPage(0); - mSelectedEntryRowId = -1; + mSelectRow = -1; } void OnFilterChanged(std::unique_ptr<TableRowsFilter> filter) @@ -162,32 +207,32 @@ public: OnFilterChanged(); } - void Draw() + void Display() { bool dummy = true; auto ls = LocaleStrings::Instance.get(); - if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPage == 0)) { - mSelectedEntryRowId = -1; - SetPage(mCurrentPage - 1); + if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPageNumber == 0)) { + SetPage(mCurrentPageNumber - 1); } ImGui::SameLine(); // +1 to convert from 0-based indices to 1-based, for human legibility - ImGui::Text("%d/%d", mCurrentPage + 1, mLastPage + 1); + ImGui::Text("%d/%d", mCurrentPageNumber + 1, mLastPage + 1); ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPage == mLastPage)) { - mSelectedEntryRowId = -1; - SetPage(mCurrentPage + 1); + if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPageNumber == mLastPage)) { + SetPage(mCurrentPageNumber + 1); } ImGui::SameLine(); - if (ImGui::Button(ls->Edit.Get(), mSelectedEntryRowId == -1)) { + if (ImGui::Button(ls->Edit.Get(), mSelectRow == -1)) { ImGui::OpenPopup(mEditDialogTitle); } if (ImGui::BeginPopupModal(mEditDialogTitle, &dummy, ImGuiWindowFlags_AlwaysAutoResize)) { - EditEntry(mSelectedEntryRowId); + auto& entry = (*mCurrentPage)[mSelectRow]; + int entryIdx = GetFirstVisibleRowIdx() + mSelectRow; + EditEntry(entry, entryIdx, mSelectRow); ImGui::EndPopup(); } @@ -197,42 +242,74 @@ public: } ImGui::SameLine(); - if (ImGui::Button(ls->Delete.Get(), mSelectedEntryRowId == -1)) { + if (ImGui::Button(ls->Delete.Get(), mSelectRow == -1)) { // TODO } ImGui::Columns(2); { - DrawMainTable(); + DisplayMainTable(); ImGui::NextColumn(); - if (mSelectedEntryRowId == -1) { + if (mSelectRow == -1) { ImGui::TextWrapped("%s", ls->SelectOrderToShowAssociatedDeliveries.Get()); } else { - DrawDeliveriesTable(); + DisplayDeliveriesTable(); } ImGui::NextColumn(); } ImGui::Columns(1); } - void DrawMainTable() + void SetPage(int page) { - if (ImGui::BeginTable("DataTable", GetTableColumnCount(), ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { - SetupTableColumns(); + mCurrentPageNumber = page; + mCurrentPage = &LoadAndGetPage(page); + mSelectRow = -1; + } + +private: + static int CalcPageForRowId(int64_t entryIdx) + { + return entryIdx / kMaxEntriesPerPage; + } + + /// Calculate range [begin, end) of index for the list of entries that are currently visible that the path-th page would show. + /// i.e. when there is a filter, look into \c mActiveEntryIndices; when there is no filter, use directly. + static std::pair<int64_t, int64_t> CalcRangeForPage(int page) + { + int begin = page * kMaxEntriesPerPage; + return { begin, begin + kMaxEntriesPerPage }; + } + + void DisplayMainTable() + { + auto ls = LocaleStrings::Instance.get(); + if (ImGui::BeginTable("DataTable", kColumnCount, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { + + if constexpr (kHasCustomer) ImGui::TableSetupColumn(ls->DatabaseCustomerColumn.Get()); + if constexpr (kHasDeadline) ImGui::TableSetupColumn(ls->DatabaseDeadlineColumn.Get()); + if constexpr (kHasFactory) ImGui::TableSetupColumn(ls->DatabaseFactoryColumn.Get()); + if constexpr (kHasOrderTime) ImGui::TableSetupColumn(ls->DatabaseOrderTimeColumn.Get()); + if constexpr (kHasCompletionTime) ImGui::TableSetupColumn(ls->DatabaseCompletionTimeColumn.Get()); + if constexpr (kHasItems) ImGui::TableSetupColumn(ls->DatabaseItemsColumn.Get()); ImGui::TableHeadersRow(); - auto [begin, end] = CalcRangeForPage(mCurrentPage); if (mActiveFilter) { + // TODO + auto [begin, end] = CalcRangeForPage(mCurrentPageNumber); end = std::min(end, (int64_t)mActiveEntries.size() - 1); for (int i = begin; i < end; ++i) { - int rowId = mActiveEntries[i]; - DisplayEntry(rowId); + int rowIdx = mActiveEntries[i]; + DisplayEntry(rowIdx); } } else { - end = std::min(end, mLastCachedRowId); - for (int rowId = begin; rowId < end; ++rowId) { - DisplayEntry(rowId); + int firstRowIdx = mCurrentPageNumber * kMaxEntriesPerPage; + auto& page = *mCurrentPage; + + int end = std::min((int)page.size(), kMaxEntriesPerPage); + for (int i = 0; i < end; ++i) { + DisplayEntry(page[i], i, firstRowIdx + i); } } @@ -240,7 +317,71 @@ public: } } - void DrawDeliveriesTable() + void DisplayEntry(int rowIdx) + { + // TODO + // auto [pageNumber, pageEntry] = SplitRowIndex(rowIdx); + // auto& entry = LoadAndGetPage(pageNumber)[pageEntry]; + // DisplayEntry(entry, rowIdx); + } + + void DisplayEntry(T& entry, int rowIdx, int entryIdx) + { + auto ls = LocaleStrings::Instance.get(); + + ImGui::PushID(rowIdx); + ImGui::TableNextRow(); + + if constexpr (kHasCustomer) { + ImGui::TableNextColumn(); + if (ImGui::Selectable(entry.Customer.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { + mSelectRow = rowIdx; + } + } + + if constexpr (kHasDeadline) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.Deadline.c_str()); + } + + if constexpr (kHasFactory) { + ImGui::TableNextColumn(); + if (ImGui::Selectable(entry.Factory.c_str(), mSelectRow == rowIdx, ImGuiSelectableFlags_SpanAllColumns)) { + mSelectRow = rowIdx; + } + } + + if constexpr (kHasOrderTime) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted(entry.OrderTime.c_str()); + } + + if constexpr (kHasCompletionTime) { + ImGui::TableNextColumn(); + if (entry.DeliveryTime.empty()) { + ImGui::TextUnformatted(ls->NotDelivered.Get()); + } else { + ImGui::TextUnformatted(entry.DeliveryTime.c_str()); + } + } + + if constexpr (kHasItems) { + ImGui::TableNextColumn(); + if (ImGui::TreeNode(entry.ItemsSummary.c_str())) { + DrawItems(entry.Items); + ImGui::TreePop(); + } + } + + ImGui::PopID(); + } + + void EditEntry(T& entry, int rowIdx, int entryIdx) + { + // TODO + } + + void DisplayDeliveriesTable() { auto ls = LocaleStrings::Instance.get(); if (ImGui::BeginTable("DeliveriesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX)) { @@ -250,7 +391,8 @@ public: ImGui::TableSetupColumn(ls->DatabaseItemsColumn.Get()); ImGui::TableHeadersRow(); - auto& deliveries = GetEntryAssociatedDeliveries(mSelectedEntryRowId); + auto& entry = (*mCurrentPage)[mSelectRow]; + auto& deliveries = entry.AssociatedDeliveries; for (auto& delivery : deliveries) { ImGui::TableNextRow(); @@ -271,35 +413,19 @@ public: } } - void SetPage(int page) - { - mCurrentPage = page; - EnsureCacheCoversPage(page); - } - - int RowIdToIndex(int64_t rowId) const - { - return rowId - mFirstCachedRowId; - } - - int64_t IndexToRowId(int index) const - { - return index + mFirstCachedRowId; - } - - std::vector<ItemEntry> LoadItems(SQLite::Statement& stmt, int64_t rowId) + std::vector<Item> LoadItems(SQLite::Statement& stmt, int64_t id) { // clang-format off DEFER { stmt.reset(); }; // clang-format on - stmt.bind(1, rowId); + stmt.bind(1, id); - std::vector<ItemEntry> entries; + std::vector<Item> entries; int itemIdCol = stmt.getColumnIndex("ItemId"); int countCol = stmt.getColumnIndex("Count"); while (stmt.executeStep()) { - entries.push_back(ItemEntry{ + entries.push_back(Item{ .ItemId = stmt.getColumn(itemIdCol).getInt(), .Count = stmt.getColumn(countCol).getInt(), }); @@ -308,7 +434,7 @@ public: return entries; } - std::string CreateItemsSummary(const std::vector<ItemEntry>& items) + std::string CreateItemsSummary(const std::vector<Item>& items) { if (items.empty()) { return "<empty>"; @@ -334,7 +460,7 @@ public: return result; } - std::vector<DeliveryEntry> LoadDeliveriesEntries(int64_t orderRowId, DeliveryDirection type) + std::vector<DeliveryEntry> LoadDeliveriesEntries(int64_t orderId, DeliveryDirection type) { bool outgoingFlag; switch (type) { @@ -347,7 +473,7 @@ public: DEFER { stmt.reset(); }; // clang-format on - stmt.bind(1, orderRowId); + stmt.bind(1, orderId); stmt.bind(2, outgoingFlag); std::vector<DeliveryEntry> entries; @@ -372,94 +498,96 @@ public: return entries; } -protected: - virtual int GetTableColumnCount() const = 0; - virtual void SetupTableColumns() = 0; + Page& LoadAndGetPage(int page) + { + auto iter = mPages.find(page); + if (iter != mPages.end()) { + return iter.value(); + } - virtual const std::vector<DeliveryEntry>& GetEntryAssociatedDeliveries(int rowId) = 0; - virtual void DisplayEntry(int rowId) = 0; - virtual void EditEntry(int rowId) = 0; + auto& stmt = *Statements.GetRows; + // clang-format off + DEFER { stmt.reset(); }; + // clang-format on - virtual void ClearEntries() = 0; + stmt.bind(1, kMaxEntriesPerPage); + stmt.bind(2, page * kMaxEntriesPerPage); - void DrawItems(const std::vector<ItemEntry>& items) - { - for (auto& item : items) { - auto& name = mProject->Products.Find(item.ItemId)->GetName(); - ImGui::Text("%s × %d", name.c_str(), item.Count); - } - } + // If a field flag is false, the column index won't be used (controlled by other if constexpr's downstream) + // so there is no UB here - void EnsureCacheCoversPage(int page) - { - auto [begin, end] = CalcRangeForPage(page); - EnsureCacheCovers(begin, end - 1); - } + // This column is always necessary (and present) because the deliveries table require it + int idCol = stmt.getColumnIndex("Id"); - void EnsureCacheCovers(int64_t firstRow, int64_t lastRow) - { - if (firstRow > lastRow) { - std::swap(firstRow, lastRow); - } + int customerCol; + if constexpr (kHasCustomer) customerCol = stmt.getColumnIndex("Customer"); - int newFirst = mFirstCachedRowId; - int newLast = mLastCachedRowId; + int deadlineCol; + if constexpr (kHasDeadline) deadlineCol = stmt.getColumnIndex("Deadline"); - bool doRebuild = false; - if (firstRow < mFirstCachedRowId) { - newFirst = (CalcPageForRowId(firstRow) + 1) * kMaxEntriesPerPage; - doRebuild = true; - } - if (lastRow > mLastCachedRowId) { - newLast = (CalcPageForRowId(lastRow) + 1) * kMaxEntriesPerPage; - doRebuild = true; - } - if (!doRebuild) return; + int factoryCol; + if constexpr (kHasFactory) factoryCol = stmt.getColumnIndex("Factory"); - EnsureCacheCoversImpl(newFirst, newLast); - } + int orderTimeCol; + if constexpr (kHasOrderTime) orderTimeCol = stmt.getColumnIndex("OrderTime"); - /// To be implemented by child classes, presumable calling LoadRange() to get the front and back new contents. - /// \param newFirst The first rowid the new cache should cover - /// \param newLast The last rowid the new cache should cover - virtual void EnsureCacheCoversImpl(int newFirst, int newLast) = 0; + int deliveryTimeCol; + if constexpr (kHasCompletionTime) deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); - template <class TEntry, class TCollector> - void LoadExtraEntries(std::vector<TEntry>& entries, int newFirst, int newLast, TCollector&& collector) - { - auto front = LoadRange<TEntry>(newFirst, mFirstCachedRowId, collector); - auto back = LoadRange<TEntry>(mLastCachedRowId + 1, newLast + 1, collector); + Page entries; + while (stmt.executeStep()) { + auto& entry = entries.emplace_back(); - mFirstCachedRowId -= front.size(); - mLastCachedRowId += back.size(); + auto id = stmt.getColumn(idCol).getInt64(); + entry.AssociatedDeliveries = LoadDeliveriesEntries(id, T::kType); - entries.insert(entries.begin(), std::make_move_iterator(front.begin()), std::make_move_iterator(front.end())); - entries.insert(entries.end(), std::make_move_iterator(back.begin()), std::make_move_iterator(back.end())); - } + if constexpr (kHasItems) { + auto items = LoadItems( + *Statements.GetItems, + id); + auto itemsSummary = CreateItemsSummary(items); + entry.Items = std::move(items); + entry.ItemsSummary = std::move(itemsSummary); + } - template <class TEntry, class TCollector> - std::vector<TEntry> LoadRange(int64_t begin, int64_t end, TCollector&& collector) - { - std::vector<TEntry> result; + if constexpr (kHasCustomer) { + auto customerId = stmt.getColumn(customerCol).getInt(); + entry.Customer = mProject->Customers.Find(customerId)->GetName(); + } - size_t size = end - begin; - if (size == 0) { - return result; - } + if constexpr (kHasDeadline) { + auto timeStamp = stmt.getColumn(deadlineCol).getInt64(); + entry.Deadline = StringifyTimeStamp(timeStamp); + } - result.reserve(size); + if constexpr (kHasFactory) { + auto factoryId = stmt.getColumn(factoryCol).getInt(); + entry.Factory = mProject->Factories.Find(factoryId)->GetName(); + } - // clang-format off - DEFER { mGetRowsStatement->reset(); }; - // clang-format on - mGetRowsStatement->bind(1, begin); - mGetRowsStatement->bind(2, end); + if constexpr (kHasOrderTime) { + auto timeStamp = stmt.getColumn(orderTimeCol).getInt64(); + entry.OrderTime = StringifyTimeStamp(timeStamp); + } - collector(result); - return result; + if constexpr (kHasCompletionTime) { + auto timeStamp = stmt.getColumn(deliveryTimeCol).getInt64(); + entry.DeliveryTime = StringifyTimeStamp(timeStamp); + } + } + + auto [res, _] = mPages.try_emplace(page, std::move(entries)); + return res.value(); + } + + void DrawItems(const std::vector<Item>& items) + { + for (auto& item : items) { + auto& name = mProject->Products.Find(item.ItemId)->GetName(); + ImGui::Text("%s × %d", name.c_str(), item.Count); + } } -private: void UpdateLastPage() { mLastPage = mActiveEntries.empty() @@ -468,23 +596,8 @@ private: } }; -struct SaleEntry +class SalesTableView : public GenericTableView<SaleEntry> { - std::vector<DeliveryEntry> AssociatedDeliveries; - std::vector<ItemEntry> Items; - std::string ItemsSummary; - std::string Customer; - std::string Deadline; - std::string DeliveryTime; - bool DeliveriesCached = false; -}; - -class SalesTableView : public GenericTableView -{ -private: - /// A cached, contiguous (row id of each entry is monotonically increasing, but not necessarily starts at 0) list ready-to-be-presented entries. May be incomplete. - std::vector<SaleEntry> mEntries; - public: SalesTableView() { @@ -492,129 +605,24 @@ public: mEditDialogTitle = ls->EditSaleEntryDialogTitle.Get(); } - virtual void OnProjectChanged(Project* newProject) override - { - auto& sales = newProject->GetTransactionsModel().GetSales(); - mGetRowCountStatement = &sales.GetRowCount; - mGetRowsStatement = &sales.GetRows; - // mFilterRowsStatement = &sales.FilterRows; - - GenericTableView::OnProjectChanged(newProject); - } - -protected: - virtual int GetTableColumnCount() const override - { - return 4; - } - - virtual void SetupTableColumns() override - { - auto ls = LocaleStrings::Instance.get(); - ImGui::TableSetupColumn(ls->DatabaseCustomerColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseDeadlineColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseCompletionTimeColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseItemsColumn.Get()); - } - - virtual const std::vector<DeliveryEntry>& GetEntryAssociatedDeliveries(int rowId) override - { - auto& entry = mEntries[RowIdToIndex(rowId)]; - if (!entry.DeliveriesCached) { - entry.AssociatedDeliveries = LoadDeliveriesEntries(rowId, DeliveryDirection::FactoryToWarehouse); - entry.DeliveriesCached = true; - } - return entry.AssociatedDeliveries; - } - - virtual void DisplayEntry(int rowId) override - { - auto& entry = mEntries[RowIdToIndex(rowId)]; - auto ls = LocaleStrings::Instance.get(); - - ImGui::PushID(rowId); - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntryRowId == rowId, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectedEntryRowId = rowId; - } - - ImGui::TableNextColumn(); - ImGui::TextUnformatted(entry.Deadline.c_str()); - - ImGui::TableNextColumn(); - if (entry.DeliveryTime.empty()) { - ImGui::TextUnformatted(ls->NotDelivered.Get()); - } else { - ImGui::TextUnformatted(entry.DeliveryTime.c_str()); - } - - ImGui::TableNextColumn(); - if (ImGui::TreeNode(entry.ItemsSummary.c_str())) { - DrawItems(entry.Items); - ImGui::TreePop(); - } - - ImGui::PopID(); - } - - virtual void EditEntry(int rowId) override +#pragma clang diagnostic push +#pragma ide diagnostic ignored "HidingNonVirtualFunction" + void OnProjectChanged(Project* newProject) { + auto& table = newProject->GetTransactionsModel().GetSales(); + Statements.GetRowCount = &table.GetRowCount; + Statements.GetRows = &table.GetRows; + Statements.GetItems = &table.GetItems; // TODO - } + // stmts.FilterRowsStatement = ; - virtual void ClearEntries() override - { - mEntries.clear(); + GenericTableView<SaleEntry>::OnProjectChanged(newProject); } - - virtual void EnsureCacheCoversImpl(int newFirst, int newLast) override - { - auto CollectRows = [&](std::vector<SaleEntry>& result) { - auto& stmt = *mGetRowsStatement; - int rowIdCol = stmt.getColumnIndex("Id"); - int customerCol = stmt.getColumnIndex("Customer"); - int deadlineCol = stmt.getColumnIndex("Deadline"); - int deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); - - while (stmt.executeStep()) { - auto customerId = stmt.getColumn(customerCol).getInt(); - auto items = LoadItems( - mProject->GetTransactionsModel().GetSales().GetItems, - stmt.getColumn(rowIdCol).getInt64()); - auto itemsSummary = CreateItemsSummary(items); - - result.push_back(SaleEntry{ - .Items = std::move(items), - .ItemsSummary = std::move(itemsSummary), - .Customer = mProject->Customers.Find(customerId)->GetName(), - .Deadline = StringifyTimeStamp(stmt.getColumn(deadlineCol).getInt64()), - .DeliveryTime = StringifyTimeStamp(stmt.getColumn(deliveryTimeCol).getInt64()), - }); - } - }; - - LoadExtraEntries<SaleEntry>(mEntries, newFirst, newLast, CollectRows); - } -}; - -struct PurchaseEntry -{ - std::vector<DeliveryEntry> AssociatedDeliveries; - std::vector<ItemEntry> Items; - std::string ItemsSummary; - std::string Factory; - std::string OrderTime; - std::string DeliveryTime; - bool DeliveriesCached; +#pragma clang diagnostic pop }; -class PurchasesTableView : public GenericTableView +class PurchasesTableView : public GenericTableView<PurchaseEntry> { -private: - std::vector<PurchaseEntry> mEntries; - public: PurchasesTableView() { @@ -622,115 +630,20 @@ public: mEditDialogTitle = ls->EditPurchaseEntryDialogTitle.Get(); } - virtual void OnProjectChanged(Project* newProject) override - { - auto& purchases = newProject->GetTransactionsModel().GetPurchases(); - mGetRowCountStatement = &purchases.GetRowCount; - mGetRowsStatement = &purchases.GetRows; - // mFilterRowsStatement = &purchases.FilterRowsStatement; - - GenericTableView::OnProjectChanged(newProject); - } - -protected: - virtual int GetTableColumnCount() const override - { - return 4; - } - - virtual void SetupTableColumns() override - { - auto ls = LocaleStrings::Instance.get(); - ImGui::TableSetupColumn(ls->DatabaseFactoryColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseOrderTimeColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseCompletionTimeColumn.Get()); - ImGui::TableSetupColumn(ls->DatabaseItemsColumn.Get()); - } - - virtual const std::vector<DeliveryEntry>& GetEntryAssociatedDeliveries(int rowId) override - { - auto& entry = mEntries[RowIdToIndex(rowId)]; - if (!entry.DeliveriesCached) { - entry.AssociatedDeliveries = LoadDeliveriesEntries(rowId, DeliveryDirection::FactoryToWarehouse); - entry.DeliveriesCached = true; - } - return entry.AssociatedDeliveries; - } - - virtual void DisplayEntry(int rowId) override - { - auto& entry = mEntries[RowIdToIndex(rowId)]; - auto ls = LocaleStrings::Instance.get(); - - ImGui::PushID(rowId); - ImGui::TableNextRow(); - - ImGui::TableNextColumn(); - if (ImGui::Selectable(entry.Factory.c_str(), mSelectedEntryRowId == rowId, ImGuiSelectableFlags_SpanAllColumns)) { - mSelectedEntryRowId = rowId; - } - - ImGui::TableNextColumn(); - if (entry.OrderTime.empty()) { - ImGui::TextUnformatted(ls->NotDelivered.Get()); - } else { - ImGui::TextUnformatted(entry.OrderTime.c_str()); - } - - ImGui::TableNextColumn(); - if (entry.DeliveryTime.empty()) { - ImGui::TextUnformatted(ls->NotDelivered.Get()); - } else { - ImGui::TextUnformatted(entry.DeliveryTime.c_str()); - } - - ImGui::TableNextColumn(); - if (ImGui::TreeNode(entry.ItemsSummary.c_str())) { - DrawItems(entry.Items); - ImGui::TreePop(); - } - - ImGui::PopID(); - } - - virtual void EditEntry(int rowId) override +#pragma clang diagnostic push +#pragma ide diagnostic ignored "HidingNonVirtualFunction" + void OnProjectChanged(Project* newProject) { + auto& table = newProject->GetTransactionsModel().GetPurchases(); + Statements.GetRowCount = &table.GetRowCount; + Statements.GetRows = &table.GetRows; + Statements.GetItems = &table.GetItems; // TODO - } - - virtual void ClearEntries() override - { - mEntries.clear(); - } - - virtual void EnsureCacheCoversImpl(int newFirst, int newLast) override - { - auto CollectRows = [&](std::vector<PurchaseEntry>& result) { - auto& stmt = *mGetRowsStatement; - int rowIdCol = stmt.getColumnIndex("Id"); - int factoryCol = stmt.getColumnIndex("Factory"); - int orderTimeCol = stmt.getColumnIndex("OrderTime"); - int deliveryTimeCol = stmt.getColumnIndex("DeliveryTime"); - - while (stmt.executeStep()) { - auto factoryId = stmt.getColumn(factoryCol).getInt(); - auto items = LoadItems( - mProject->GetTransactionsModel().GetPurchases().GetItems, - stmt.getColumn(rowIdCol).getInt64()); - auto itemsSummary = CreateItemsSummary(items); - - result.push_back(PurchaseEntry{ - .Items = std::move(items), - .ItemsSummary = std::move(itemsSummary), - .Factory = mProject->Factories.Find(factoryId)->GetName(), - .OrderTime = StringifyTimeStamp(stmt.getColumn(orderTimeCol).getInt64()), - .DeliveryTime = StringifyTimeStamp(stmt.getColumn(deliveryTimeCol).getInt64()), - }); - } - }; + // stmts.FilterRowsStatement = ; - LoadExtraEntries<PurchaseEntry>(mEntries, newFirst, newLast, CollectRows); + GenericTableView<PurchaseEntry>::OnProjectChanged(newProject); } +#pragma clang diagnostic pop }; } // namespace @@ -751,11 +664,11 @@ void UI::DatabaseViewTab() if (ImGui::BeginTabBar("##DatabaseViewTabs")) { if (ImGui::BeginTabItem(ls->SalesViewTab.Get())) { - sales.Draw(); + sales.Display(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem(ls->PurchasesViewTab.Get())) { - purchases.Draw(); + purchases.Display(); ImGui::EndTabItem(); } ImGui::EndTabBar(); |