Skip to content

Commit 0ac887b

Browse files
committed
Add remote Inventory writes
1 parent 3af959d commit 0ac887b

49 files changed

Lines changed: 2284 additions & 213 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,16 @@ qt_add_executable(BrickSuite
8383
src/services/application/dto/RemoteMutationDtos.cpp
8484
src/services/application/dto/RemotePullingMutationDtos.h
8585
src/services/application/dto/RemotePullingMutationDtos.cpp
86+
src/services/application/dto/RemoteInventoryMutationDtos.h
87+
src/services/application/dto/RemoteInventoryMutationDtos.cpp
8688
src/services/application/HostPullingMutationService.h
8789
src/services/application/HostPullingMutationService.cpp
90+
src/services/application/HostInventoryMutationService.h
91+
src/services/application/HostInventoryMutationService.cpp
8892
src/services/application/RemotePullingApplicationService.h
8993
src/services/application/RemotePullingApplicationService.cpp
94+
src/services/application/RemoteInventoryMutationApplicationService.h
95+
src/services/application/RemoteInventoryMutationApplicationService.cpp
9096

9197
src/database/DatabaseManager.h
9298
src/database/DatabaseManager.cpp
@@ -105,6 +111,8 @@ qt_add_executable(BrickSuite
105111

106112
src/ui/MainWindow.h
107113
src/ui/MainWindow.cpp
114+
src/ui/inventory/RemoteInventoryMutationDialog.h
115+
src/ui/inventory/RemoteInventoryMutationDialog.cpp
108116
src/import/RebrickableReferenceImporter.cpp
109117
src/import/RebrickableReferenceImporter.h
110118
src/import/global/RebrickableImportTypes.h
@@ -251,6 +259,7 @@ qt_add_executable(BrickSuite
251259
src/ui/inventory/MyInventoryWidget.h
252260
src/ui/inventory/AddInventoryDialog.h
253261
src/ui/inventory/AddInventoryDialog.cpp
262+
src/ui/inventory/AddInventoryDialogButtonState.h
254263
src/import/RebrickableInventoryImporter.h
255264
src/import/BrickOwlInventoryImporter.cpp
256265
src/import/RebrickableInventoryImporter.cpp
@@ -517,6 +526,24 @@ if(BUILD_TESTING)
517526
target_link_libraries(InventoryRemoveHardeningTest PRIVATE Qt6::Core Qt6::Sql)
518527
add_test(NAME InventoryRemoveHardening COMMAND InventoryRemoveHardeningTest)
519528

529+
qt_add_executable(InventoryTransactionSeamsTest EXCLUDE_FROM_ALL
530+
tests/InventoryTransactionSeamsTest.cpp
531+
src/database/DatabaseManager.h src/database/DatabaseManager.cpp
532+
src/database/DatabaseSchema.h src/database/DatabaseSchema.cpp
533+
src/models/BuildAllocation.h src/models/BuildAllocation.cpp
534+
src/models/InventoryMovement.h src/models/InventoryMovement.cpp
535+
src/models/InventoryRecord.h src/models/InventoryRecord.cpp
536+
src/models/Manufacturer.h
537+
src/models/Part.h src/models/Part.cpp
538+
src/repositories/BuildAllocationRepository.h src/repositories/BuildAllocationRepository.cpp
539+
src/repositories/InventoryMovementRepository.h src/repositories/InventoryMovementRepository.cpp
540+
src/repositories/InventoryRecordRepository.h src/repositories/InventoryRecordRepository.cpp
541+
src/repositories/ManufacturerRepository.h src/repositories/ManufacturerRepository.cpp
542+
src/repositories/PartRepository.h src/repositories/PartRepository.cpp
543+
)
544+
target_link_libraries(InventoryTransactionSeamsTest PRIVATE Qt6::Core Qt6::Sql)
545+
add_test(NAME InventoryTransactionSeams COMMAND InventoryTransactionSeamsTest)
546+
520547
qt_add_executable(BrickLinkCandidateResolutionTest EXCLUDE_FROM_ALL
521548
tests/BrickLinkCandidateResolutionTest.cpp
522549
src/database/DatabaseManager.h src/database/DatabaseManager.cpp
@@ -1100,6 +1127,7 @@ if(BUILD_TESTING)
11001127
src/models/InventoryMovement.h src/models/InventoryMovement.cpp
11011128
src/models/InventorySearchCriteria.h src/models/InventorySearchResult.h
11021129
src/models/InventoryHistoryResult.h
1130+
src/models/LostInventoryItem.h
11031131
src/models/Build.h src/models/Build.cpp
11041132
src/models/BuildRequirement.h src/models/BuildRequirement.cpp
11051133
src/models/BuildAllocation.h src/models/BuildAllocation.cpp
@@ -1115,6 +1143,7 @@ if(BUILD_TESTING)
11151143
src/repositories/StorageLocationTypeRepository.h src/repositories/StorageLocationTypeRepository.cpp
11161144
src/repositories/InventoryRecordRepository.h src/repositories/InventoryRecordRepository.cpp
11171145
src/repositories/InventoryMovementRepository.h src/repositories/InventoryMovementRepository.cpp
1146+
src/repositories/LostInventoryRepository.h src/repositories/LostInventoryRepository.cpp
11181147
src/repositories/BuildRepository.h src/repositories/BuildRepository.cpp
11191148
src/repositories/BuildRequirementRepository.h src/repositories/BuildRequirementRepository.cpp
11201149
src/repositories/BuildAllocationRepository.h src/repositories/BuildAllocationRepository.cpp
@@ -1247,6 +1276,7 @@ if(BUILD_TESTING)
12471276
src/repositories/BuildRequirementRepository.h src/repositories/BuildRequirementRepository.cpp
12481277
src/repositories/InventoryRecordRepository.h src/repositories/InventoryRecordRepository.cpp
12491278
src/repositories/InventoryMovementRepository.h src/repositories/InventoryMovementRepository.cpp
1279+
src/repositories/LostInventoryRepository.h src/repositories/LostInventoryRepository.cpp
12501280
src/repositories/PartRepository.h src/repositories/PartRepository.cpp
12511281
src/repositories/ColorRepository.h src/repositories/ColorRepository.cpp
12521282
src/repositories/StorageLocationRepository.h src/repositories/StorageLocationRepository.cpp
@@ -1261,6 +1291,90 @@ if(BUILD_TESTING)
12611291
)
12621292
target_link_libraries(RemotePullingMutationTest PRIVATE Qt6::Core Qt6::Sql)
12631293
add_test(NAME RemotePullingMutation COMMAND RemotePullingMutationTest)
1294+
1295+
qt_add_executable(RemoteInventoryMutationTest EXCLUDE_FROM_ALL
1296+
tests/RemoteInventoryMutationTest.cpp
1297+
src/database/DatabaseManager.h src/database/DatabaseManager.cpp
1298+
src/database/DatabaseSchema.h src/database/DatabaseSchema.cpp
1299+
src/models/InventoryRecord.h src/models/InventoryRecord.cpp
1300+
src/models/InventoryMovement.h src/models/InventoryMovement.cpp
1301+
src/models/BuildAllocation.h src/models/BuildAllocation.cpp
1302+
src/models/Part.h src/models/Part.cpp src/models/Manufacturer.h
1303+
src/repositories/RepositoryConnection.h
1304+
src/repositories/InventoryRecordRepository.h src/repositories/InventoryRecordRepository.cpp
1305+
src/repositories/InventoryMovementRepository.h src/repositories/InventoryMovementRepository.cpp
1306+
src/repositories/BuildAllocationRepository.h src/repositories/BuildAllocationRepository.cpp
1307+
src/repositories/PartRepository.h src/repositories/PartRepository.cpp
1308+
src/repositories/ManufacturerRepository.h src/repositories/ManufacturerRepository.cpp
1309+
src/services/application/HostInventoryMutationService.h src/services/application/HostInventoryMutationService.cpp
1310+
src/services/application/HostWriteExecutor.h src/services/application/HostWriteExecutor.cpp
1311+
src/services/application/HostMutationPublicationService.h src/services/application/HostMutationPublicationService.cpp
1312+
src/services/application/dto/RemoteMutationDtos.h src/services/application/dto/RemoteMutationDtos.cpp
1313+
src/services/application/dto/RemoteInventoryMutationDtos.h src/services/application/dto/RemoteInventoryMutationDtos.cpp
1314+
src/repositories/RemoteMutationReceiptRepository.h src/repositories/RemoteMutationReceiptRepository.cpp
1315+
src/network/OperationalInvalidation.h src/network/OperationalInvalidation.cpp
1316+
src/network/BrickSuiteProtocol.h src/network/BrickSuiteProtocol.cpp
1317+
src/network/BrickSuiteAuthentication.h src/network/BrickSuiteAuthentication.cpp
1318+
src/network/BrickSuiteHostIdentity.h src/network/BrickSuiteHostIdentity.cpp
1319+
src/network/BrickSuiteConnectionState.h
1320+
src/network/BrickSuiteOperationDispatcher.h src/network/BrickSuiteOperationDispatcher.cpp
1321+
src/network/BrickSuiteWebSocketServer.h src/network/BrickSuiteWebSocketServer.cpp
1322+
src/network/BrickSuiteWebSocketClient.h src/network/BrickSuiteWebSocketClient.cpp
1323+
src/services/application/HostMutationProtocolService.h src/services/application/HostMutationProtocolService.cpp
1324+
src/services/application/RemoteMutationApplicationServices.h src/services/application/RemoteMutationApplicationServices.cpp
1325+
src/services/application/RemoteInventoryMutationApplicationService.h src/services/application/RemoteInventoryMutationApplicationService.cpp
1326+
src/services/CredentialStore.h src/services/CredentialStore.cpp
1327+
)
1328+
target_compile_definitions(RemoteInventoryMutationTest PRIVATE BRICKSUITE_VERSION="${PROJECT_VERSION}" BRICKSUITE_TESTING)
1329+
target_link_libraries(RemoteInventoryMutationTest PRIVATE Qt6::Core Qt6::Sql Qt6::Network Qt6::WebSockets OpenSSL::Crypto)
1330+
if(WIN32)
1331+
target_link_libraries(RemoteInventoryMutationTest PRIVATE Advapi32)
1332+
elseif(APPLE)
1333+
target_link_libraries(RemoteInventoryMutationTest PRIVATE "-framework Security")
1334+
endif()
1335+
add_test(NAME RemoteInventoryMutation COMMAND RemoteInventoryMutationTest)
1336+
1337+
qt_add_executable(RemoteAddInventoryDialogButtonTest EXCLUDE_FROM_ALL
1338+
tests/RemoteAddInventoryDialogButtonTest.cpp
1339+
src/ui/inventory/AddInventoryDialogButtonState.h
1340+
)
1341+
target_link_libraries(RemoteAddInventoryDialogButtonTest PRIVATE
1342+
Qt6::Core Qt6::Gui Qt6::Widgets)
1343+
add_test(NAME RemoteAddInventoryDialogButton COMMAND RemoteAddInventoryDialogButtonTest)
1344+
set_tests_properties(RemoteAddInventoryDialogButton PROPERTIES
1345+
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
1346+
1347+
qt_add_executable(RemoteInventoryDialogParityTest EXCLUDE_FROM_ALL
1348+
tests/RemoteInventoryDialogParityTest.cpp
1349+
src/database/DatabaseManager.h src/database/DatabaseManager.cpp
1350+
src/database/DatabaseSchema.h src/database/DatabaseSchema.cpp
1351+
src/models/Color.h src/models/Color.cpp src/models/Manufacturer.h src/models/Part.h src/models/Part.cpp
1352+
src/repositories/ColorRepository.h src/repositories/ColorRepository.cpp
1353+
src/repositories/PartRepository.h src/repositories/PartRepository.cpp
1354+
src/repositories/ManufacturerRepository.h src/repositories/ManufacturerRepository.cpp
1355+
src/ui/helpers/ColorComboHelper.h src/ui/helpers/ColorComboHelper.cpp
1356+
src/ui/inventory/RemoteInventoryMutationDialog.h src/ui/inventory/RemoteInventoryMutationDialog.cpp
1357+
src/services/application/dto/RemoteMutationDtos.h src/services/application/dto/RemoteMutationDtos.cpp
1358+
src/services/application/dto/RemoteInventoryMutationDtos.h src/services/application/dto/RemoteInventoryMutationDtos.cpp
1359+
src/services/application/RemoteMutationApplicationServices.h src/services/application/RemoteMutationApplicationServices.cpp
1360+
src/services/application/RemoteInventoryMutationApplicationService.h src/services/application/RemoteInventoryMutationApplicationService.cpp
1361+
src/network/BrickSuiteProtocol.h src/network/BrickSuiteProtocol.cpp
1362+
src/network/BrickSuiteAuthentication.h src/network/BrickSuiteAuthentication.cpp
1363+
src/network/BrickSuiteHostIdentity.h src/network/BrickSuiteHostIdentity.cpp
1364+
src/network/BrickSuiteConnectionState.h
1365+
src/network/OperationalInvalidation.h src/network/OperationalInvalidation.cpp
1366+
src/network/BrickSuiteWebSocketClient.h src/network/BrickSuiteWebSocketClient.cpp
1367+
src/services/CredentialStore.h src/services/CredentialStore.cpp
1368+
)
1369+
target_compile_definitions(RemoteInventoryDialogParityTest PRIVATE BRICKSUITE_VERSION="${PROJECT_VERSION}")
1370+
target_link_libraries(RemoteInventoryDialogParityTest PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Sql Qt6::Network Qt6::WebSockets OpenSSL::Crypto)
1371+
if(WIN32)
1372+
target_link_libraries(RemoteInventoryDialogParityTest PRIVATE Advapi32)
1373+
elseif(APPLE)
1374+
target_link_libraries(RemoteInventoryDialogParityTest PRIVATE "-framework Security")
1375+
endif()
1376+
add_test(NAME RemoteInventoryDialogParity COMMAND RemoteInventoryDialogParityTest)
1377+
set_tests_properties(RemoteInventoryDialogParity PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
12641378
endif()
12651379

12661380
if(WIN32)

docs/BRICKSUITE_PROTOCOL_1_2.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# BrickSuite Protocol 1.2
22

3+
## Inventory mutations
4+
5+
Protocol 1.2 Hosts advertise seven independently authorized Inventory operations and matching
6+
capabilities: `inventory.add`, `inventory.edit`, `inventory.move`, `inventory.correct`,
7+
`inventory.remove`, `inventory.markLost`, and `inventory.markFound`. Protocol 1.1 sessions do not
8+
receive these operations or capabilities.
9+
10+
Every request uses the standard durable mutation envelope (`workspaceId`, `mutationId`, `expected`,
11+
and `mutation`). Part identity is a canonical Part number, Color identity is the Rebrickable Color
12+
ID, Manufacturer identity is its normalized name, and Storage identity is the Host Storage ID from
13+
remote operational reads. Client-local catalog row IDs are never authoritative.
14+
15+
Record mutations include the expected Inventory record ID, quantity, Storage ID, and `modifiedUtc`.
16+
The Host rejects stale state with `STALE_VERSION`; invalid Workspace ownership, catalog identities,
17+
quantities, enumerated values, or non-inventory-capable destinations are rejected before mutation.
18+
Results identify source, destination, and surviving Inventory records where applicable, resulting
19+
quantities and Storage, the modification token, and create/merge outcomes.
20+
21+
Domain changes, movement History, and the durable receipt commit atomically. Repeating the same
22+
mutation ID and payload returns the stored result with `replayed=true`; changing the payload produces
23+
`IDEMPOTENCY_CONFLICT`. Timeouts or disconnects have unknown outcome and must be retried with the
24+
exact same mutation ID and payload. Newly committed operations publish Workspace-scoped Inventory,
25+
Inventory History, Builds, Build Requirements, Missing Parts, and Pulling invalidation; replay and
26+
failed operations publish nothing.
27+
28+
Remote Inventory uses the same user-facing workflows as local Inventory while translating selections
29+
to portable identities before asynchronous submission. The read-only `inventory.lost.list` operation
30+
returns the Host's outstanding Lost Part/Color projection so Mark Found never requires raw identity
31+
entry on the Client.
32+
333
Protocol 1.2 extends the frozen Protocol 1.1 read and invalidation contract with the
434
domain-neutral infrastructure required for Host-authoritative mutations. M26.6A does
535
not expose any operational write operation or enable any Remote Client write control.

resources/help/inventory.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ <h2>Parts Catalog or Part Reference?</h2><p>Use <a href="parts_catalog.html"><b>
77
<h2>Actions for an existing row</h2><img src="images/inventory_actions.png" alt="Inventory Actions menu"><ul><li><b>Details</b> shows current inventory and Part information.</li><li><b>Edit</b> changes supported descriptive attributes.</li><li><b>Move</b> transfers a chosen quantity to another active Inventory-capable leaf and records both locations.</li><li><b>Correct Entry...</b> adjusts a wrong stored count. Review the intended correction; do not use Move for a count correction.</li><li><b>Remove Entry...</b> deliberately removes unallocated quantity and records the operation. Quantity committed to active Build allocations cannot be removed until those allocations are reduced or released. It is not the same as Mark Lost.</li><li><b>Mark Lost...</b> records physically missing pieces that may later be returned through Lost / Found.</li><li><b>View History</b> shows additions, corrections, removals, moves, imports, Build activity, and Lost/Found events.</li></ul><img src="images/inventory_move.png" alt="Move Inventory"><img src="images/inventory_history.png" alt="Inventory History">
88
<h2>Import and receive inventory</h2><p><b>Import CSV</b> supports previewed Rebrickable Append, Replace, Subtract, and Compare Only. Select the operation and physical attributes, review projected quantities and errors, and only then apply. Compare Only never changes BrickSuite.</p><p>Supported BrickOwl order receiving also produces a preview with identity and color status. Committing valid rows is transactional and records history; cancelling makes no changes.</p>
99
<h2>Identity and provenance</h2><p>Manufacturer records who made the physical piece and is preserved through movement and Build workflows. Rebrickable and BrickLink identifiers describe catalog identity instead; they are not Manufacturer choices.</p><div class="note">My Inventory is for loose pieces. Builds manage projects. My Collection records physical Sets, Minifigs, and MOCs.</div>
10+
<h2>Inventory on a BrickSuite Host</h2><p>When connected to a Protocol 1.2 BrickSuite Host, Add, Edit, Move, Correct, Remove, Mark Lost, and Mark Found are available only when the Host advertises the exact permission for that action. The Host remains authoritative and records the same Inventory History used by local workflows. Bulk Inventory import remains Host-only.</p><p>If Host data changed after a dialog opened, BrickSuite rejects the stale change and refreshes the authoritative Inventory rather than overwriting it. A timeout or disconnect can leave the outcome unknown after the Host may have committed the operation. Keep the dialog open and use its safe retry; BrickSuite reuses the same mutation identity so the Inventory change is not applied twice.</p><div class="note">For safety, Remote Edit, Move, Correct, and Mark Lost reject Inventory currently allocated to an active Build. Remove retains its existing allocation-aware quantity restriction.</div>
1011
<p><a href="rebrickable_import.html">Rebrickable Import</a> | <a href="lost_found.html">Lost / Found</a> | <a href="storage.html">Storage</a> | <a href="reference_data.html">Lists &amp; Reference Data</a></p></body></html>

0 commit comments

Comments
 (0)