Implement the full Bill of Materials stack on top of the existing
relationships table and bom_single_level view from migration 001.
API endpoints (6 new routes under /api/items/{partNumber}/bom):
- GET /bom Single-level BOM for an item
- GET /bom/expanded Multi-level BOM via recursive CTE (depth param)
- GET /bom/where-used Reverse lookup: which parents use this item
- POST /bom Add child to BOM with quantity, ref designators
- PUT /bom/{child} Update relationship type, quantity, ref des
- DELETE /bom/{child} Remove child from BOM
Database layer (internal/db/relationships.go):
- RelationshipRepository with full CRUD operations
- Single-level BOM query joining relationships with items
- Multi-level BOM expansion via recursive CTE (max depth 20)
- Where-used reverse lookup query
- Cycle detection at insert time to prevent circular BOMs
- BOMEntry and BOMTreeEntry types for denormalized query results
Server wiring:
- Added RelationshipRepository to Server struct in handlers.go
- Registered BOM routes in routes.go under /{partNumber} subrouter
FreeCAD workbench (pkg/freecad/silo_commands.py):
- 9 new BOM methods on SiloClient (get, expanded, where-used, add,
update, delete)
- Silo_BOM command class with two-tab dialog:
- BOM tab: table of children with Add/Edit/Remove buttons
- Where Used tab: read-only table of parent assemblies
- Add sub-dialog with fields for part number, type, qty, unit, ref des
- Edit sub-dialog pre-populated with current values
- Remove with confirmation prompt
- silo-bom.svg icon matching existing toolbar style
- Command registered in InitGui.py toolbar
No new migrations required - uses existing relationships table and
bom_single_level view from 001_initial.sql.
Opening documents (especially assemblies) inside the QDialog nested
event loop can crash FreeCAD when the Assembly solver triggers during
document restore. Move FreeCAD.openDocument() to after dialog.exec_()
returns.
Use urllib.parse to check if the URL has a path component. Only append
/api when the path is empty (e.g. https://silo.kindred.internal).
URLs that already include a path (e.g. http://localhost:8080/api) are
left unchanged. Restore localhost placeholder as the default.
- Auto-append /api to base URL if not already present, so users can
enter just the hostname (e.g. https://silo.kindred.internal)
- Load system CA certificate bundle in SSL context so the bundled
Python trusts internal CAs (FreeIPA) without disabling verification
- Update settings dialog placeholder and hint text to clarify expected
URL format
Replace hardcoded FreeCAD addon path searches with __file__-relative
resolution for icons in both InitGui.py and silo_commands.py. Icons now
load correctly regardless of install location.
Add Silo_Settings command with URL and SSL verification fields. Settings
persist via FreeCAD preferences and take priority over env vars. Wire
SSL context into all SiloClient HTTP methods.