36#include <unordered_map>
37#include <unordered_set>
43using IdSet = std::unordered_set<std::string>;
46 std::vector<std::string> actors;
47 std::vector<std::string> properties;
48 std::vector<std::string> contracts;
49 std::vector<std::string> statements;
50 std::vector<std::string> transactions;
51 std::vector<std::string> analyses;
52 std::vector<std::string> annuals;
53 std::vector<std::string> statementDrafts;
54 std::vector<std::string> transactionDrafts;
55 std::vector<std::string> importLogs;
56 std::vector<std::string> exportLogs;
57 std::unordered_map<std::string, std::string> transactionStatementById;
61createRepositoryBundle(
const std::shared_ptr<SqliteDb>& db) {
63 bundle.
actors = std::make_shared<SqliteActorRepository>(db);
64 bundle.
properties = std::make_shared<SqlitePropertyRepository>(db);
65 bundle.
contracts = std::make_shared<SqliteContractRepository>(db);
66 bundle.
statements = std::make_shared<SqliteStatementRepository>(db);
67 bundle.
transactions = std::make_shared<SqliteTransactionRepository>(db);
68 bundle.
statementDrafts = std::make_shared<SqliteStatementDraftRepository>(db);
70 std::make_shared<SqliteTransactionDraftRepository>(db);
71 bundle.
analyses = std::make_shared<SqliteAnalysisRepository>(db);
72 bundle.
annuals = std::make_shared<SqliteAnnualRepository>(db);
73 bundle.
importLogs = std::make_shared<SqliteImportLogRepository>(db);
74 bundle.
exportLogs = std::make_shared<SqliteExportLogRepository>(db);
78template<
typename Collection>
79std::vector<std::string> collectSortedIds(
const Collection& items) {
80 std::vector<std::string> ids;
81 ids.reserve(items.size());
82 for (
const auto& item : items) {
87 if constexpr (
requires { item->id(); }) {
88 if (!item->id().empty()) {
89 ids.push_back(item->id());
91 }
else if constexpr (
requires { item->id; }) {
92 if (!item->id.empty()) {
93 ids.push_back(item->id);
98 std::sort(ids.begin(), ids.end());
99 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
103template<
typename RepoPtr,
typename LoadFn>
104std::vector<std::string> collectRepositoryIds(
const RepoPtr& repo,
110 return collectSortedIds(load(*repo));
113IdSet toIdSet(
const std::vector<std::string>& ids) {
114 return IdSet(ids.begin(), ids.end());
118 SnapshotIds snapshot;
119 snapshot.actors = collectRepositoryIds(repos.
actors, [](
const auto& repo) {
120 return repo.getActors();
122 snapshot.properties =
123 collectRepositoryIds(repos.
properties, [](
const auto& repo) {
124 return repo.getProperties();
127 collectRepositoryIds(repos.
contracts, [](
const auto& repo) {
128 return repo.getContracts();
130 snapshot.statements =
131 collectRepositoryIds(repos.
statements, [](
const auto& repo) {
132 return repo.getStatements();
134 snapshot.transactions =
135 collectRepositoryIds(repos.
transactions, [](
const auto& repo) {
136 return repo.getTransactions();
139 collectRepositoryIds(repos.
analyses, [](
const auto& repo) {
140 return repo.getAnalyses();
142 snapshot.annuals = collectRepositoryIds(repos.
annuals, [](
const auto& repo) {
143 return repo.getAnnuals();
145 snapshot.statementDrafts =
147 return repo.getStatementDrafts();
149 snapshot.transactionDrafts =
151 return repo.getTransactionDrafts();
153 snapshot.importLogs =
154 collectRepositoryIds(repos.
importLogs, [](
const auto& repo) {
155 return repo.getImportLogs();
157 snapshot.exportLogs =
158 collectRepositoryIds(repos.
exportLogs, [](
const auto& repo) {
159 return repo.getExportLogs();
163 for (
const auto& transaction : repos.
transactions->getTransactions()) {
164 if (!transaction || transaction->id().empty()) {
168 snapshot.transactionStatementById.emplace(transaction->id(),
169 transaction->statementId());
176SnapshotIds loadCurrentIds(
178 SnapshotIds snapshot;
179 const auto& catalog = document.
catalog;
180 const auto& workflow = document.
workflow;
181 snapshot.actors = collectSortedIds(catalog.actors());
182 snapshot.properties = collectSortedIds(catalog.properties());
183 snapshot.contracts = collectSortedIds(catalog.contracts());
184 snapshot.statements = collectSortedIds(catalog.statements());
185 snapshot.transactions = collectSortedIds(catalog.transactions());
186 snapshot.analyses = collectSortedIds(catalog.analyses());
187 snapshot.annuals = collectSortedIds(catalog.annuals());
188 snapshot.statementDrafts = collectSortedIds(workflow.statementDrafts);
189 snapshot.transactionDrafts = collectSortedIds(workflow.transactionDrafts);
190 snapshot.importLogs = collectSortedIds(workflow.importLogs);
191 snapshot.exportLogs = collectSortedIds(workflow.exportLogs);
193 for (
const auto& transaction : catalog.transactions()) {
194 if (!transaction || transaction->id().empty()) {
198 snapshot.transactionStatementById.emplace(transaction->id(),
199 transaction->statementId());
205template<
typename RepoPtr,
typename RemoveFn>
206void removeMissingIds(
const RepoPtr& repo,
207 const std::vector<std::string>& existingIds,
208 const IdSet& currentIds, RemoveFn&& remove,
209 std::vector<std::string>* deletedIds =
nullptr) {
214 for (
const auto&
id : existingIds) {
215 if (currentIds.contains(
id)) {
221 deletedIds->push_back(
id);
226void removeMissingTransactions(
228 const SnapshotIds& current,
229 const std::vector<std::string>& deletedStatements,
230 std::vector<std::string>& deletedTransactions) {
235 const IdSet currentTransactionIds = toIdSet(current.transactions);
236 const IdSet deletedStatementIds(deletedStatements.begin(),
237 deletedStatements.end());
239 for (
const auto& transactionId : existing.transactions) {
240 if (currentTransactionIds.contains(transactionId)) {
244 const auto statementIt =
245 existing.transactionStatementById.find(transactionId);
246 if (statementIt != existing.transactionStatementById.end() &&
247 deletedStatementIds.contains(statementIt->second)) {
252 deletedTransactions.push_back(transactionId);
256void syncStatementTransactions(
270 db,
"INSERT OR REPLACE INTO statement_transactions (statement_id, "
271 "transaction_id, position) VALUES (?, ?, ?);");
277 if (!statement || statement->id().empty()) {
281 const auto& transactionIds = statement->transactionIds();
282 for (std::size_t i = 0; i < transactionIds.size(); ++i) {
283 const auto& transactionId = transactionIds[i];
284 if (transactionId.empty()) {
289 insertStmt.bindText(1, statement->id());
290 insertStmt.bindText(2, transactionId);
291 insertStmt.bindInt(3,
static_cast<int>(i));
300 : db_(std::move(db)) {
302 throw std::runtime_error(
"db is null");
308 auto repos = createRepositoryBundle(db_);
310 return manager.
load();
317 auto repos = createRepositoryBundle(db_);
320 const auto existing = loadExistingIds(repos);
321 const auto current = loadCurrentIds(document);
324 repos.
statements, existing.statements, toIdSet(current.statements),
325 [](
auto& repo,
const std::string&
id) {
326 repo.removeStatement(id);
330 removeMissingTransactions(repos, existing, current,
335 repos.
contracts, existing.contracts, toIdSet(current.contracts),
336 [](
auto& repo,
const std::string&
id) {
337 repo.removeContract(id);
341 repos.
actors, existing.actors, toIdSet(current.actors),
342 [](
auto& repo,
const std::string&
id) {
343 repo.removeActor(id);
347 repos.
properties, existing.properties, toIdSet(current.properties),
348 [](
auto& repo,
const std::string&
id) {
349 repo.removeProperty(id);
353 repos.
analyses, existing.analyses, toIdSet(current.analyses),
354 [](
auto& repo,
const std::string&
id) {
355 repo.removeAnalysis(id);
359 repos.
annuals, existing.annuals, toIdSet(current.annuals),
360 [](
auto& repo,
const std::string&
id) {
361 repo.removeAnnual(id);
367 toIdSet(current.statementDrafts),
368 [](
auto& repo,
const std::string&
id) {
369 repo.removeStatementDraft(id);
374 toIdSet(current.transactionDrafts),
375 [](
auto& repo,
const std::string&
id) {
376 repo.removeTransactionDraft(id);
380 repos.
importLogs, existing.importLogs, toIdSet(current.importLogs),
381 [](
auto& repo,
const std::string&
id) {
382 repo.removeImportLog(id);
386 repos.
exportLogs, existing.exportLogs, toIdSet(current.exportLogs),
387 [](
auto& repo,
const std::string&
id) {
388 repo.removeExportLog(id);
393 manager.
save(document);
394 syncStatementTransactions(db_->handle(), document);
Domain model for persisted analysis definitions.
Domain model for annual aggregates.
Declares the persisted metadata record for an export run.
Declares the persisted metadata record for an import run.
Aggregates repository ports required by workspace state persistence.
Declares the SQLite-backed actor repository.
Declares the SQLite-backed analysis repository.
Declares the SQLite-backed annual repository.
Declares the SQLite-backed contract repository.
Declares the lightweight SQLite connection wrapper.
Declares the SQLite-backed export log repository.
Declares the SQLite-backed import log repository.
Declares the SQLite-backed property repository.
Declares the SQLite-backed statement draft repository.
Declares the SQLite-backed statement repository.
Declares a lightweight SQLite transaction guard.
Declares the SQLite-backed transaction draft repository.
Declares the SQLite-backed transaction repository.
Draft statement model used by import finalization and core application flow.
RAII wrapper for sqlite3_stmt* that auto-finalizes on destruction.
Draft transaction model used by import finalization and core application flow.
Internal mutable workspace session state for application services.
Loader/saver for internal workspace session state using repository abstractions.
Declares SQLite-backed loading and saving of the full application state.
void commit()
Commit the transaction.
core::application::workspace::WorkspaceSessionState load()
Loads the complete persisted application state from the database.
core::domain::DeletionImpact save(const core::application::workspace::WorkspaceSessionState &document)
Saves the complete application state and reports deletion impact details.
WorkspaceStateStore(std::shared_ptr< SqliteDb > db)
Creates a state store over an existing SQLite database connection wrapper.
Loads and saves aggregate workspace state via repository abstractions.
void save(const core::application::workspace::WorkspaceSessionState &document)
Projects, validates and persists workspace session state.
core::application::workspace::WorkspaceSessionState load()
Loads, rehydrates and validates workspace session state.
const StatementList & statements() const noexcept
Returns all statements in the catalog.
core::domain::catalog::WorkspaceCatalog catalog
WorkspaceWorkflowState workflow
Lists entity identifiers removed by cascading workspace operations.
std::vector< std::string > deletedAnnualIds
std::vector< std::string > deletedExportLogIds
std::vector< std::string > deletedStatementIds
std::vector< std::string > deletedActorIds
std::vector< std::string > deletedImportLogIds
std::vector< std::string > deletedPropertyIds
std::vector< std::string > deletedTransactionIds
std::vector< std::string > deletedStatementDraftIds
std::vector< std::string > deletedAnalysisIds
std::vector< std::string > deletedContractIds
std::vector< std::string > deletedTransactionDraftIds
Groups repository implementations consumed by WorkspaceStateManager.
std::shared_ptr< core::ports::repositories::IExportLogRepository > exportLogs
std::shared_ptr< core::ports::repositories::IStatementDraftRepository > statementDrafts
std::shared_ptr< core::ports::repositories::IPropertyRepository > properties
std::shared_ptr< core::ports::repositories::IActorRepository > actors
std::shared_ptr< core::ports::repositories::IStatementRepository > statements
std::shared_ptr< core::ports::repositories::IContractRepository > contracts
std::shared_ptr< core::ports::repositories::ITransactionDraftRepository > transactionDrafts
std::shared_ptr< core::ports::repositories::IImportLogRepository > importLogs
std::shared_ptr< core::ports::repositories::IAnalysisRepository > analyses
std::shared_ptr< core::ports::repositories::ITransactionRepository > transactions
std::shared_ptr< core::ports::repositories::IAnnualRepository > annuals