Группировка связанных операций
Архитектура
Связь между операциями
related_operation_id— поле дочерней (зеркальной) операции, ссылающееся на ID родительской- Пример: покупка SBER (parent) → продажа RUB (child,
related_operation_id = parent.id) - У родительской операции
related_operation_id = null - ВАЖНО: операция может быть child (покупка облигации при продаже валюты), но при этом являться "основной" на странице актива
Backend (реализовано)
В OperationController::index() (cryptodiary/app/Http/Controllers/OperationController.php):
- При
showRelatedOperations=false: в основной запрос добавляетсяwhereNull('operations.related_operation_id')— скрываются дочерние операции - При
showRelatedOperations=true: фильтр поrelated_operation_idНЕ применяется — в результат попадают все операции (и parent, и child) - Дочерние операции (
relatedChildOperations) загружаются через constrained eager loading с полным набором scope-ов + фильтромdistribution_id(если showSubsales=false) - Родительская операция (
relatedParentOperation) также загружается с computed-полями — для показа контекста child-операций $eagerScopes— замыкание с общим набором scope-ов для переиспользования в обоих eager load
Frontend (реализовано)
Store (src/stores/operations.js → updateTable):
mapOperationRow(row)— вынесенная функция маппинга для переиспользования- При
showRelatedOperations=trueвыполняется группировка:allIds— Set всех ID в текущем результатеchildrenToNest— Set ID child-операций, чей parent тоже в результате (они пропускаются как отдельные строки)- Для каждой не-пропущенной операции собираются sub-rows из трёх источников: a.
resultChildren— операции из результата, ссылающиеся черезrelated_operation_idb.eagerChildren— eager-loadedrelated_child_operations, которых нет в основном результате c.parentAsContext— eager-loadedrelated_parent_operation(когда текущая строка — child, а её parent не в результате) - Если есть sub-rows → строка помечается
isParentOperation: true, sub-rows помечаютсяrelatedParentId
- При
showRelatedOperations=false— простой маппинг без группировки
Компонент (src/components/OperationsTable.vue):
<Table>получает groupable-пропсы приshowRelatedOperations=true::groupable="operationsStore.filter.showRelatedOperations"group-field="relatedParentId"— поле в sub-row, указывающее на главную строкуparent-field="isParentOperation"— булево поле, отмечающее строки-родителиparent-key-field="id"— поле в главной строке для связи с sub-rows
Таблица (src/components/ui/Table.vue):
- Встроенный механизм
groupableобеспечивает expand/collapse, indent и стилизацию parent/child
Ключевые файлы
src/stores/operations.js— store, фильтр,getOperations,updateTable(маппинг + группировка)src/components/OperationsTable.vue— UI таблицы, передача groupable-пропсовsrc/components/ui/Table.vue— универсальная таблица с groupable-механизмомcryptodiary/app/Http/Controllers/OperationController.php— бэкенд, фильтрация + eager loadingcryptodiary/app/Models/Operation.php— модель, связиrelatedChildOperations()/relatedParentOperation()