23 const QVariantMap map = value.toMap();
25 const QString aliasValue = map.value(QStringLiteral(
"value")).toString();
26 if (!aliasValue.isEmpty()) {
29 const QString aliasSource = map.value(QStringLiteral(
"source")).toString();
30 if (!aliasSource.isEmpty()) {
33 const QString
id = map.value(QStringLiteral(
"id")).toString();
38 return value.toString();
41QVariantList normalizeStringValues(
const QVariantList& values) {
43 out.reserve(values.size());
44 for (
const auto& value : values) {
50QString rowIdAt(
const QVariantList& rows,
int index,
51 const QString& idKey = QStringLiteral(
"id")) {
52 if (index < 0 || index >= rows.size()) {
55 return rows.at(index).toMap().value(idKey).toString();
58int indexOfId(
const QVariantList& rows,
const QString&
id,
59 const QString& idKey = QStringLiteral(
"id")) {
63 for (
int i = 0; i < rows.size(); ++i) {
64 if (rows.at(i).toMap().value(idKey).toString() ==
id) {
71int indexOfKeyValue(
const QVariantList& rows,
const QString& key,
72 const QVariant& value) {
73 for (
int i = 0; i < rows.size(); ++i) {
74 if (rows.at(i).toMap().value(key) == value) {
81QVariantMap rowById(
const QVariantList& rows,
const QString&
id,
82 const QString& idKey = QStringLiteral(
"id")) {
83 const int index = indexOfId(rows,
id.trimmed(), idKey);
84 return index >= 0 ? rows.at(index).toMap() : QVariantMap{};
87QString deleteNextSelectionId(
const QVariantList& rows,
88 const QString& removedId,
int defaultIndex = 0,
89 const QString& idKey = QStringLiteral(
"id")) {
93 const int removedIndex = indexOfId(rows, removedId, idKey);
94 const int nextIndex = removedIndex >= 0 ? removedIndex + 1 : defaultIndex;
95 const int wrapped = nextIndex % rows.size();
96 return rowIdAt(rows, wrapped < 0 ? wrapped + rows.size() : wrapped, idKey);
99int wrappedIndex(
int index,
int count) {
103 const int normalized = index % count;
104 return normalized < 0 ? normalized + count : normalized;
107QVariantList rowIds(
const QVariantList& rows,
108 const QString& idKey = QStringLiteral(
"id")) {
110 out.reserve(rows.size());
111 for (
const auto& rowValue : rows) {
112 const QString
id = rowValue.toMap().value(idKey).toString();
120bool rowHasId(
const QVariantList& rows,
const QString&
id,
121 const QString& idKey = QStringLiteral(
"id")) {
122 return indexOfId(rows,
id.trimmed(), idKey) >= 0;
125QVariantList pruneAndAppendMissingIds(
const QVariantList& preferredIds,
126 const QVariantList& availableIds) {
128 for (
const auto&
id : preferredIds) {
129 if (availableIds.contains(
id) && !out.contains(
id)) {
133 for (
const auto&
id : availableIds) {
134 if (!out.contains(
id)) {
141QVariantList orderRowsByIds(
const QVariantList& rows,
142 const QVariantList& orderIds,
143 const QString& idKey = QStringLiteral(
"id")) {
145 out.reserve(rows.size());
146 for (
const auto& orderId : orderIds) {
147 for (
const auto& rowValue : rows) {
148 if (rowValue.toMap().value(idKey).toString() == orderId.toString() &&
149 !out.contains(rowValue)) {
150 out.push_back(rowValue);
155 for (
const auto& rowValue : rows) {
156 if (!out.contains(rowValue)) {
157 out.push_back(rowValue);
163QVariantList orderWithInsertedId(
const QVariantList& currentOrder,
164 const QVariantList& availableIds,
165 const QString& insertedId,
166 int insertAfterIndex) {
167 QVariantList out = pruneAndAppendMissingIds(currentOrder, availableIds);
168 if (!insertedId.isEmpty() && !out.contains(insertedId)) {
169 const int insertIndex =
170 std::clamp(insertAfterIndex + 1, 0,
static_cast<int>(out.size()));
171 out.insert(insertIndex, insertedId);
176QVariantMap orderedSelectionState(
const QVariantList& rows,
177 const QVariantList& preferredOrder,
178 int currentIndex,
const QString& selectedId,
179 const QString& idKey = QStringLiteral(
"id")) {
180 const QVariantList orderIds =
181 pruneAndAppendMissingIds(preferredOrder, rowIds(rows, idKey));
182 const QVariantList orderedRows = orderRowsByIds(rows, orderIds, idKey);
183 if (orderedRows.isEmpty()) {
184 return {{QStringLiteral(
"rows"), orderedRows},
185 {QStringLiteral(
"orderIds"), orderIds},
186 {QStringLiteral(
"index"), -1},
187 {QStringLiteral(
"id"), QString()}};
189 const int selectedIndex = indexOfId(orderedRows, selectedId, idKey);
190 const int lastIndex =
static_cast<int>(orderedRows.size()) - 1;
191 const int resolvedIndex = selectedIndex >= 0
193 : std::clamp(currentIndex, 0, lastIndex);
194 const QString resolvedId = selectedIndex >= 0
196 : rowIdAt(orderedRows, resolvedIndex, idKey);
197 return {{QStringLiteral(
"rows"), orderedRows},
198 {QStringLiteral(
"orderIds"), orderIds},
199 {QStringLiteral(
"index"), resolvedIndex},
200 {QStringLiteral(
"id"), resolvedId}};
204navigateSelectionState(
const QVariantList& rows,
int currentIndex,
205 const QString& selectedId,
int delta,
206 int defaultIndex = 0,
207 const QString& idKey = QStringLiteral(
"id")) {
208 const int selectedIndex = indexOfId(rows, selectedId, idKey);
209 const int baseIndex = selectedIndex >= 0 ? selectedIndex : defaultIndex;
210 const int nextIndex = wrappedIndex(baseIndex + delta, rows.size());
211 return {{QStringLiteral(
"index"), nextIndex},
212 {QStringLiteral(
"id"), rowIdAt(rows, nextIndex, idKey)}};
216deleteReselectionState(
const QVariantList& rows,
217 const QVariantList& preferredOrder,
int currentIndex,
218 const QString& removedId,
219 const QString& idKey = QStringLiteral(
"id")) {
220 QVariantList remainingRows;
221 for (
const auto& rowValue : rows) {
222 if (rowValue.toMap().value(idKey).toString() != removedId) {
223 remainingRows.push_back(rowValue);
226 QVariantList remainingOrder;
227 for (
const auto&
id : preferredOrder) {
228 if (
id.toString() != removedId) {
229 remainingOrder.push_back(
id);
232 return orderedSelectionState(remainingRows, remainingOrder, currentIndex, {},
236QString rememberedOrFirstRowId(
const QVariantList& rows,
237 const QVariantMap& rememberedIds,
238 const QString& ownerId,
239 const QString& idKey = QStringLiteral(
"id")) {
240 const QString remembered =
241 rememberedIds.value(ownerId.trimmed()).toString().trimmed();
242 if (rowHasId(rows, remembered, idKey)) {
245 return rows.isEmpty() ? QString() : rowIdAt(rows, 0, idKey);
248bool containsTrimmed(
const QVariantList& values,
const QString&
id) {
249 const QString nextId =
id.trimmed();
250 if (nextId.isEmpty()) {
253 for (
const auto& value : values) {
261QVariantList addUniqueTrimmed(
const QVariantList& values,
262 const QString& value) {
263 const QString next = value.trimmed();
264 QVariantList out = normalizeStringValues(values);
265 if (next.isEmpty() || containsTrimmed(out, next)) {
272QVariantList removeTrimmed(
const QVariantList& values,
const QString& value) {
273 const QString target = value.trimmed();
274 if (target.isEmpty()) {
278 out.reserve(values.size());
279 for (
const auto& entry : values) {
280 const QString current =
stringValue(entry).trimmed();
281 if (current != target) {
282 out.push_back(current);
288bool setSelectedId(QVariantList& selectedIds,
const QString&
id,
290 const QString normalizedId =
id.trimmed();
291 if (normalizedId.isEmpty()) {
294 const QVariantList next = selected
295 ? addUniqueTrimmed(selectedIds, normalizedId)
296 : removeTrimmed(selectedIds, normalizedId);
297 if (next == selectedIds) {
304QVariantMap emptyTransactionFormState() {
306 tx.insert(QStringLiteral(
"id"), QString());
307 tx.insert(QStringLiteral(
"name"), QString());
308 tx.insert(QStringLiteral(
"bookingDate"), QString());
309 tx.insert(QStringLiteral(
"valuta"), QString());
310 tx.insert(QStringLiteral(
"amount"), 0.0);
311 tx.insert(QStringLiteral(
"description"), QString());
312 tx.insert(QStringLiteral(
"status"), 0);
313 tx.insert(QStringLiteral(
"actorId"), QString());
314 tx.insert(QStringLiteral(
"propertyIds"), QVariantList());
315 tx.insert(QStringLiteral(
"allocatable"),
false);
316 tx.insert(QStringLiteral(
"contractId"), QString());
317 tx.insert(QStringLiteral(
"statementId"), QString());
321QVariantMap normalizeTransactionFormState(
const QVariantMap& tx) {
322 QVariantMap out = emptyTransactionFormState();
323 for (
auto it = tx.constBegin(); it != tx.constEnd(); ++it) {
324 out.insert(it.key(), it.value());
326 out.insert(QStringLiteral(
"status"),
327 out.value(QStringLiteral(
"status")).toInt());
329 QStringLiteral(
"propertyIds"),
330 normalizeStringValues(out.value(QStringLiteral(
"propertyIds")).toList()));
331 out.insert(QStringLiteral(
"actorId"),
332 out.value(QStringLiteral(
"actorId")).toString());
333 out.insert(QStringLiteral(
"contractId"),
334 out.value(QStringLiteral(
"contractId")).toString());
335 out.insert(QStringLiteral(
"name"),
336 out.value(QStringLiteral(
"name")).toString());
337 out.insert(QStringLiteral(
"bookingDate"),
338 out.value(QStringLiteral(
"bookingDate")).toString());
339 out.insert(QStringLiteral(
"valuta"),
340 out.value(QStringLiteral(
"valuta")).toString());
341 out.insert(QStringLiteral(
"description"),
342 out.value(QStringLiteral(
"description")).toString());
343 out.insert(QStringLiteral(
"statementId"),
344 out.value(QStringLiteral(
"statementId")).toString());
345 out.insert(QStringLiteral(
"allocatable"),
346 out.value(QStringLiteral(
"allocatable")).toBool());
350QVariantList normalizeTransactionFormStates(
const QVariantList& values) {
352 out.reserve(values.size());
353 for (
const auto& value : values) {
354 out.push_back(normalizeTransactionFormState(value.toMap()));
359bool transactionFormStateHasContent(
const QVariantMap& tx) {
360 const QVariantMap normalized = normalizeTransactionFormState(tx);
361 const QVariant amount = normalized.value(QStringLiteral(
"amount"));
362 const bool hasAmount = !amount.toString().trimmed().isEmpty();
363 return !normalized.value(QStringLiteral(
"name")).toString().isEmpty() ||
364 !normalized.value(QStringLiteral(
"bookingDate"))
367 !normalized.value(QStringLiteral(
"valuta")).toString().isEmpty() ||
369 !normalized.value(QStringLiteral(
"actorId")).toString().isEmpty() ||
370 !normalized.value(QStringLiteral(
"contractId")).toString().isEmpty() ||
371 !normalized.value(QStringLiteral(
"propertyIds")).toList().isEmpty() ||
372 normalized.value(QStringLiteral(
"allocatable")).toBool() ||
373 normalized.value(QStringLiteral(
"status")).toInt() != 0;
376QVariantMap createFormStateList(
const QVariantList& states,
int currentIndex,
377 const QVariantMap& emptyState) {
378 QVariantList normalizedStates = normalizeTransactionFormStates(states);
379 if (normalizedStates.isEmpty()) {
380 normalizedStates.push_back(normalizeTransactionFormState(emptyState));
382 const int lastIndex =
static_cast<int>(normalizedStates.size()) - 1;
383 const int index = std::clamp(currentIndex, 0, lastIndex);
384 return {{QStringLiteral(
"states"), normalizedStates},
385 {QStringLiteral(
"index"), index}};
388QVariantMap insertFormStateAfterCurrent(
const QVariantList& states,
390 const QVariantMap& emptyState) {
391 const QVariantMap base =
392 createFormStateList(states, currentIndex, emptyState);
393 QVariantList normalizedStates = base.value(QStringLiteral(
"states")).toList();
394 const int index = base.value(QStringLiteral(
"index")).toInt();
395 const int insertIndex =
396 std::clamp(index + 1, 0,
static_cast<int>(normalizedStates.size()));
397 normalizedStates.insert(insertIndex,
398 normalizeTransactionFormState(emptyState));
399 return {{QStringLiteral(
"states"), normalizedStates},
400 {QStringLiteral(
"index"), insertIndex}};
403QVariantMap removeFormStateAt(
const QVariantList& states,
int currentIndex,
404 const QVariantMap& emptyState) {
405 const QVariantMap base =
406 createFormStateList(states, currentIndex, emptyState);
407 QVariantList normalizedStates = base.value(QStringLiteral(
"states")).toList();
408 int index = base.value(QStringLiteral(
"index")).toInt();
409 if (index < 0 || index >= normalizedStates.size()) {
412 normalizedStates.removeAt(index);
413 if (normalizedStates.isEmpty()) {
414 normalizedStates.push_back(normalizeTransactionFormState(emptyState));
417 index = std::clamp(index, 0,
static_cast<int>(normalizedStates.size()) - 1);
419 return {{QStringLiteral(
"states"), normalizedStates},
420 {QStringLiteral(
"index"), index}};
423QVariantMap currentFormState(
const QVariantList& states,
int currentIndex,
424 const QVariantMap& emptyState) {
425 const QVariantMap base =
426 createFormStateList(states, currentIndex, emptyState);
427 const QVariantList normalizedStates =
428 base.value(QStringLiteral(
"states")).toList();
429 const int index = base.value(QStringLiteral(
"index")).toInt();
430 return {{QStringLiteral(
"states"), normalizedStates},
431 {QStringLiteral(
"index"), index},
432 {QStringLiteral(
"state"),
433 normalizeTransactionFormState(normalizedStates.at(index).toMap())}};
436QVariantMap setCurrentRawFormState(
const QVariantList& states,
int currentIndex,
437 const QVariantMap& formState,
438 const QVariantMap& emptyState) {
439 QVariantMap state = currentFormState(states, currentIndex, emptyState);
440 QVariantList nextStates = state.value(QStringLiteral(
"states")).toList();
441 const int index = state.value(QStringLiteral(
"index")).toInt();
442 if (index >= 0 && index < nextStates.size()) {
443 nextStates[index] = formState;
445 state.insert(QStringLiteral(
"states"), nextStates);
446 state.insert(QStringLiteral(
"state"), formState);
450bool bookingEditStateChanged(
const QString& savedStatementName,
451 const QVariantMap& savedTransactionState,
452 const QString& currentStatementName,
453 const QVariantMap& transaction) {
454 return savedStatementName != currentStatementName ||
455 savedTransactionState != normalizeTransactionFormState(transaction);
464 : QObject(parent), store_(store), commands_(commands),
465 selection_(selection), selectors_(selectors) {
466 createTransactionStates_ = QVariantList{emptyTransaction()};
467 currentTransactionFormState_ = emptyTransaction();
481 return isCreateMode() ? createStatementName_ : editStatementName_;
486 if (createStatementName_ == value) {
489 createStatementName_ = value;
491 if (editStatementName_ == value) {
494 editStatementName_ = value;
499void BookingViewModel::bindSignals() {
500 if (!store_ || !selection_) {
516 editTransactionOrderIds_.clear();
517 editTransactionIndex_ = 0;
521 QObject::connect(selection_,
527 rememberSelectedTransaction();
534 return selectors_ ? selectors_->statementRowsWithTransactions()
539 return selection_ ? selection_->selectedStatementId() : QString();
543 return selection_ ? selection_->selectedTransactionId() : QString();
551BookingViewModel::statementTransactionRows(
const QString& statementId)
const {
560 selection_->selectTransaction(statementId, QString());
564 const QString& transactionId) {
568 selection_->selectTransaction(statementId, transactionId);
573 return createTransactionStates_.isEmpty()
574 ? tr(
"No transactions")
575 : tr(
"Transaction %1 / %2")
576 .arg(createTransactionIndex_ + 1)
577 .arg(createTransactionStates_.size());
580 const QVariantMap state = editTransactionState();
581 const QVariantList rows = state.value(QStringLiteral(
"rows")).toList();
582 if (rows.isEmpty()) {
583 return tr(
"No transactions");
585 const int index = state.value(QStringLiteral(
"index")).toInt();
586 return tr(
"Transaction %1 / %2").arg(index + 1).arg(rows.size());
590 return isCreateMode() ? createTransactionStates_.size() > 1
591 : editTransactionState()
592 .value(QStringLiteral(
"rows"))
603 return createTransactionStates_.size() > 1;
606 !editTransactionData_.value(QStringLiteral(
"id")).toString().isEmpty();
609QVariantMap BookingViewModel::emptyTransaction()
const {
610 return emptyTransactionFormState();
614BookingViewModel::normalizeTransaction(
const QVariantMap& tx)
const {
615 return normalizeTransactionFormState(tx);
618QVariantMap BookingViewModel::editTransactionState()
const {
620 return {{QStringLiteral(
"rows"), QVariantList()},
621 {QStringLiteral(
"orderIds"), QVariantList()},
622 {QStringLiteral(
"index"), -1},
623 {QStringLiteral(
"id"), QString()}};
626 const QVariantList preferred = editTransactionOrderIds_.isEmpty()
628 : editTransactionOrderIds_;
629 return orderedSelectionState(rows, preferred, editTransactionIndex_,
633QVariantMap BookingViewModel::transactionById(
const QString& txId)
const {
634 if (!selectors_ || txId.isEmpty()) {
635 return emptyTransaction();
637 QVariantMap transaction = selectors_->transactionRowById(txId);
638 if (transaction.isEmpty()) {
639 return emptyTransaction();
644 return normalizeTransaction(transaction);
648BookingViewModel::normalizedTransactionState(
const QVariantMap& data)
const {
649 return normalizeTransactionFormState(data);
652QVariantMap BookingViewModel::transactionData()
const {
653 return isCreateMode() ? currentTransactionFormState_ : editTransactionData_;
656void BookingViewModel::setTransactionData(
const QVariantMap& value) {
658 const QVariantMap formState = value.isEmpty() ? emptyTransaction() : value;
659 const QVariantMap state = setCurrentRawFormState(
660 createTransactionStates_, createTransactionIndex_, formState,
662 createTransactionStates_ = state.value(QStringLiteral(
"states")).toList();
663 createTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
664 syncCurrentCreateTransaction();
666 if (editTransactionData_ == value) {
669 editTransactionData_ = value.isEmpty() ? emptyTransaction() : value;
674QVariant BookingViewModel::transactionField(
const QString& key)
const {
675 return transactionData().value(key);
678void BookingViewModel::setTransactionField(
const QString& key,
679 const QVariant& value) {
680 QVariantMap next = transactionData();
681 next.insert(key, value);
682 setTransactionData(next);
685void BookingViewModel::applyTransactionFormChange(
const QVariantMap& changes) {
689 setTransactionData(selectors_->transactionFormWithCatalogSelection(
690 transactionData(), changes));
694 return transactionField(QStringLiteral(
"name")).toString();
698 setTransactionField(QStringLiteral(
"name"), value);
702 return transactionField(QStringLiteral(
"bookingDate")).toString();
706 setTransactionField(QStringLiteral(
"bookingDate"), value);
710 return transactionField(QStringLiteral(
"valuta")).toString();
714 setTransactionField(QStringLiteral(
"valuta"), value);
718 const QVariant amount = transactionField(QStringLiteral(
"amount"));
719 return amount.isValid() ? amount.toString() : QString();
723 setTransactionField(QStringLiteral(
"amount"), value);
727 return transactionField(QStringLiteral(
"allocatable")).toBool();
731 setTransactionField(QStringLiteral(
"allocatable"), value);
734void BookingViewModel::captureEditState() {
735 savedEditStatementName_ = editStatementName_;
736 savedEditTransactionState_ = normalizedTransactionState(editTransactionData_);
739void BookingViewModel::syncCurrentCreateTransaction() {
741 createTransactionStates_ = QVariantList{emptyTransaction()};
742 createTransactionIndex_ = 0;
743 currentTransactionFormState_ = emptyTransaction();
746 const QVariantMap state = currentFormState(
747 createTransactionStates_, createTransactionIndex_, emptyTransaction());
748 createTransactionStates_ = state.value(QStringLiteral(
"states")).toList();
749 createTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
750 currentTransactionFormState_ = state.value(QStringLiteral(
"state")).toMap();
754 createStatementName_.clear();
756 const QVariantMap state =
757 createFormStateList(QVariantList(), 0, emptyTransaction());
758 createTransactionStates_ = state.value(QStringLiteral(
"states")).toList();
759 createTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
760 currentTransactionFormState_ = state.value(QStringLiteral(
"state")).toMap();
762 createTransactionStates_ = QVariantList{emptyTransaction()};
763 createTransactionIndex_ = 0;
764 currentTransactionFormState_ = emptyTransaction();
770 if (!commands_ || !selection_) {
774 const QVariantMap state = insertFormStateAfterCurrent(
775 createTransactionStates_, createTransactionIndex_, emptyTransaction());
776 createTransactionStates_ = state.value(QStringLiteral(
"states")).toList();
777 createTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
778 syncCurrentCreateTransaction();
786 const QVariantMap state = editTransactionState();
787 editTransactionOrderIds_ = state.value(QStringLiteral(
"orderIds")).toList();
788 const QVariantList rows = state.value(QStringLiteral(
"rows")).toList();
789 int insertAfterIndex =
790 state.value(QStringLiteral(
"index"), editTransactionIndex_).toInt();
791 if (insertAfterIndex < 0 || insertAfterIndex >= rows.size()) {
792 insertAfterIndex = rows.size() - 1;
794 const QVariantMap current = normalizeTransaction(editTransactionData_);
795 const QString newId = commands_->insertTransactionAfter(
796 current.value(QStringLiteral(
"id")).toString(), QString(),
797 current.value(QStringLiteral(
"bookingDate")).toString(),
798 current.value(QStringLiteral(
"valuta")).toString(), 0.0,
800 if (newId.isEmpty()) {
803 const QVariantList updatedRows =
805 const QVariantList updatedIds = rowIds(updatedRows);
806 editTransactionOrderIds_ = orderWithInsertedId(
807 editTransactionOrderIds_, updatedIds, newId, insertAfterIndex);
813 if (!commands_ || !selection_) {
817 const QVariantMap state = removeFormStateAt(
818 createTransactionStates_, createTransactionIndex_, emptyTransaction());
819 createTransactionStates_ = state.value(QStringLiteral(
"states")).toList();
820 createTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
821 syncCurrentCreateTransaction();
825 const QString deletedId =
826 editTransactionData_.value(QStringLiteral(
"id")).toString();
830 const QVariantMap selectionState = editTransactionState();
831 editTransactionOrderIds_ =
832 selectionState.value(QStringLiteral(
"orderIds")).toList();
833 commands_->deleteTransaction(deletedId);
834 const QVariantMap reselectionState = deleteReselectionState(
836 editTransactionIndex_, deletedId, QStringLiteral(
"id"));
837 editTransactionOrderIds_ =
838 reselectionState.value(QStringLiteral(
"orderIds")).toList();
839 editTransactionIndex_ =
840 reselectionState.value(QStringLiteral(
"index")).toInt();
841 const QString txId = reselectionState.value(QStringLiteral(
"id")).toString();
843 rememberSelectedTransaction();
844 editTransactionData_ = transactionById(txId);
848void BookingViewModel::rememberSelectedTransaction() {
858BookingViewModel::transactionIdForStatement(
const QString& statementId)
const {
860 ? rememberedOrFirstRowId(statementTransactionRows(statementId),
861 lastTransactionIdByStatementId_,
862 statementId, QStringLiteral(
"id"))
866bool BookingViewModel::ensureSelectedTransactionForStatement() {
872 if (rowHasId(statementTransactionRows(statementId), currentTxId,
873 QStringLiteral(
"id"))) {
876 const QString nextTxId = transactionIdForStatement(statementId);
877 if (currentTxId == nextTxId) {
880 selection_->selectTransaction(statementId, nextTxId);
884void BookingViewModel::syncEditState() {
888 if (!ensureSelectedTransactionForStatement()) {
892 .value(QStringLiteral(
"name"))
894 const QVariantMap state = editTransactionState();
895 const QVariantList rows = state.value(QStringLiteral(
"rows")).toList();
896 editTransactionOrderIds_ = state.value(QStringLiteral(
"orderIds")).toList();
897 if (rows.isEmpty()) {
898 editTransactionIndex_ = -1;
899 editTransactionData_ = emptyTransaction();
903 editTransactionIndex_ = state.value(QStringLiteral(
"index")).toInt();
904 const QString txId = state.value(QStringLiteral(
"id")).toString();
905 editTransactionData_ = transactionById(txId);
908 rememberSelectedTransaction();
918 if (rows.isEmpty()) {
922 const int currentIndex = indexOfId(rows, currentId);
923 if (currentIndex == 0) {
924 rememberSelectedTransaction();
925 selection_->selectTransaction(QString(), QString());
926 editTransactionOrderIds_.clear();
927 editTransactionIndex_ = -1;
931 const int nextIndex = currentIndex < 0 ? rows.size() - 1 : currentIndex - 1;
932 const QString nextId =
933 rows.value(nextIndex).toMap().value(QStringLiteral(
"id")).toString();
934 rememberSelectedTransaction();
935 selection_->selectTransaction(nextId, transactionIdForStatement(nextId));
936 editTransactionOrderIds_.clear();
937 editTransactionIndex_ = 0;
946 if (rows.isEmpty()) {
950 const int currentIndex = indexOfId(rows, currentId);
951 if (currentIndex == rows.size() - 1) {
952 rememberSelectedTransaction();
953 selection_->selectTransaction(QString(), QString());
954 editTransactionOrderIds_.clear();
955 editTransactionIndex_ = -1;
959 const int nextIndex = currentIndex < 0 ? 0 : currentIndex + 1;
960 const QString nextId =
961 rows.value(nextIndex).toMap().value(QStringLiteral(
"id")).toString();
962 rememberSelectedTransaction();
963 selection_->selectTransaction(nextId, transactionIdForStatement(nextId));
964 editTransactionOrderIds_.clear();
965 editTransactionIndex_ = 0;
974 if (createTransactionStates_.isEmpty()) {
977 createTransactionIndex_ = wrappedIndex(createTransactionIndex_ - 1,
978 createTransactionStates_.size());
979 syncCurrentCreateTransaction();
983 const QVariantMap state = editTransactionState();
984 const QVariantList rows = state.value(QStringLiteral(
"rows")).toList();
985 editTransactionOrderIds_ = state.value(QStringLiteral(
"orderIds")).toList();
986 if (rows.isEmpty()) {
989 const QVariantMap next =
990 navigateSelectionState(rows, state.value(QStringLiteral(
"index")).toInt(),
991 state.value(QStringLiteral(
"id")).toString(), -1,
992 0, QStringLiteral(
"id"));
993 editTransactionIndex_ = next.value(QStringLiteral(
"index")).toInt();
994 const QString txId = next.value(QStringLiteral(
"id")).toString();
995 editTransactionData_ = transactionById(txId);
997 rememberSelectedTransaction();
1006 if (createTransactionStates_.isEmpty()) {
1009 createTransactionIndex_ = wrappedIndex(createTransactionIndex_ + 1,
1010 createTransactionStates_.size());
1011 syncCurrentCreateTransaction();
1015 const QVariantMap state = editTransactionState();
1016 const QVariantList rows = state.value(QStringLiteral(
"rows")).toList();
1017 editTransactionOrderIds_ = state.value(QStringLiteral(
"orderIds")).toList();
1018 if (rows.isEmpty()) {
1021 const QVariantMap next = navigateSelectionState(
1022 rows, state.value(QStringLiteral(
"index")).toInt(),
1023 state.value(QStringLiteral(
"id")).toString(), 1, 0, QStringLiteral(
"id"));
1024 editTransactionIndex_ = next.value(QStringLiteral(
"index")).toInt();
1025 const QString txId = next.value(QStringLiteral(
"id")).toString();
1026 editTransactionData_ = transactionById(txId);
1028 rememberSelectedTransaction();
1033 return !createStatementName_.trimmed().isEmpty();
1040 return bookingEditStateChanged(savedEditStatementName_,
1041 savedEditTransactionState_, editStatementName_,
1042 editTransactionData_);
1046 if (!commands_ || !selectors_ || !selection_ || !
canCreate()) {
1050 "BookingViewModel::submit",
"Booking create submitted",
1052 {
"transactionCount", std::to_string(createTransactionStates_.size())}});
1053 QVariantList transactionStates;
1054 for (
const QVariant& stateValue : createTransactionStates_) {
1055 const QVariantMap rawTx = stateValue.toMap();
1056 const QVariantMap tx = normalizeTransaction(rawTx);
1057 if (!transactionFormStateHasContent(tx)) {
1060 transactionStates.push_back(rawTx);
1062 const QString statementId = commands_->addStatementWithTransactions(
1063 createStatementName_, transactionStates);
1064 if (statementId.isEmpty()) {
1068 selection_->selectTransaction(statementId,
1069 transactionIdForStatement(statementId));
1074 if (
isCreateMode() || !commands_ || !selectors_ || !selection_ ||
1079 "BookingViewModel::updateCurrent",
"Booking update submitted",
1083 const bool statementChanged = savedEditStatementName_ != editStatementName_;
1085 const QVariantMap transactionData = editTransactionData_;
1086 const QString txId = transactionData.value(QStringLiteral(
"id")).toString();
1087 const bool transactionChanged =
1088 savedEditTransactionState_ != normalizedTransactionState(transactionData);
1089 const QVariantMap normalizedTx = normalizeTransaction(transactionData);
1090 if (transactionChanged && !txId.isEmpty()) {
1091 commands_->updateTransaction(
1092 txId, transactionData.value(QStringLiteral(
"name")).toString(),
1093 transactionData.value(QStringLiteral(
"bookingDate")).toString(),
1094 transactionData.value(QStringLiteral(
"valuta")).toString(),
1095 transactionData.value(QStringLiteral(
"amount")), statementId,
1100 transactionData.value(QStringLiteral(
"allocatable")).toBool(),
1105 if (statementChanged) {
1116 "BookingViewModel::deleteCurrentStatement",
"Statement delete submitted",
1119 commands_->deleteStatement(removedId);
1120 const QString nextId = deleteNextSelectionId(
statementRows(), removedId, 0,
1121 QStringLiteral(
"id"));
1122 selection_->selectTransaction(
1123 nextId, nextId.isEmpty() ? QString() : transactionIdForStatement(nextId));
1131 const QVariantMap tx = normalizeTransaction(transactionData());
1136 return index >= 0 ? index : 0;
1141 const QVariantMap option = options.value(index).toMap();
1148 return selectors_ ? selectors_->propertyRows() : QVariantList();
1152 return normalizeTransaction(transactionData())
1166 return selectors_ ? selectors_->actorDropdownRows() : QVariantList();
1170 return selectors_ ? selectors_->contractDropdownRows() : QVariantList();
1173int BookingViewModel::selectedIndexFor(
const QVariantList& rows,
1174 const QString&
id)
const {
1178 const int index = indexOfId(rows,
id);
1179 return index >= 0 ? index : 0;
1183 return selectedIndexFor(
1189 return selectedIndexFor(
1199 QVariantList propertyIds =
1200 normalizeTransaction(transactionData())
1203 if (!setSelectedId(propertyIds, propertyId, selected)) {
1206 applyTransactionFormChange(
Declares the QML API for the Booking view.
Declarations for the UI payload mapper helpers.
Provides translated C++ UI strings for shell, workflow, adapter and view model code.
Declarations for the UI Trace component.
Declares the workspace selection state synchronized with the store.
Declares read-only workspace selectors for QML payloads.
Declares the UI workspace snapshot store.
Q_INVOKABLE void setPropertySelected(const QString &propertyId, bool selected)
Updates whether a property is selected for the current transaction.
QString selectedStatementId
QVariantList selectedPropertyIds
bool transactionAllocatable
void setTransactionAllocatable(bool value)
Updates the current transaction allocatable flag.
QString transactionBookingDate
QString selectedTransactionId
BookingViewModel(WorkspaceStore *store, WorkspaceCommands *commands, WorkspaceSelection *selection, WorkspaceSelectors *selectors, QObject *parent=nullptr)
Creates the booking view model backed by workspace roles.
QVariantList statementRows
Q_INVOKABLE QString submit()
Creates a statement and its transaction form states through workspace.
int selectedContractIndex
Q_INVOKABLE void updateCurrent()
Updates the selected statement and transaction through workspace.
QVariantList actorDisplayRows
Q_INVOKABLE void nextStatement()
Selects the next statement or the create form.
Q_INVOKABLE void addTransactionAfterCurrent()
Adds a transaction after the current transaction.
Q_INVOKABLE void previousTransaction()
Selects the previous transaction in the current statement.
QString transactionAmountText
Q_INVOKABLE void selectActorIndex(int index)
Selects an actor from the actor dropdown.
Q_INVOKABLE void deleteCurrentTransaction()
Deletes the current transaction when at least one remains.
void setTransactionName(const QString &value)
Updates the current transaction name field.
QVariantList propertyRows
QString transactionInfoText
Q_INVOKABLE void selectStatement(const QString &statementId)
Selects a statement from the sidebar.
void setTransactionStatusIndex(int index)
Updates the current transaction status from an option index.
Q_INVOKABLE void resetCreateState()
Clears the create form and creates a single empty transaction state.
QString transactionValuta
Q_INVOKABLE void selectContractIndex(int index)
Selects a contract from the contract dropdown.
void setTransactionAmountText(const QString &value)
Updates the current amount field.
Q_INVOKABLE void previousStatement()
Selects the previous statement or the create form.
void setTransactionBookingDate(const QString &value)
Updates the current transaction booking date field.
QVariantList transactionStatusOptions
bool hasMultipleTransactions
QVariantList contractDisplayRows
int transactionStatusIndex
Q_INVOKABLE void nextTransaction()
Selects the next transaction in the current statement.
Q_INVOKABLE void deleteCurrentStatement()
Deletes the selected statement through workspace.
void setStatementName(const QString &value)
Updates the statement name shown in the form.
void setTransactionValuta(const QString &value)
Updates the current transaction valuta field.
Q_INVOKABLE void selectTransaction(const QString &statementId, const QString &transactionId)
Selects a transaction from the sidebar.
bool canDeleteTransaction
Maps UI intent to core workspace writer commands.
Tracks the currently selected workspace entities.
void selectedStatementIdChanged()
Emitted when the selected statement id changed.
void selectedTransactionIdChanged()
Emitted when the selected transaction id changed.
Projects workspace store data into QML-facing read models.
QVariantList statementTransactionRows(const QString &statementId) const
Returns transactions belonging to a statement.
Owns the current UI-side workspace snapshot and revision lifecycle.
void dataRevisionChanged()
void traceViewModel(const char *origin, std::string message, core::errors::ErrorContext context={})
Emits a view model trace event.
QVariantList toVariantList(const QVariant &value)
Converts a QVariant value to a QVariantList.
QString stringValue(const QVariantMap &map, const QString &key)
Reads a string value from a map.
QStringList toQStringList(const std::vector< std::string > &values)
Converts standard strings to a Qt string list.
QVariantList options(const QString &neutral, const QString &unverified, const QString &verified, const QString &completed)
Builds transaction status options.
QString verified()
Label for verified transaction status.
QString neutral()
Label for neutral transaction status.
QString unverified()
Label for unverified transaction status.
QString completed()
Label for completed transaction status.
Declares UI-side workspace command submission.